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

[BUG] Error when executing 'tuya-lan-find' #291

Open
codydalerodgers opened this issue Jun 12, 2024 · 5 comments
Open

[BUG] Error when executing 'tuya-lan-find' #291

codydalerodgers opened this issue Jun 12, 2024 · 5 comments
Labels
bug Something isn't working

Comments

@codydalerodgers
Copy link

What is the bug?

_I am trying to follow the instructions as documented but on the step where I am supposed to run tuya-lan-find I am getting the following error:

/usr/local/lib/node_modules/homebridge-tuya/bin/cli-find.js:29
const proxy = Proxy();
^
TypeError: Proxy is not a function
at Object. (/usr/local/lib/node_modules/homebridge-tuya/bin/cli-find.js:29:15)
at Module._compile (node:internal/modules/cjs/loader:1256:14)
at Module._extensions..js (node:internal/modules/cjs/loader:1310:10)
at Module.load (node:internal/modules/cjs/loader:1119:32)
at Module.load (node:internal/modules/cjs/loader:960:12)
at Function.executeUserEntryPoint [as runMain] (node:internal/modules/run_main:81:12)
at node:internal/main/run_main_module:23:47

How can one reproduce the bug?

Following the install of homebridge-tuya-lan, try to execute 'tuya-lan-find'.

What is the expected behavior?

It should show a QR code.

What is your host/environment?

Homebridge v1.8.2

Do you have any screenshots?

image

Do you have any additional context?

N/A

@codydalerodgers codydalerodgers added the bug Something isn't working label Jun 12, 2024
@jdmu
Copy link

jdmu commented Jul 4, 2024

I am getting the same

@jdmu
Copy link

jdmu commented Jul 4, 2024

I am getting the same
I troubleshot and patched the config it can be edited in the homebridge terminal - there is still an issue with the certificates included though

#!/usr/bin/env node

const Proxy = require('http-mitm-proxy').Proxy;
const EventEmitter = require('events');
const program = require('commander');
const QRCode = require('qrcode');
const path = require('path');
const os = require('os');
const JSON5 = require('json5');
const fs = require('fs-extra');

// Disable debug messages from the proxy
try {
    require('debug').disable();
} catch(ex) {}

const ROOT = path.resolve(__dirname);

const pemFile = path.join(ROOT, 'certs', 'ca.pem');

let localIPs = [];
const ifaces = os.networkInterfaces();
Object.keys(ifaces).forEach(name => {
    ifaces[name].forEach(network => {
        if (network.family === 'IPv4' && !network.internal) localIPs.push(network.address);
    });
});

const proxy = new Proxy();
const emitter = new EventEmitter();

program
    .name('tuya-lan find')
    .option('--ip <ip>', 'IP address to listen for requests')
    .option('-p, --port <port>', 'port the proxy should listen on', 8060)
    .option('--schema', 'include schema in the output')
    .parse(process.argv);


if (program.ip) {
   if (localIPs.includes(program.ip)) localIPs = [program.ip];
   else {
       console.log(`The requested IP, ${program.ip}, is not a valid external IPv4 address. The valid options are:\n\t${localIPs.join('\n\t')}`);
       process.exit();
   }
}

if (localIPs.length > 1) {
    console.log(`You have multiple network interfaces: ${localIPs.join(', ')}\nChoose one by passing it with the --ip parameter.\n\nExample: tuya-lan-find --ip ${localIPs[0]}`);
    process.exit();
}
const localIPPorts = localIPs.map(ip => `${ip}:${program.port}`);

const escapeUnicode = str => str.replace(/[\u00A0-\uffff]/gu, c => "\\u" + ("000" + c.charCodeAt().toString(16)).slice(-4));

proxy.onError(function(ctx, err) {
    switch (err.code) {
        case 'ERR_STREAM_DESTROYED':
        case 'ECONNRESET':
            return;

        case 'ECONNREFUSED':
            console.error('Failed to intercept secure communications. This could happen due to bad CA certificate.'+err);
            return;

        case 'EACCES':
            console.error(`Permission was denied to use port ${program.port}.`);
            return;

        default:
            console.error('Error:', err.code, err);
    }
});

