const forge = require('node-forge'); const dgram = require('dgram'); const net = require('net'); const crypto = require('crypto'); const UDP_KEY = Buffer.from('6c1ec8e2bb9bb59ab50b0daf649b410a', 'hex'); const server = dgram.createSocket({type: 'udp4'}); server.on('error', err => { if (err && err.code === 'EADDRINUSE') { console.log('[TuyaAccessory] Discovery error: Port 6666 is in use. Will retry in 15 seconds.'); } else { console.log(`[TuyaAccessory] Discovery error:\n${err.stack}`); server.close(); } }); server.on('close', () => { console.log('[TuyaAccessory] Discovery stopped on 6666.'); }); server.on('message', (msg, info) => { translate(6666, msg, info); }); server.bind(6666, () => { console.log('[TuyaAccessory] Discovery started on 6666.'); }); const sserver = dgram.createSocket('udp4'); sserver.on('error', err => { if (err && err.code === 'EADDRINUSE') { console.log('[TuyaAccessory] Discovery error: Port 6667 is in use. Will retry in 15 seconds.'); } else { console.log(`[TuyaAccessory] Discovery error:\n${err.stack}`); sserver.close(); } }); sserver.on('close', () => { console.log('[TuyaAccessory] Discovery stopped on 6667.'); }); sserver.on('message', (msg, info) => { translate(6667, msg, info); }); sserver.bind(6667, () => { console.log('[TuyaAccessory] Discovery started on 6667.'); }); function translate(port, msg, info) { if (!(msg instanceof Buffer)) return; const len = msg.length; if (len < 16 || msg.readUInt32BE(0) !== 0x000055aa || msg.readUInt32BE(len - 4) !== 0x0000aa55 ) { console.log(`[TuyaDiscovery] Incompatible UDP from ${info.address}:${port} 0x${msg.readUInt32BE(0).toString(16).padStart(8, '0')}...0x${msg.readUInt32BE(len - 4).toString(16).padStart(8, '0')}`); return; } const size = msg.readUInt32BE(12); if (len - 8 < size) { console.log(`[TuyaDiscovery] ERROR: UDP from ${info.address}:${port} size ${len - size}`); return; } const cleanMsg = msg.slice(len - size + 4, len - 8); let decryptedMsg, result; try { const decipher = crypto.createDecipheriv('aes-128-ecb', UDP_KEY, ''); decryptedMsg = decipher.update(cleanMsg, 'utf8', 'utf8'); decryptedMsg += decipher.final('utf8'); } catch(ex) { decryptedMsg = cleanMsg.toString('utf8'); } try { result = JSON.parse(decryptedMsg); console.log(`${info.address}:${port}`, JSON.stringify(result)); } catch (ex) { console.log(`${info.address}:${port} Failed`, msg.toString('hex')); } console.log(' '); return result; }