UNPKG

magic-home

Version:

An implementation of the "Magic Home" app functionality

103 lines (80 loc) 2.35 kB
const dgram = require("dgram"); const os = require("os"); const { NODE_VERSION } = require("./constants"); const DISCOVERY_PORT = 48899; const BROADCAST_ADDR = "255.255.255.255"; const IPV4_FAMILY = (NODE_VERSION.major >= 18) ? 4 : "IPv4"; class Discovery { constructor() { this._scanned = false; this._clients = []; } get clients() { return this._clients; } get scanned() { return this._scanned; } /** * Convenience method which shortens the discovery operation to a single line */ static scan(timeout) { return new Discovery().scan(timeout); } /** * Send a scan packet into the network * @param {Number} timeout number of milliseconds to wait before the clients are returned * @param {function} callback Called with (err, clients) * @returns A Promise resolving to the found clients */ scan(timeout = 500, callback = undefined) { const promise = new Promise((resolve, reject) => { const socket = dgram.createSocket("udp4"); let addresses = []; if (os.platform() == "win32") { let ifaces = os.networkInterfaces(); for(let i in ifaces) { let iface_addresses = ifaces[i].filter(iface => iface.family == IPV4_FAMILY && !iface.internal).map(iface => iface.address); // create broadcast address from interface adress iface_addresses = iface_addresses.map(addr => { let blocks = addr.split("."); blocks[3] = "255"; return blocks.join("."); }); addresses.push(...iface_addresses); } } else { addresses = [ BROADCAST_ADDR ]; } socket.on("error", err => { socket.close(); reject(err); }); socket.on("message", (msg, rinfo) => { const tmpInfos = msg.toString().split(','); if (tmpInfos.length == 3){ this._clients.push({ address: tmpInfos[0], id: tmpInfos[1], model: tmpInfos[2] }); } }); socket.on("listening", () => { socket.setBroadcast(true); addresses.forEach(addr => socket.send("HF-A11ASSISTHREAD", DISCOVERY_PORT, addr)); }); socket.bind(DISCOVERY_PORT); setTimeout(() => { socket.close(); this._scanned = true; resolve(this._clients); }, timeout); }); if (callback && typeof callback == "function") { promise.then(callback.bind(null, null), callback); } return promise; } } module.exports = Discovery;