proxy.onRequest(function(ctx, callback) {
    if (ctx.clientToProxyRequest.method === 'GET' && ctx.clientToProxyRequest.url === '/cert' && localIPPorts.includes(ctx.clientToProxyRequest.headers.host)) {
        ctx.use(Proxy.gunzip);
        console.log('Intercepted certificate request');

        ctx.proxyToClientResponse.writeHeader(200, {
            'Accept-Ranges': 'bytes',
            'Cache-Control': 'public, max-age=0',
            'Content-Type': 'application/x-x509-ca-cert',
            'Content-Disposition': 'attachment; filename=cert.pem',
            'Content-Transfer-Encoding': 'binary',
            'Content-Length': fs.statSync(pemFile).size,
            'Connection': 'keep-alive',
        });
        //ctx.proxyToClientResponse.end(fs.readFileSync(path.join(ROOT, 'certs', 'ca.pem')));
        ctx.proxyToClientResponse.write(fs.readFileSync(pemFile));
        ctx.proxyToClientResponse.end();

        return;

    } else if (ctx.clientToProxyRequest.method === 'POST' && /tuya/.test(ctx.clientToProxyRequest.headers.host)) {
        ctx.use(Proxy.gunzip);

        ctx.onRequestData(function(ctx, chunk, callback) {
            return callback(null, chunk);
        });
        ctx.onRequestEnd(function(ctx, callback) {
            callback();
        });

        let chunks = [];
        ctx.onResponseData(function(ctx, chunk, callback) {
            chunks.push(chunk);
            return callback(null, chunk);
        });
        ctx.onResponseEnd(function(ctx, callback) {
            emitter.emit('tuya-config', Buffer.concat(chunks).toString());
            callback();
        });
    }

    return callback();
});

emitter.on('tuya-config', body => {
    if (body.indexOf('tuya.m.my.group.device.list') === -1) return;
    console.log('Intercepted config from Tuya');
    let data;
    const fail = (msg, err) => {
        console.error(msg, err);
        process.exit(1);
    };
    try {
        data = JSON.parse(body);
    } catch (ex) {
        return fail('There was a problem decoding config:', ex);
    }
    if (!Array.isArray(data.result)) return fail('Couldn\'t find a valid result-set.');

    let devices = [];
    data.result.some(data => {
        if (data && data.a === 'tuya.m.my.group.device.list') {
            devices = data.result;
            return true;
        }
        return false;
    });

    if (!Array.isArray(devices)) return fail('Couldn\'t find a good list of devices.');

    console.log(`\nFound ${devices.length} device${devices.length === 1 ? '' : 's'}:`);

    const foundDevices = devices.map(device => {
        return {
            name: device.name,
            id: device.devId,
            key: device.localKey,
            pid: device.productId
        }
    });

    if (program.schema) {
        let schemas = [];
        data.result.some(data => {
            if (data && data.a === 'tuya.m.device.ref.info.my.list') {
                schemas = data.result;
                return true;
            }
            return false;
        });

        if (Array.isArray(schemas)) {
            const defs = {};
            schemas.forEach(schema => {
                if (schema.id && schema.schemaInfo) {
                    defs[schema.id] = {};
                    if (schema.schemaInfo.schema) defs[schema.id].schema = escapeUnicode(schema.schemaInfo.schema);
                    if (schema.schemaInfo.schemaExt && schema.schemaInfo.schemaExt !== '[]') defs[schema.id].extras = escapeUnicode(schema.schemaInfo.schemaExt);
                }
            });
            foundDevices.forEach(device => {
                if (defs[device.pid]) device.def = defs[device.pid];
            });
        } else console.log('Didn\'t find schema definitions. You will need to identify the data-points manually if this is a new device.');
    }

    foundDevices.forEach(device => {
        delete device.pid;
    });

    console.log(JSON5.stringify(foundDevices, '\n', 2));

    setTimeout(() => {
        process.exit(0);
    }, 5000);
});

proxy.listen({port: program.port, sslCaDir: ROOT,  host: localIPs[0]}, err => {
    if (err) {
        console.error('Error starting proxy: ' + err);
        return setTimeout(() => {
            process.exit(0);
        }, 5000);
    }
    let {address, port} = proxy.httpServer.address();
    if (address === '::' || address === '0.0.0.0') address = localIPs[0];

    QRCode.toString(`http://${address}:${port}/cert`, {type: 'terminal'}, function(err, url) {
        console.log(url);
        console.log('\nFollow the instructions on https://github.com/AMoo-Miki/homebridge-tuya-lan/wiki/Setup-Instructions');
        console.log(`Proxy IP: ${address}`);
        console.log(`Proxy Port: ${port}\n\n`);
    })
});


@montanaishome
Copy link

same issue here

@AdmiralTriggerHappy
Copy link

I used @jdmu patch but get a bad CA cert error

@maciej-nawrocki
Copy link

Same here:
Failed to intercept secure communications. This could happen due to bad CA certificate.Error: connect ECONNREFUSED 0.0.0.0:46741 PROXY_TO_PROXY_SOCKET_ERROR

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
bug Something isn't working
Projects
None yet
Development

No branches or pull requests

5 participants