From 639a78419eaef6f0afdc623feffad3b5fc5dddeb Mon Sep 17 00:00:00 2001 From: David Ventura Date: Thu, 25 Apr 2024 23:50:20 +0200 Subject: [PATCH] refactor to allow bundling --- Makefile | 5 +- cmd/bin.ts | 49 +++++ cmd/pair.ts | 37 ---- discovery.ts | 16 +- http_server.ts | 216 ++++++++++----------- package-lock.json | 483 +++++++++++++++++++++++++++++++++++++++++++--- package.json | 5 + pair.ts | 33 ++++ 8 files changed, 656 insertions(+), 188 deletions(-) create mode 100644 cmd/bin.ts delete mode 100644 cmd/pair.ts create mode 100644 pair.ts diff --git a/Makefile b/Makefile index b5843a0..41c07ec 100644 --- a/Makefile +++ b/Makefile @@ -10,13 +10,14 @@ venv: requirements.txt build: node_modules ./node_modules/.bin/tsc + ./node_modules/.bin/esbuild cmd/bin.ts --bundle --platform=node --format=esm --outdir=dist tar czf build.tar.gz build package.json run: node_modules - ./node_modules/.bin/ts-node --esm http_server.ts + ./node_modules/.bin/ts-node --esm cmd/bin http_server --port=1234 pair: node_modules - ./node_modules/.bin/ts-node --esm cmd/pair.ts + ./node_modules/.bin/ts-node --esm cmd/bin pair hook: bundle.js venv ./venv/bin/python3 -u loader3.py diff --git a/cmd/bin.ts b/cmd/bin.ts new file mode 100644 index 0000000..cdd28b9 --- /dev/null +++ b/cmd/bin.ts @@ -0,0 +1,49 @@ +import { hideBin } from "yargs/helpers"; +import yargs from "yargs/yargs"; + +import { serveHttp } from "../http_server.js"; +import { opt } from "../options.js"; +import { pair } from "../pair.js"; + +yargs(hideBin(process.argv)) + .command( + "http_server", + "start http server", + (yargs) => { + return yargs + .option("debug", { default: false }) + .option("ansi", { default: false }) + .option("audio", { default: false }) + .boolean(["debug", "ansi", "audio"]) + .option("discovery_ip", { default: "192.168.1.255" }) + .option("attempt_to_fix_packet_loss", { default: false }) + .option("port", { describe: "HTTP Port to listen on", default: 5000 }) + .number(["port"]) + .strict(); + }, + (argv) => { + const opts: opt = argv as opt; + serveHttp(opts, argv.port, argv.audio || false); + }, + ) + .command( + "pair", + "configure a camera", + (yargs) => { + return yargs + .option("debug", { default: false }) + .option("ansi", { default: false }) + .boolean(["debug", "ansi"]) + .option("discovery_ip", { default: "192.168.1.255" }) + .option("attempt_to_fix_packet_loss", { default: false }) + .demandOption("ssid") + .demandOption("password") + .string(["ssid", "password"]); + }, + (argv) => { + const opts: opt = argv as unknown as opt; + pair({ opts, ssid: argv.ssid, password: argv.password }); + }, + ) + .demandCommand() + .parseSync(); diff --git a/cmd/pair.ts b/cmd/pair.ts deleted file mode 100644 index f9d8b28..0000000 --- a/cmd/pair.ts +++ /dev/null @@ -1,37 +0,0 @@ -import { RemoteInfo } from "dgram"; - -import { discoverDevices } from "../discovery.js"; -import { DevSerial } from "../impl.js"; -import { Handlers, makeSession, Session, configureWifi } from "../session.js"; - -const opts = { - debug: true, - ansi: true, - discovery_ip: "192.168.1.255", - attempt_to_fix_packet_loss: false, -}; - -let sessions: Record = {}; - -let devEv = discoverDevices(opts); -if (process.env.SSID == undefined || process.env.PSK == undefined) { - throw new Error("Set `SSID` and `PSK` environment variables"); -} - -const onLogin = configureWifi(process.env.SSID, process.env.PSK); - -console.log(process.env.SSID, process.env.PSK); -devEv.on("discover", (rinfo: RemoteInfo, dev: DevSerial) => { - if (dev.devId in sessions) { - console.log(`ignoring ${dev.devId} - ${rinfo.address}`); - return; - } - console.log(`discovered ${dev.devId} - ${rinfo.address}`); - const s = makeSession(Handlers, dev, rinfo, onLogin, opts); - - s.eventEmitter.on("disconnect", () => { - console.log("deleting from sessions"); - sessions[dev.devId] = undefined; - }); - sessions[dev.devId] = s; -}); diff --git a/discovery.ts b/discovery.ts index 7beea73..e3f02ca 100644 --- a/discovery.ts +++ b/discovery.ts @@ -2,23 +2,21 @@ import EventEmitter from "node:events"; import { create_LanSearch, parse_PunchPkt } from "./impl.js"; import { createSocket, RemoteInfo } from "node:dgram"; import { Commands } from "./datatypes.js"; -import { hexdump } from "./hexdump.js"; -import { opt } from "./options.js"; -const handleIncomingPunch = (msg: Buffer, ee: EventEmitter, rinfo: RemoteInfo, options: opt) => { +const handleIncomingPunch = (msg: Buffer, ee: EventEmitter, rinfo: RemoteInfo, debug: boolean) => { const ab = new Uint8Array(msg).buffer; const dv = new DataView(ab); const cmd_id = dv.readU16(); if (cmd_id != Commands.PunchPkt) { return; } - if (options.debug) { + if (debug) { console.log("Discovery got a PunchPkt"); } ee.emit("discover", rinfo, parse_PunchPkt(dv)); }; -export const discoverDevices = (options: opt): EventEmitter => { +export const discoverDevices = (debug: boolean, discovery_ip: string): EventEmitter => { const sock = createSocket("udp4"); const SEND_PORT = 32108; const ee = new EventEmitter(); @@ -28,18 +26,18 @@ export const discoverDevices = (options: opt): EventEmitter => { sock.close(); }); - sock.on("message", (msg, rinfo) => handleIncomingPunch(msg, ee, rinfo, options)); + sock.on("message", (msg, rinfo) => handleIncomingPunch(msg, ee, rinfo, debug)); sock.on("listening", () => { sock.setBroadcast(true); - console.log("Searching for devices.."); + console.log(`Searching for devices on ${discovery_ip}`); let buf = create_LanSearch(); setInterval(() => { console.log("."); - sock.send(new Uint8Array(buf.buffer), SEND_PORT, options.discovery_ip); + sock.send(new Uint8Array(buf.buffer), SEND_PORT, discovery_ip); }, 2000); - sock.send(new Uint8Array(buf.buffer), SEND_PORT, options.discovery_ip); + sock.send(new Uint8Array(buf.buffer), SEND_PORT, discovery_ip); }); sock.bind(); diff --git a/http_server.ts b/http_server.ts index 173712a..de4ef72 100644 --- a/http_server.ts +++ b/http_server.ts @@ -2,134 +2,126 @@ import { RemoteInfo } from "dgram"; import { readFileSync } from "node:fs"; import http from "node:http"; +import { opt } from "./options.js"; import { discoverDevices } from "./discovery.js"; import { DevSerial } from "./impl.js"; import { Handlers, makeSession, Session, startVideoStream } from "./session.js"; -import { ServerResponse } from "http"; - -const opts = { - debug: false, - ansi: false, - // discovery_ip: "192.168.40.255", //, "192.168.1.255" - // discovery_ip: "192.168.40.104", - discovery_ip: "192.168.1.255", - attempt_to_fix_packet_loss: false, - //attempt_to_fix_packet_loss: true, -}; let BOUNDARY = "a very good boundary line"; -let responses: Record = {}; -let audioResponses: Record = {}; +let responses: Record = {}; +let audioResponses: Record = {}; let sessions: Record = {}; -const server = http.createServer((req, res) => { - if (req.url.startsWith("/ui/")) { - let devId = req.url.split("/")[2]; - let s = sessions[devId]; - if (s === undefined) { - res.writeHead(400); - res.end("invalid ID"); +export const serveHttp = (opts: opt, port: number, with_audio: boolean) => { + const server = http.createServer((req, res) => { + if (req.url.startsWith("/ui/")) { + let devId = req.url.split("/")[2]; + let s = sessions[devId]; + if (s === undefined) { + res.writeHead(400); + res.end("invalid ID"); + return; + } + if (!s.connected) { + res.writeHead(400); + res.end("Nothing online"); + return; + } + const ui = readFileSync("asd.html").toString(); + res.end(ui.replace(/\${id}/g, devId)); return; } - if (!s.connected) { - res.writeHead(400); - res.end("Nothing online"); - return; - } - const ui = readFileSync("asd.html").toString(); - res.end(ui.replace(/\${id}/g, devId)); - return; - } - if (req.url.startsWith("/audio/")) { - let devId = req.url.split("/")[2]; - let s = sessions[devId]; - if (s === undefined) { - res.writeHead(400); - res.end("invalid ID"); - return; - } - if (!s.connected) { - res.writeHead(400); - res.end("Nothing online"); - return; - } - res.setHeader("Content-Type", `text/event-stream`); - audioResponses[devId].push(res); - return; - } - - if (req.url.startsWith("/camera/")) { - let devId = req.url.split("/")[2]; - console.log("requested for", devId); - let s = sessions[devId]; - - if (s === undefined) { - res.writeHead(400); - res.end("invalid ID"); - return; - } - if (!s.connected) { - res.writeHead(400); - res.end("Nothing online"); + if (req.url.startsWith("/audio/")) { + let devId = req.url.split("/")[2]; + let s = sessions[devId]; + if (s === undefined) { + res.writeHead(400); + res.end("invalid ID"); + return; + } + if (!s.connected) { + res.writeHead(400); + res.end("Nothing online"); + return; + } + res.setHeader("Content-Type", `text/event-stream`); + audioResponses[devId].push(res); return; } - res.setHeader("Content-Type", `multipart/x-mixed-replace; boundary="${BOUNDARY}"`); - responses[devId].push(res); - res.on("close", () => { - responses[devId] = responses[devId].filter((r) => r !== res); - console.log("Conn closed, kicked"); - }); - } else { - res.write(``); - Object.keys(sessions).forEach((id) => - res.write(`

${id}


`), - ); - res.write(``); - res.end(); - } -}); + if (req.url.startsWith("/camera/")) { + let devId = req.url.split("/")[2]; + console.log("requested for", devId); + let s = sessions[devId]; -let devEv = discoverDevices(opts); -devEv.on("discover", (rinfo: RemoteInfo, dev: DevSerial) => { - if (dev.devId in sessions) { - console.log(`ignoring ${dev.devId} - ${rinfo.address}`); - return; - } + if (s === undefined) { + res.writeHead(400); + res.end("invalid ID"); + return; + } + if (!s.connected) { + res.writeHead(400); + res.end("Nothing online"); + return; + } - console.log(`discovered ${dev.devId} - ${rinfo.address}`); - responses[dev.devId] = []; - audioResponses[dev.devId] = []; - const s = makeSession(Handlers, dev, rinfo, startVideoStream, opts); - - const withAudio = true; - const header = Buffer.from(`--${BOUNDARY}\r\nContent-Type: image/jpeg\r\n\r\n`); - - s.eventEmitter.on("frame", () => { - const assembled = Buffer.concat(s.curImage); - - responses[dev.devId].forEach((res) => { - res.write(header); - res.write(assembled); - }); + res.setHeader("Content-Type", `multipart/x-mixed-replace; boundary="${BOUNDARY}"`); + responses[devId].push(res); + res.on("close", () => { + responses[devId] = responses[devId].filter((r) => r !== res); + console.log("Conn closed, kicked"); + }); + } else { + res.write(``); + Object.keys(sessions).forEach((id) => + res.write(`

${id}


`), + ); + res.write(``); + res.end(); + } }); - s.eventEmitter.on("disconnect", () => { - console.log("deleting from sessions"); - delete sessions[dev.devId]; - }); - if (withAudio) { - s.eventEmitter.on("audio", ({ gap, data }) => { - // ew, maybe WS? - var b64encoded = Buffer.from(data).toString("base64"); - audioResponses[dev.devId].forEach((res) => { - res.write("data: "); - res.write(b64encoded); - res.write("\n\n"); + let devEv = discoverDevices(opts.debug, opts.discovery_ip); + devEv.on("discover", (rinfo: RemoteInfo, dev: DevSerial) => { + if (dev.devId in sessions) { + console.log(`ignoring ${dev.devId} - ${rinfo.address}`); + return; + } + + console.log(`discovered ${dev.devId} - ${rinfo.address}`); + responses[dev.devId] = []; + audioResponses[dev.devId] = []; + const s = makeSession(Handlers, dev, rinfo, startVideoStream, opts); + + const header = Buffer.from(`--${BOUNDARY}\r\nContent-Type: image/jpeg\r\n\r\n`); + + s.eventEmitter.on("frame", () => { + const assembled = Buffer.concat(s.curImage); + + responses[dev.devId].forEach((res) => { + res.write(header); + res.write(assembled); }); }); - } - sessions[dev.devId] = s; -}); -server.listen(1234); + s.eventEmitter.on("disconnect", () => { + console.log("deleting from sessions"); + delete sessions[dev.devId]; + }); + if (with_audio) { + s.eventEmitter.on("audio", ({ gap, data }) => { + // ew, maybe WS? + var b64encoded = Buffer.from(data).toString("base64"); + audioResponses[dev.devId].forEach((res) => { + res.write("data: "); + res.write(b64encoded); + res.write("\n\n"); + }); + }); + } + sessions[dev.devId] = s; + }); + + console.log(`Starting HTTP server on port ${port}`); + server.listen(port); +}; diff --git a/package-lock.json b/package-lock.json index 00478f9..434668f 100644 --- a/package-lock.json +++ b/package-lock.json @@ -4,7 +4,12 @@ "requires": true, "packages": { "": { + "dependencies": { + "esbuild": "^0.20.2", + "yargs": "^17.7.2" + }, "devDependencies": { + "@types/yargs": "^17.0.32", "mocha": "^10.2.0", "ts-node": "^10.9.2", "typescript": "^5.3.3" @@ -22,6 +27,351 @@ "node": ">=12" } }, + "node_modules/@esbuild/aix-ppc64": { + "version": "0.20.2", + "resolved": "https://registry.npmjs.org/@esbuild/aix-ppc64/-/aix-ppc64-0.20.2.tgz", + "integrity": "sha512-D+EBOJHXdNZcLJRBkhENNG8Wji2kgc9AZ9KiPr1JuZjsNtyHzrsfLRrY0tk2H2aoFu6RANO1y1iPPUCDYWkb5g==", + "cpu": [ + "ppc64" + ], + "optional": true, + "os": [ + "aix" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/android-arm": { + "version": "0.20.2", + "resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.20.2.tgz", + "integrity": "sha512-t98Ra6pw2VaDhqNWO2Oph2LXbz/EJcnLmKLGBJwEwXX/JAN83Fym1rU8l0JUWK6HkIbWONCSSatf4sf2NBRx/w==", + "cpu": [ + "arm" + ], + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/android-arm64": { + "version": "0.20.2", + "resolved": "https://registry.npmjs.org/@esbuild/android-arm64/-/android-arm64-0.20.2.tgz", + "integrity": "sha512-mRzjLacRtl/tWU0SvD8lUEwb61yP9cqQo6noDZP/O8VkwafSYwZ4yWy24kan8jE/IMERpYncRt2dw438LP3Xmg==", + "cpu": [ + "arm64" + ], + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/android-x64": { + "version": "0.20.2", + "resolved": "https://registry.npmjs.org/@esbuild/android-x64/-/android-x64-0.20.2.tgz", + "integrity": "sha512-btzExgV+/lMGDDa194CcUQm53ncxzeBrWJcncOBxuC6ndBkKxnHdFJn86mCIgTELsooUmwUm9FkhSp5HYu00Rg==", + "cpu": [ + "x64" + ], + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/darwin-arm64": { + "version": "0.20.2", + "resolved": "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.20.2.tgz", + "integrity": "sha512-4J6IRT+10J3aJH3l1yzEg9y3wkTDgDk7TSDFX+wKFiWjqWp/iCfLIYzGyasx9l0SAFPT1HwSCR+0w/h1ES/MjA==", + "cpu": [ + "arm64" + ], + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/darwin-x64": { + "version": "0.20.2", + "resolved": "https://registry.npmjs.org/@esbuild/darwin-x64/-/darwin-x64-0.20.2.tgz", + "integrity": "sha512-tBcXp9KNphnNH0dfhv8KYkZhjc+H3XBkF5DKtswJblV7KlT9EI2+jeA8DgBjp908WEuYll6pF+UStUCfEpdysA==", + "cpu": [ + "x64" + ], + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/freebsd-arm64": { + "version": "0.20.2", + "resolved": "https://registry.npmjs.org/@esbuild/freebsd-arm64/-/freebsd-arm64-0.20.2.tgz", + "integrity": "sha512-d3qI41G4SuLiCGCFGUrKsSeTXyWG6yem1KcGZVS+3FYlYhtNoNgYrWcvkOoaqMhwXSMrZRl69ArHsGJ9mYdbbw==", + "cpu": [ + "arm64" + ], + "optional": true, + "os": [ + "freebsd" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/freebsd-x64": { + "version": "0.20.2", + "resolved": "https://registry.npmjs.org/@esbuild/freebsd-x64/-/freebsd-x64-0.20.2.tgz", + "integrity": "sha512-d+DipyvHRuqEeM5zDivKV1KuXn9WeRX6vqSqIDgwIfPQtwMP4jaDsQsDncjTDDsExT4lR/91OLjRo8bmC1e+Cw==", + "cpu": [ + "x64" + ], + "optional": true, + "os": [ + "freebsd" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/linux-arm": { + "version": "0.20.2", + "resolved": "https://registry.npmjs.org/@esbuild/linux-arm/-/linux-arm-0.20.2.tgz", + "integrity": "sha512-VhLPeR8HTMPccbuWWcEUD1Az68TqaTYyj6nfE4QByZIQEQVWBB8vup8PpR7y1QHL3CpcF6xd5WVBU/+SBEvGTg==", + "cpu": [ + "arm" + ], + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/linux-arm64": { + "version": "0.20.2", + "resolved": "https://registry.npmjs.org/@esbuild/linux-arm64/-/linux-arm64-0.20.2.tgz", + "integrity": "sha512-9pb6rBjGvTFNira2FLIWqDk/uaf42sSyLE8j1rnUpuzsODBq7FvpwHYZxQ/It/8b+QOS1RYfqgGFNLRI+qlq2A==", + "cpu": [ + "arm64" + ], + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/linux-ia32": { + "version": "0.20.2", + "resolved": "https://registry.npmjs.org/@esbuild/linux-ia32/-/linux-ia32-0.20.2.tgz", + "integrity": "sha512-o10utieEkNPFDZFQm9CoP7Tvb33UutoJqg3qKf1PWVeeJhJw0Q347PxMvBgVVFgouYLGIhFYG0UGdBumROyiig==", + "cpu": [ + "ia32" + ], + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/linux-loong64": { + "version": "0.20.2", + "resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.20.2.tgz", + "integrity": "sha512-PR7sp6R/UC4CFVomVINKJ80pMFlfDfMQMYynX7t1tNTeivQ6XdX5r2XovMmha/VjR1YN/HgHWsVcTRIMkymrgQ==", + "cpu": [ + "loong64" + ], + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/linux-mips64el": { + "version": "0.20.2", + "resolved": "https://registry.npmjs.org/@esbuild/linux-mips64el/-/linux-mips64el-0.20.2.tgz", + "integrity": "sha512-4BlTqeutE/KnOiTG5Y6Sb/Hw6hsBOZapOVF6njAESHInhlQAghVVZL1ZpIctBOoTFbQyGW+LsVYZ8lSSB3wkjA==", + "cpu": [ + "mips64el" + ], + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/linux-ppc64": { + "version": "0.20.2", + "resolved": "https://registry.npmjs.org/@esbuild/linux-ppc64/-/linux-ppc64-0.20.2.tgz", + "integrity": "sha512-rD3KsaDprDcfajSKdn25ooz5J5/fWBylaaXkuotBDGnMnDP1Uv5DLAN/45qfnf3JDYyJv/ytGHQaziHUdyzaAg==", + "cpu": [ + "ppc64" + ], + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/linux-riscv64": { + "version": "0.20.2", + "resolved": "https://registry.npmjs.org/@esbuild/linux-riscv64/-/linux-riscv64-0.20.2.tgz", + "integrity": "sha512-snwmBKacKmwTMmhLlz/3aH1Q9T8v45bKYGE3j26TsaOVtjIag4wLfWSiZykXzXuE1kbCE+zJRmwp+ZbIHinnVg==", + "cpu": [ + "riscv64" + ], + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/linux-s390x": { + "version": "0.20.2", + "resolved": "https://registry.npmjs.org/@esbuild/linux-s390x/-/linux-s390x-0.20.2.tgz", + "integrity": "sha512-wcWISOobRWNm3cezm5HOZcYz1sKoHLd8VL1dl309DiixxVFoFe/o8HnwuIwn6sXre88Nwj+VwZUvJf4AFxkyrQ==", + "cpu": [ + "s390x" + ], + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/linux-x64": { + "version": "0.20.2", + "resolved": "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.20.2.tgz", + "integrity": "sha512-1MdwI6OOTsfQfek8sLwgyjOXAu+wKhLEoaOLTjbijk6E2WONYpH9ZU2mNtR+lZ2B4uwr+usqGuVfFT9tMtGvGw==", + "cpu": [ + "x64" + ], + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/netbsd-x64": { + "version": "0.20.2", + "resolved": "https://registry.npmjs.org/@esbuild/netbsd-x64/-/netbsd-x64-0.20.2.tgz", + "integrity": "sha512-K8/DhBxcVQkzYc43yJXDSyjlFeHQJBiowJ0uVL6Tor3jGQfSGHNNJcWxNbOI8v5k82prYqzPuwkzHt3J1T1iZQ==", + "cpu": [ + "x64" + ], + "optional": true, + "os": [ + "netbsd" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/openbsd-x64": { + "version": "0.20.2", + "resolved": "https://registry.npmjs.org/@esbuild/openbsd-x64/-/openbsd-x64-0.20.2.tgz", + "integrity": "sha512-eMpKlV0SThJmmJgiVyN9jTPJ2VBPquf6Kt/nAoo6DgHAoN57K15ZghiHaMvqjCye/uU4X5u3YSMgVBI1h3vKrQ==", + "cpu": [ + "x64" + ], + "optional": true, + "os": [ + "openbsd" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/sunos-x64": { + "version": "0.20.2", + "resolved": "https://registry.npmjs.org/@esbuild/sunos-x64/-/sunos-x64-0.20.2.tgz", + "integrity": "sha512-2UyFtRC6cXLyejf/YEld4Hajo7UHILetzE1vsRcGL3earZEW77JxrFjH4Ez2qaTiEfMgAXxfAZCm1fvM/G/o8w==", + "cpu": [ + "x64" + ], + "optional": true, + "os": [ + "sunos" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/win32-arm64": { + "version": "0.20.2", + "resolved": "https://registry.npmjs.org/@esbuild/win32-arm64/-/win32-arm64-0.20.2.tgz", + "integrity": "sha512-GRibxoawM9ZCnDxnP3usoUDO9vUkpAxIIZ6GQI+IlVmr5kP3zUq+l17xELTHMWTWzjxa2guPNyrpq1GWmPvcGQ==", + "cpu": [ + "arm64" + ], + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/win32-ia32": { + "version": "0.20.2", + "resolved": "https://registry.npmjs.org/@esbuild/win32-ia32/-/win32-ia32-0.20.2.tgz", + "integrity": "sha512-HfLOfn9YWmkSKRQqovpnITazdtquEW8/SoHW7pWpuEeguaZI4QnCRW6b+oZTztdBnZOS2hqJ6im/D5cPzBTTlQ==", + "cpu": [ + "ia32" + ], + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/win32-x64": { + "version": "0.20.2", + "resolved": "https://registry.npmjs.org/@esbuild/win32-x64/-/win32-x64-0.20.2.tgz", + "integrity": "sha512-N49X4lJX27+l9jbLKSqZ6bKNjzQvHaT8IIFUy+YIqmXQdjYCToGWwOItDrfby14c78aDd5NHQl29xingXfCdLQ==", + "cpu": [ + "x64" + ], + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=12" + } + }, "node_modules/@jridgewell/resolve-uri": { "version": "3.1.1", "resolved": "https://registry.npmjs.org/@jridgewell/resolve-uri/-/resolve-uri-3.1.1.tgz", @@ -81,6 +431,21 @@ "undici-types": "~5.26.4" } }, + "node_modules/@types/yargs": { + "version": "17.0.32", + "resolved": "https://registry.npmjs.org/@types/yargs/-/yargs-17.0.32.tgz", + "integrity": "sha512-xQ67Yc/laOG5uMfX/093MRlGGCIBzZMarVa+gfNKJxWAIgykYpVGkBdbqEzGDDfCrVUj6Hiff4mTZ5BA6TmAog==", + "dev": true, + "dependencies": { + "@types/yargs-parser": "*" + } + }, + "node_modules/@types/yargs-parser": { + "version": "21.0.3", + "resolved": "https://registry.npmjs.org/@types/yargs-parser/-/yargs-parser-21.0.3.tgz", + "integrity": "sha512-I4q9QU9MQv4oEOz4tAHJtNz1cwuLxn2F3xcc2iV5WdqLPpUnj30aUuxt1mAxYTG+oe8CZMV/+6rU4S4gRDzqtQ==", + "dev": true + }, "node_modules/acorn": { "version": "8.11.3", "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.11.3.tgz", @@ -115,7 +480,6 @@ "version": "5.0.1", "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", - "dev": true, "engines": { "node": ">=8" } @@ -124,7 +488,6 @@ "version": "4.3.0", "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", - "dev": true, "dependencies": { "color-convert": "^2.0.1" }, @@ -270,21 +633,22 @@ } }, "node_modules/cliui": { - "version": "7.0.4", - "resolved": "https://registry.npmjs.org/cliui/-/cliui-7.0.4.tgz", - "integrity": "sha512-OcRE68cOsVMXp1Yvonl/fzkQOyjLSu/8bhPDfQt0e0/Eb283TKP20Fs2MqoPsr9SwA595rRCA+QMzYc9nBP+JQ==", - "dev": true, + "version": "8.0.1", + "resolved": "https://registry.npmjs.org/cliui/-/cliui-8.0.1.tgz", + "integrity": "sha512-BSeNnyus75C4//NQ9gQt1/csTXyo/8Sb+afLAkzAptFuMsod9HFokGNudZpi/oQV73hnVK+sR+5PVRMd+Dr7YQ==", "dependencies": { "string-width": "^4.2.0", - "strip-ansi": "^6.0.0", + "strip-ansi": "^6.0.1", "wrap-ansi": "^7.0.0" + }, + "engines": { + "node": ">=12" } }, "node_modules/color-convert": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", - "dev": true, "dependencies": { "color-name": "~1.1.4" }, @@ -295,8 +659,7 @@ "node_modules/color-name": { "version": "1.1.4", "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", - "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", - "dev": true + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==" }, "node_modules/concat-map": { "version": "0.0.1", @@ -357,14 +720,49 @@ "node_modules/emoji-regex": { "version": "8.0.0", "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", - "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==", - "dev": true + "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==" + }, + "node_modules/esbuild": { + "version": "0.20.2", + "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.20.2.tgz", + "integrity": "sha512-WdOOppmUNU+IbZ0PaDiTst80zjnrOkyJNHoKupIcVyU8Lvla3Ugx94VzkQ32Ijqd7UhHJy75gNWDMUekcrSJ6g==", + "hasInstallScript": true, + "bin": { + "esbuild": "bin/esbuild" + }, + "engines": { + "node": ">=12" + }, + "optionalDependencies": { + "@esbuild/aix-ppc64": "0.20.2", + "@esbuild/android-arm": "0.20.2", + "@esbuild/android-arm64": "0.20.2", + "@esbuild/android-x64": "0.20.2", + "@esbuild/darwin-arm64": "0.20.2", + "@esbuild/darwin-x64": "0.20.2", + "@esbuild/freebsd-arm64": "0.20.2", + "@esbuild/freebsd-x64": "0.20.2", + "@esbuild/linux-arm": "0.20.2", + "@esbuild/linux-arm64": "0.20.2", + "@esbuild/linux-ia32": "0.20.2", + "@esbuild/linux-loong64": "0.20.2", + "@esbuild/linux-mips64el": "0.20.2", + "@esbuild/linux-ppc64": "0.20.2", + "@esbuild/linux-riscv64": "0.20.2", + "@esbuild/linux-s390x": "0.20.2", + "@esbuild/linux-x64": "0.20.2", + "@esbuild/netbsd-x64": "0.20.2", + "@esbuild/openbsd-x64": "0.20.2", + "@esbuild/sunos-x64": "0.20.2", + "@esbuild/win32-arm64": "0.20.2", + "@esbuild/win32-ia32": "0.20.2", + "@esbuild/win32-x64": "0.20.2" + } }, "node_modules/escalade": { "version": "3.1.1", "resolved": "https://registry.npmjs.org/escalade/-/escalade-3.1.1.tgz", "integrity": "sha512-k0er2gUkLf8O0zKJiAhmkTnJlTvINGv7ygDNPbeIsX/TJjGJZHuh9B2UxbsaEkmlEo9MfhrSzmhIlhRlI2GXnw==", - "dev": true, "engines": { "node": ">=6" } @@ -442,7 +840,6 @@ "version": "2.0.5", "resolved": "https://registry.npmjs.org/get-caller-file/-/get-caller-file-2.0.5.tgz", "integrity": "sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg==", - "dev": true, "engines": { "node": "6.* || 8.* || >= 10.*" } @@ -560,7 +957,6 @@ "version": "3.0.0", "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz", "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==", - "dev": true, "engines": { "node": ">=8" } @@ -708,6 +1104,35 @@ "url": "https://opencollective.com/mochajs" } }, + "node_modules/mocha/node_modules/cliui": { + "version": "7.0.4", + "resolved": "https://registry.npmjs.org/cliui/-/cliui-7.0.4.tgz", + "integrity": "sha512-OcRE68cOsVMXp1Yvonl/fzkQOyjLSu/8bhPDfQt0e0/Eb283TKP20Fs2MqoPsr9SwA595rRCA+QMzYc9nBP+JQ==", + "dev": true, + "dependencies": { + "string-width": "^4.2.0", + "strip-ansi": "^6.0.0", + "wrap-ansi": "^7.0.0" + } + }, + "node_modules/mocha/node_modules/yargs": { + "version": "16.2.0", + "resolved": "https://registry.npmjs.org/yargs/-/yargs-16.2.0.tgz", + "integrity": "sha512-D1mvvtDG0L5ft/jGWkLpG1+m0eQxOfaBvTNELraWj22wSVUMWxZUvYgJYcKh6jGGIkJFhH4IZPQhR4TKpc8mBw==", + "dev": true, + "dependencies": { + "cliui": "^7.0.2", + "escalade": "^3.1.1", + "get-caller-file": "^2.0.5", + "require-directory": "^2.1.1", + "string-width": "^4.2.0", + "y18n": "^5.0.5", + "yargs-parser": "^20.2.2" + }, + "engines": { + "node": ">=10" + } + }, "node_modules/ms": { "version": "2.1.3", "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", @@ -829,7 +1254,6 @@ "version": "2.1.1", "resolved": "https://registry.npmjs.org/require-directory/-/require-directory-2.1.1.tgz", "integrity": "sha512-fGxEI7+wsG9xrvdjsrlmL22OMTTiHRwAMroiEeMgq8gzoLC/PQr7RsRDSTLUg/bZAZtF+TVIkHc6/4RIKrui+Q==", - "dev": true, "engines": { "node": ">=0.10.0" } @@ -867,7 +1291,6 @@ "version": "4.2.3", "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", - "dev": true, "dependencies": { "emoji-regex": "^8.0.0", "is-fullwidth-code-point": "^3.0.0", @@ -881,7 +1304,6 @@ "version": "6.0.1", "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", - "dev": true, "dependencies": { "ansi-regex": "^5.0.1" }, @@ -1016,7 +1438,6 @@ "version": "7.0.0", "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz", "integrity": "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==", - "dev": true, "dependencies": { "ansi-styles": "^4.0.0", "string-width": "^4.1.0", @@ -1039,27 +1460,25 @@ "version": "5.0.8", "resolved": "https://registry.npmjs.org/y18n/-/y18n-5.0.8.tgz", "integrity": "sha512-0pfFzegeDWJHJIAmTLRP2DwHjdF5s7jo9tuztdQxAhINCdvS+3nGINqPd00AphqJR/0LhANUS6/+7SCb98YOfA==", - "dev": true, "engines": { "node": ">=10" } }, "node_modules/yargs": { - "version": "16.2.0", - "resolved": "https://registry.npmjs.org/yargs/-/yargs-16.2.0.tgz", - "integrity": "sha512-D1mvvtDG0L5ft/jGWkLpG1+m0eQxOfaBvTNELraWj22wSVUMWxZUvYgJYcKh6jGGIkJFhH4IZPQhR4TKpc8mBw==", - "dev": true, + "version": "17.7.2", + "resolved": "https://registry.npmjs.org/yargs/-/yargs-17.7.2.tgz", + "integrity": "sha512-7dSzzRQ++CKnNI/krKnYRV7JKKPUXMEh61soaHKg9mrWEhzFWhFnxPxGl+69cD1Ou63C13NUPCnmIcrvqCuM6w==", "dependencies": { - "cliui": "^7.0.2", + "cliui": "^8.0.1", "escalade": "^3.1.1", "get-caller-file": "^2.0.5", "require-directory": "^2.1.1", - "string-width": "^4.2.0", + "string-width": "^4.2.3", "y18n": "^5.0.5", - "yargs-parser": "^20.2.2" + "yargs-parser": "^21.1.1" }, "engines": { - "node": ">=10" + "node": ">=12" } }, "node_modules/yargs-parser": { @@ -1086,6 +1505,14 @@ "node": ">=10" } }, + "node_modules/yargs/node_modules/yargs-parser": { + "version": "21.1.1", + "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-21.1.1.tgz", + "integrity": "sha512-tVpsJW7DdjecAiFpbIB1e3qxIQsE6NoPc5/eTdrbbIC4h0LVsWhnoa3g+m2HclBIujHzsxZ4VJVA+GUuc2/LBw==", + "engines": { + "node": ">=12" + } + }, "node_modules/yn": { "version": "3.1.1", "resolved": "https://registry.npmjs.org/yn/-/yn-3.1.1.tgz", diff --git a/package.json b/package.json index a57c88d..7eacb88 100644 --- a/package.json +++ b/package.json @@ -4,8 +4,13 @@ "test": "mocha tests" }, "devDependencies": { + "@types/yargs": "^17.0.32", "mocha": "^10.2.0", "ts-node": "^10.9.2", + "esbuild": "^0.20.2", "typescript": "^5.3.3" + }, + "dependencies": { + "yargs": "^17.7.2" } } diff --git a/pair.ts b/pair.ts new file mode 100644 index 0000000..088a852 --- /dev/null +++ b/pair.ts @@ -0,0 +1,33 @@ +import { RemoteInfo } from "dgram"; + +import { opt } from "./options.js"; +import { discoverDevices } from "./discovery.js"; +import { DevSerial } from "./impl.js"; +import { Handlers, makeSession, Session, configureWifi } from "./session.js"; + +export const pair = ({ opts, ssid, password }: { opts: opt; ssid: string; password: string }) => { + console.log(`Will configure any devices found to join ${ssid}`); + let sessions: Record = {}; + + let devEv = discoverDevices(opts.debug, opts.discovery_ip); + if (password == "") { + throw new Error("You must set a non-zero-length password"); + } + + const onLogin = configureWifi(ssid, password); + + devEv.on("discover", (rinfo: RemoteInfo, dev: DevSerial) => { + if (dev.devId in sessions) { + console.log(`ignoring ${dev.devId} - ${rinfo.address}`); + return; + } + console.log(`discovered ${dev.devId} - ${rinfo.address}`); + const s = makeSession(Handlers, dev, rinfo, onLogin, opts); + + s.eventEmitter.on("disconnect", () => { + console.log("deleting from sessions"); + delete sessions[dev.devId]; + }); + sessions[dev.devId] = s; + }); +};