diff --git a/Makefile b/Makefile index dfa8945..c7d151f 100644 --- a/Makefile +++ b/Makefile @@ -1,4 +1,4 @@ -.PHONY: run hook install-wireshark-dissector test build +.PHONY: run hook install-wireshark-dissector test build typecheck bundle.js: frida-hooks.js func_replacements.js ~/node_modules/.bin/frida-compile -o $@ frida-hooks.js @@ -8,12 +8,13 @@ venv: requirements.txt ./venv/bin/pip install -r requirements.txt touch venv -build: node_modules +typecheck: node_modules npm run tsc +build: node_modules npm run build -run: node_modules - ./node_modules/.bin/ts-node --esm cmd/bin.ts http_server --port=1234 +run: build + node dist/bin.cjs http_server --port=1234 hook: bundle.js venv ./venv/bin/python3 -u loader3.py diff --git a/asd.html b/asd.html index 50e8e53..50d2a47 100644 --- a/asd.html +++ b/asd.html @@ -32,17 +32,6 @@ audio_button.disabled = !${audio}; update_audio_button(); - function console_log(s) { - if (!${debug}) { - return; - } - let m = ""; - for (let i = 0; i < arguments.length; i++) { - m += arguments[i]; - } - console.log(m); - } - function setup_audio() { audio_context = new AudioContext(); const gain_node = audio_context.createGain(); // Declare gain node @@ -52,7 +41,7 @@ //const audioBuffer = audio_context.createBuffer(channels, decoded.length, sample_rate); audio_context.onstatechange = () => { - console_log("Audio state is now ", audio_context.state); + console.log("Audio state is now ", audio_context.state); update_audio_button(audio_context.state == "running"); }; @@ -62,10 +51,10 @@ const evtSource = new EventSource("/audio/${id}"); if (${debug}) { evtSource.onopen = (e) => { - console_log("evtsource open"); + console.log("evtsource open"); } evtSource.onerror = (e) => { - console_log("evtsource error", e); + console.log("evtsource error", e); } } let endsAt = 0; @@ -108,7 +97,7 @@ audio_context.resume(); return; } - console_log("Unknown audio stream status"); + console.log("Unknown audio stream status"); } diff --git a/cmd/bin.ts b/cmd/bin.ts index bd0682e..328b650 100644 --- a/cmd/bin.ts +++ b/cmd/bin.ts @@ -5,17 +5,20 @@ import { serveHttp } from "../http_server.js"; import { opt } from "../options.js"; import { pair } from "../pair.js"; +import { buildLogger } from "../logger.js"; + yargs(hideBin(process.argv)) .command( "http_server", "start http server", (yargs) => { return yargs - .option("debug", { describe: "Log debug messages", default: false }) .option("ansi", { default: false }) .option("slow_startup", { default: false }) .option("audio", { describe: "Also stream audio from camera", default: true }) - .boolean(["debug", "ansi", "audio", "slow_startup"]) + .option("color", { describe: "Use color in logs", default: undefined }) + .boolean(["ansi", "audio", "slow_startup", "color"]) + .option("log_level", { describe: "Set log level", default: "info" }) .option("discovery_ip", { describe: "Camera discovery IP address", default: "192.168.1.255" }) .option("attempt_to_fix_packet_loss", { default: false }) .option("port", { describe: "HTTP Port to listen on", default: 5000 }) @@ -24,6 +27,7 @@ yargs(hideBin(process.argv)) }, (argv) => { const opts: opt = argv as opt; + buildLogger(argv.log_level, argv.color); serveHttp(opts, argv.port, argv.audio || false); }, ) @@ -32,10 +36,11 @@ yargs(hideBin(process.argv)) "configure a camera", (yargs) => { return yargs - .option("debug", { describe: "Log debug messages", default: false }) .option("ansi", { default: false }) .option("slow_startup", { default: false }) - .boolean(["debug", "ansi", "slow_startup"]) + .option("color", { describe: "Use color in logs", default: undefined }) + .boolean(["ansi", "slow_startup", "color"]) + .option("log_level", { describe: "Set log level", default: "info" }) .option("discovery_ip", { describe: "Camera discovery IP address", default: "192.168.1.255" }) .option("attempt_to_fix_packet_loss", { default: false }) .option("ssid", { describe: "Wifi network for the camera to connect to" }) @@ -45,6 +50,7 @@ yargs(hideBin(process.argv)) }, (argv) => { const opts: opt = argv as unknown as opt; + buildLogger(argv.log_level, argv.color); pair({ opts, ssid: argv.ssid, password: argv.password }); }, ) diff --git a/discovery.ts b/discovery.ts index 3c5c1e4..ac5ebab 100644 --- a/discovery.ts +++ b/discovery.ts @@ -2,21 +2,20 @@ 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 { logger } from "./logger.js"; -const handleIncomingPunch = (msg: Buffer, ee: EventEmitter, rinfo: RemoteInfo, debug: boolean) => { +const handleIncomingPunch = (msg: Buffer, ee: EventEmitter, rinfo: RemoteInfo) => { const ab = new Uint8Array(msg).buffer; const dv = new DataView(ab); const cmd_id = dv.readU16(); if (cmd_id != Commands.PunchPkt) { return; } - if (debug) { - console.log("Discovery got a PunchPkt"); - } + logger.debug("Received a PunchPkt message"); ee.emit("discover", rinfo, parse_PunchPkt(dv)); }; -export const discoverDevices = (debug: boolean, discovery_ip: string): EventEmitter => { +export const discoverDevices = (discovery_ip: string): EventEmitter => { const sock = createSocket("udp4"); const SEND_PORT = 32108; const ee = new EventEmitter(); @@ -26,15 +25,14 @@ export const discoverDevices = (debug: boolean, discovery_ip: string): EventEmit sock.close(); }); - sock.on("message", (msg, rinfo) => handleIncomingPunch(msg, ee, rinfo, debug)); + sock.on("message", (msg, rinfo) => handleIncomingPunch(msg, ee, rinfo)); sock.on("listening", () => { sock.setBroadcast(true); - console.log(`Searching for devices on ${discovery_ip}`); + logger.info(`Searching for devices on ${discovery_ip}`); let buf = create_LanSearch(); setInterval(() => { - process.stdout.write("."); sock.send(new Uint8Array(buf.buffer), SEND_PORT, discovery_ip); }, 2000); sock.send(new Uint8Array(buf.buffer), SEND_PORT, discovery_ip); diff --git a/handlers.ts b/handlers.ts index 8e7823d..179b184 100644 --- a/handlers.ts +++ b/handlers.ts @@ -1,16 +1,14 @@ import { Commands, CommandsByValue, ControlCommands } from "./datatypes.js"; import { XqBytesDec } from "./func_replacements.js"; -import { hexdump } from "./hexdump.js"; import { create_P2pRdy, SendListWifi, SendUsrChk, DevSerial } from "./impl.js"; import { Session } from "./session.js"; import { u16_swap, u32_swap } from "./utils.js"; +import { logger } from "./logger.js"; export const notImpl = (session: Session, dv: DataView) => { const raw = dv.readU16(); const cmd = CommandsByValue[raw]; - if (session.options.debug) { - console.log(`^^ ${cmd} (${raw.toString(16)}) and it's not implemented yet`); - } + logger.debug(`^^ ${cmd} (${raw.toString(16)}) and it's not implemented yet`); }; export const noop = (_: Session, __: DataView) => {}; @@ -47,17 +45,13 @@ export const createResponseForControlCommand = (session: Session, dv: DataView): const payload_len = u16_swap(dv.add(0xc).readU16()); if (start_type != 0x110a) { - console.error(`Expected start_type to be 0xa11, got 0x${start_type.toString(16)}`); + logger.error(`Expected start_type to be 0xa11, got 0x${start_type.toString(16)}`); return []; } const rotate_chr = 4; if (payload_len > rotate_chr) { // 20 = 16 (header) + 4 (??) XqBytesDec(dv.add(20), payload_len - 4, rotate_chr); - if (session.options.debug) { - console.log("Decrypted"); - console.log(hexdump(dv)); - } } if (cmd_id == ControlCommands.ConnectUserAck) { @@ -74,7 +68,7 @@ export const createResponseForControlCommand = (session: Session, dv: DataView): let dbm = dv.add(0x24).readU8() - 0x100; // 0xbf - 0x100 = -65dbm .. constant?? // > -50 = excellent, -50 to -60 good, -60 to -70 fair, <-70 weak - console.log(`Camera ${session.devName}: ${charging}charging, battery at ${power / 1000}V, Wifi ${dbm} dBm`); + logger.info(`Camera ${session.devName}: ${charging}charging, battery at ${power / 1000}V, Wifi ${dbm} dBm`); } if (cmd_id == ControlCommands.WifiSettingsAck) { @@ -94,21 +88,17 @@ export const createResponseForControlCommand = (session: Session, dv: DataView): dns2: dv.add(0x10c).readString(0x10), }; const buf = SendListWifi(session); - console.log(`Current Wifi settings: ${JSON.stringify(wifiSettings, null, 2)}`); + logger.info(`Current Wifi settings: ${JSON.stringify(wifiSettings, null, 2)}`); return [buf]; } if (cmd_id == ControlCommands.ListWifiAck) { let startat = 0x10; let msg_len = 91; - if (session.options.debug) { - console.log("payload len", payload_len); - } + logger.debug("payload len", payload_len); let msg_count = (payload_len - 9) / msg_len; let remote_msg_count = dv.add(startat).readU32LE(); - if (session.options.debug) { - console.log("should get messages:", msg_count, "in payload: ", remote_msg_count); - } + logger.debug("should get messages:", msg_count, "in payload: ", remote_msg_count); startat += 4; let items = []; for (let i = 0; i < msg_count; i++) { @@ -122,11 +112,9 @@ export const createResponseForControlCommand = (session: Session, dv: DataView): mode: dv.add(startat + 0x54).readU32LE(), channel: dv.add(startat + 0x58).readU32LE(), }; - console.log(`Wifi Item: ${JSON.stringify(wifiListItem, null, 2)}`); + logger.info(`Wifi Item: ${JSON.stringify(wifiListItem, null, 2)}`); startat += msg_len; - if (session.options.debug) { - console.log("ended at", startat); - } + logger.debug("ended at", startat); items.push(wifiListItem); } } diff --git a/http_server.ts b/http_server.ts index 26ab7fa..0f74140 100644 --- a/http_server.ts +++ b/http_server.ts @@ -2,6 +2,7 @@ import { RemoteInfo } from "dgram"; import { readFileSync, existsSync } from "node:fs"; import http from "node:http"; +import { logger } from "./logger.js"; import { opt } from "./options.js"; import { discoverDevices } from "./discovery.js"; import { DevSerial, SendDevStatus } from "./impl.js"; @@ -77,7 +78,7 @@ export const serveHttp = (opts: opt, port: number, with_audio: boolean) => { } res.setHeader("Content-Type", `text/event-stream`); audioResponses[devId].push(res); - console.log(`Audio stream requested for camera ${devId}`); + logger.info(`Audio stream requested for camera ${devId}`); return; } @@ -90,7 +91,7 @@ export const serveHttp = (opts: opt, port: number, with_audio: boolean) => { if (req.url.startsWith("/camera/")) { let devId = req.url.split("/")[2]; - console.log(`Video stream requested for camera ${devId}`); + logger.info(`Video stream requested for camera ${devId}`); let s = sessions[devId]; if (s === undefined) { @@ -108,7 +109,7 @@ export const serveHttp = (opts: opt, port: number, with_audio: boolean) => { responses[devId].push(res); res.on("close", () => { responses[devId] = responses[devId].filter((r) => r !== res); - console.log(`Video stream closed for camera ${devId}`); + logger.info(`Video stream closed for camera ${devId}`); }); } else { res.write(""); @@ -127,21 +128,21 @@ export const serveHttp = (opts: opt, port: number, with_audio: boolean) => { } }); - let devEv = discoverDevices(opts.debug, opts.discovery_ip); + let devEv = discoverDevices(opts.discovery_ip); const startSession = (s: Session) => { s.send(SendDevStatus(s)); startVideoStream(s); - console.log(`Camera ${s.devName} is now ready to stream`); - } + logger.info(`Camera ${s.devName} is now ready to stream`); + }; devEv.on("discover", (rinfo: RemoteInfo, dev: DevSerial) => { if (dev.devId in sessions) { - console.log(`Camera ${dev.devId} at ${rinfo.address} already discovered, ignoring`); + logger.info(`Camera ${dev.devId} at ${rinfo.address} already discovered, ignoring`); return; } - console.log(`Discovered camera ${dev.devId} at ${rinfo.address}`); + logger.info(`Discovered camera ${dev.devId} at ${rinfo.address}`); responses[dev.devId] = []; audioResponses[dev.devId] = []; const s = makeSession(Handlers, dev, rinfo, startSession, opts); @@ -158,7 +159,7 @@ export const serveHttp = (opts: opt, port: number, with_audio: boolean) => { }); s.eventEmitter.on("disconnect", () => { - console.log(`Camera ${dev.devId} disconnected`); + logger.info(`Camera ${dev.devId} disconnected`); delete sessions[dev.devId]; }); if (with_audio) { @@ -175,6 +176,6 @@ export const serveHttp = (opts: opt, port: number, with_audio: boolean) => { sessions[dev.devId] = s; }); - console.log(`Starting HTTP server on port ${port}`); + logger.info(`Starting HTTP server on port ${port}`); server.listen(port); }; diff --git a/logger.ts b/logger.ts new file mode 100644 index 0000000..56b59bb --- /dev/null +++ b/logger.ts @@ -0,0 +1,26 @@ +import { isatty } from "node:tty"; +import { addColors, config, createLogger, format, transports as wtransports } from "winston"; + +const myFormat = format.printf(({ level, message, timestamp }) => { + return `${timestamp} [${level}] ${message}`; +}); + +const transports = { + console: new wtransports.Console(), +}; + +export let logger = undefined; + +export const buildLogger = (level: string, colorize: boolean | undefined) => { + let use_color = colorize === undefined ? isatty(1) : colorize; + const fmt = use_color + ? format.combine(format.colorize(), format.timestamp(), myFormat) + : format.combine(format.timestamp(), myFormat); + logger = createLogger({ + levels: { ...config.syslog.levels, trace: 10 }, + level, + format: fmt, + transports: [transports.console], + }); + addColors({ trace: "white" }); +}; diff --git a/options.ts b/options.ts index f9b416d..3c3df45 100644 --- a/options.ts +++ b/options.ts @@ -1,5 +1,4 @@ export type opt = { - debug: boolean; ansi: boolean; discovery_ip: string; slow_startup: boolean; diff --git a/package-lock.json b/package-lock.json index 434668f..16f596f 100644 --- a/package-lock.json +++ b/package-lock.json @@ -5,16 +5,25 @@ "packages": { "": { "dependencies": { - "esbuild": "^0.20.2", + "winston": "^3.13.0", "yargs": "^17.7.2" }, "devDependencies": { "@types/yargs": "^17.0.32", + "esbuild": "^0.20.2", "mocha": "^10.2.0", "ts-node": "^10.9.2", "typescript": "^5.3.3" } }, + "node_modules/@colors/colors": { + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/@colors/colors/-/colors-1.6.0.tgz", + "integrity": "sha512-Ir+AOibqzrIsL6ajt3Rz3LskB7OiMVHqltZmspbW/TJuTVuyOMirVqAkjfY6JISiLHgyNqicAC8AyHHGzNd/dA==", + "engines": { + "node": ">=0.1.90" + } + }, "node_modules/@cspotcode/source-map-support": { "version": "0.8.1", "resolved": "https://registry.npmjs.org/@cspotcode/source-map-support/-/source-map-support-0.8.1.tgz", @@ -27,6 +36,16 @@ "node": ">=12" } }, + "node_modules/@dabh/diagnostics": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/@dabh/diagnostics/-/diagnostics-2.0.3.tgz", + "integrity": "sha512-hrlQOIi7hAfzsMqlGSFyVucrx38O+j6wiGOf//H2ecvIEqYN4ADBSS2iLMh5UFyDunCNniUIPk/q3riFv45xRA==", + "dependencies": { + "colorspace": "1.1.x", + "enabled": "2.0.x", + "kuler": "^2.0.0" + } + }, "node_modules/@esbuild/aix-ppc64": { "version": "0.20.2", "resolved": "https://registry.npmjs.org/@esbuild/aix-ppc64/-/aix-ppc64-0.20.2.tgz", @@ -34,6 +53,7 @@ "cpu": [ "ppc64" ], + "dev": true, "optional": true, "os": [ "aix" @@ -49,6 +69,7 @@ "cpu": [ "arm" ], + "dev": true, "optional": true, "os": [ "android" @@ -64,6 +85,7 @@ "cpu": [ "arm64" ], + "dev": true, "optional": true, "os": [ "android" @@ -79,6 +101,7 @@ "cpu": [ "x64" ], + "dev": true, "optional": true, "os": [ "android" @@ -94,6 +117,7 @@ "cpu": [ "arm64" ], + "dev": true, "optional": true, "os": [ "darwin" @@ -109,6 +133,7 @@ "cpu": [ "x64" ], + "dev": true, "optional": true, "os": [ "darwin" @@ -124,6 +149,7 @@ "cpu": [ "arm64" ], + "dev": true, "optional": true, "os": [ "freebsd" @@ -139,6 +165,7 @@ "cpu": [ "x64" ], + "dev": true, "optional": true, "os": [ "freebsd" @@ -154,6 +181,7 @@ "cpu": [ "arm" ], + "dev": true, "optional": true, "os": [ "linux" @@ -169,6 +197,7 @@ "cpu": [ "arm64" ], + "dev": true, "optional": true, "os": [ "linux" @@ -184,6 +213,7 @@ "cpu": [ "ia32" ], + "dev": true, "optional": true, "os": [ "linux" @@ -199,6 +229,7 @@ "cpu": [ "loong64" ], + "dev": true, "optional": true, "os": [ "linux" @@ -214,6 +245,7 @@ "cpu": [ "mips64el" ], + "dev": true, "optional": true, "os": [ "linux" @@ -229,6 +261,7 @@ "cpu": [ "ppc64" ], + "dev": true, "optional": true, "os": [ "linux" @@ -244,6 +277,7 @@ "cpu": [ "riscv64" ], + "dev": true, "optional": true, "os": [ "linux" @@ -259,6 +293,7 @@ "cpu": [ "s390x" ], + "dev": true, "optional": true, "os": [ "linux" @@ -274,6 +309,7 @@ "cpu": [ "x64" ], + "dev": true, "optional": true, "os": [ "linux" @@ -289,6 +325,7 @@ "cpu": [ "x64" ], + "dev": true, "optional": true, "os": [ "netbsd" @@ -304,6 +341,7 @@ "cpu": [ "x64" ], + "dev": true, "optional": true, "os": [ "openbsd" @@ -319,6 +357,7 @@ "cpu": [ "x64" ], + "dev": true, "optional": true, "os": [ "sunos" @@ -334,6 +373,7 @@ "cpu": [ "arm64" ], + "dev": true, "optional": true, "os": [ "win32" @@ -349,6 +389,7 @@ "cpu": [ "ia32" ], + "dev": true, "optional": true, "os": [ "win32" @@ -364,6 +405,7 @@ "cpu": [ "x64" ], + "dev": true, "optional": true, "os": [ "win32" @@ -431,6 +473,11 @@ "undici-types": "~5.26.4" } }, + "node_modules/@types/triple-beam": { + "version": "1.3.5", + "resolved": "https://registry.npmjs.org/@types/triple-beam/-/triple-beam-1.3.5.tgz", + "integrity": "sha512-6WaYesThRMCl19iryMYP7/x2OVgCtbIVflDGFpWnb9irXI3UjYE4AzmYuiUKY1AJstGijoY+MgUszMgRxIYTYw==" + }, "node_modules/@types/yargs": { "version": "17.0.32", "resolved": "https://registry.npmjs.org/@types/yargs/-/yargs-17.0.32.tgz", @@ -523,6 +570,11 @@ "integrity": "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==", "dev": true }, + "node_modules/async": { + "version": "3.2.5", + "resolved": "https://registry.npmjs.org/async/-/async-3.2.5.tgz", + "integrity": "sha512-baNZyqaaLhyLVKm/DlvdW051MSgO6b8eVfIezl9E5PqWxFgzLm/wQntEW4zOytVburDEr0JlALEpdOFwvErLsg==" + }, "node_modules/balanced-match": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz", @@ -645,6 +697,15 @@ "node": ">=12" } }, + "node_modules/color": { + "version": "3.2.1", + "resolved": "https://registry.npmjs.org/color/-/color-3.2.1.tgz", + "integrity": "sha512-aBl7dZI9ENN6fUGC7mWpMTPNHmWUSNan9tuWN6ahh5ZLNk9baLJOnSMlrQkHcrfFgz2/RigjUVAjdx36VcemKA==", + "dependencies": { + "color-convert": "^1.9.3", + "color-string": "^1.6.0" + } + }, "node_modules/color-convert": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", @@ -661,6 +722,37 @@ "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==" }, + "node_modules/color-string": { + "version": "1.9.1", + "resolved": "https://registry.npmjs.org/color-string/-/color-string-1.9.1.tgz", + "integrity": "sha512-shrVawQFojnZv6xM40anx4CkoDP+fZsw/ZerEMsW/pyzsRbElpsL/DBVW7q3ExxwusdNXI3lXpuhEZkzs8p5Eg==", + "dependencies": { + "color-name": "^1.0.0", + "simple-swizzle": "^0.2.2" + } + }, + "node_modules/color/node_modules/color-convert": { + "version": "1.9.3", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz", + "integrity": "sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==", + "dependencies": { + "color-name": "1.1.3" + } + }, + "node_modules/color/node_modules/color-name": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz", + "integrity": "sha512-72fSenhMw2HZMTVHeCA9KCmpEIbzWiQsjN+BHcBbS9vr1mtt+vJjPdksIBNUmKAW8TFUDPJK5SUU3QhE9NEXDw==" + }, + "node_modules/colorspace": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/colorspace/-/colorspace-1.1.4.tgz", + "integrity": "sha512-BgvKJiuVu1igBUF2kEjRCZXol6wiiGbY5ipL/oVPwm0BL9sIpMIzM8IK7vwuxIIzOXMV3Ey5w+vxhm0rR/TN8w==", + "dependencies": { + "color": "^3.1.3", + "text-hex": "1.0.x" + } + }, "node_modules/concat-map": { "version": "0.0.1", "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", @@ -722,10 +814,16 @@ "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==" }, + "node_modules/enabled": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/enabled/-/enabled-2.0.0.tgz", + "integrity": "sha512-AKrN98kuwOzMIdAizXGI86UFBoo26CL21UM763y1h/GMSJ4/OHU9k2YlsmBpyScFo/wbLzWQJBMCW4+IO3/+OQ==" + }, "node_modules/esbuild": { "version": "0.20.2", "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.20.2.tgz", "integrity": "sha512-WdOOppmUNU+IbZ0PaDiTst80zjnrOkyJNHoKupIcVyU8Lvla3Ugx94VzkQ32Ijqd7UhHJy75gNWDMUekcrSJ6g==", + "dev": true, "hasInstallScript": true, "bin": { "esbuild": "bin/esbuild" @@ -779,6 +877,11 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/fecha": { + "version": "4.2.3", + "resolved": "https://registry.npmjs.org/fecha/-/fecha-4.2.3.tgz", + "integrity": "sha512-OP2IUU6HeYKJi3i0z4A19kHMQoLVs4Hc+DPqqxI2h/DPZHTm/vjsfC6P0b4jCMy14XizLBqvndQ+UilD7707Jw==" + }, "node_modules/fill-range": { "version": "7.0.1", "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.0.1.tgz", @@ -816,6 +919,11 @@ "flat": "cli.js" } }, + "node_modules/fn.name": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/fn.name/-/fn.name-1.1.0.tgz", + "integrity": "sha512-GRnmB5gPyJpAhTQdSZTSp9uaPSvl09KoYcMQtsB9rQoOmzs9dH6ffeccH+Z+cv6P68Hu5bC6JjRh4Ah/mHSNRw==" + }, "node_modules/fs.realpath": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz", @@ -929,8 +1037,12 @@ "node_modules/inherits": { "version": "2.0.4", "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", - "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==", - "dev": true + "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==" + }, + "node_modules/is-arrayish": { + "version": "0.3.2", + "resolved": "https://registry.npmjs.org/is-arrayish/-/is-arrayish-0.3.2.tgz", + "integrity": "sha512-eVRqCvVlZbuw3GrM63ovNSNAeA1K16kaR/LRY/92w0zxQ5/1YzwblUX652i4Xs9RwAGjW9d9y6X88t8OaAJfWQ==" }, "node_modules/is-binary-path": { "version": "2.1.0", @@ -991,6 +1103,17 @@ "node": ">=8" } }, + "node_modules/is-stream": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/is-stream/-/is-stream-2.0.1.tgz", + "integrity": "sha512-hFoiJiTl63nn+kstHGBtewWSKnQLpyb155KHheA1l39uvtO9nWIop1p3udqPcUd/xbF1VLMO4n7OI6p7RbngDg==", + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/is-unicode-supported": { "version": "0.1.0", "resolved": "https://registry.npmjs.org/is-unicode-supported/-/is-unicode-supported-0.1.0.tgz", @@ -1015,6 +1138,11 @@ "js-yaml": "bin/js-yaml.js" } }, + "node_modules/kuler": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/kuler/-/kuler-2.0.0.tgz", + "integrity": "sha512-Xq9nH7KlWZmXAtodXDDRE7vs6DU1gTU8zYDHDiWLSip45Egwq3plLHzPn27NgvzL2r1LMPC1vdqh98sQxtqj4A==" + }, "node_modules/locate-path": { "version": "6.0.0", "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-6.0.0.tgz", @@ -1046,6 +1174,22 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/logform": { + "version": "2.6.0", + "resolved": "https://registry.npmjs.org/logform/-/logform-2.6.0.tgz", + "integrity": "sha512-1ulHeNPp6k/LD8H91o7VYFBng5i1BDE7HoKxVbZiGFidS1Rj65qcywLxX+pVfAPoQJEjRdvKcusKwOupHCVOVQ==", + "dependencies": { + "@colors/colors": "1.6.0", + "@types/triple-beam": "^1.3.2", + "fecha": "^4.2.0", + "ms": "^2.1.1", + "safe-stable-stringify": "^2.3.1", + "triple-beam": "^1.3.0" + }, + "engines": { + "node": ">= 12.0.0" + } + }, "node_modules/make-error": { "version": "1.3.6", "resolved": "https://registry.npmjs.org/make-error/-/make-error-1.3.6.tgz", @@ -1136,8 +1280,7 @@ "node_modules/ms": { "version": "2.1.3", "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", - "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", - "dev": true + "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==" }, "node_modules/nanoid": { "version": "3.3.3", @@ -1169,6 +1312,14 @@ "wrappy": "1" } }, + "node_modules/one-time": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/one-time/-/one-time-1.0.0.tgz", + "integrity": "sha512-5DXOiRKwuSEcQ/l0kGCF6Q3jcADFv5tSmRaJck/OqkVFcOzutB134KRSfF0xDrL39MNnqxbHBbUUcjZIhTgb2g==", + "dependencies": { + "fn.name": "1.x.x" + } + }, "node_modules/p-limit": { "version": "3.1.0", "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-3.1.0.tgz", @@ -1238,6 +1389,19 @@ "safe-buffer": "^5.1.0" } }, + "node_modules/readable-stream": { + "version": "3.6.2", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.2.tgz", + "integrity": "sha512-9u/sniCrY3D5WdsERHzHE4G2YCXqoG5FTHUiCC4SIbr6XcLZBY05ya9EKjYek9O5xOAwjGq+1JdGBAS7Q9ScoA==", + "dependencies": { + "inherits": "^2.0.3", + "string_decoder": "^1.1.1", + "util-deprecate": "^1.0.1" + }, + "engines": { + "node": ">= 6" + } + }, "node_modules/readdirp": { "version": "3.6.0", "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-3.6.0.tgz", @@ -1262,7 +1426,6 @@ "version": "5.2.1", "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz", "integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==", - "dev": true, "funding": [ { "type": "github", @@ -1278,6 +1441,14 @@ } ] }, + "node_modules/safe-stable-stringify": { + "version": "2.4.3", + "resolved": "https://registry.npmjs.org/safe-stable-stringify/-/safe-stable-stringify-2.4.3.tgz", + "integrity": "sha512-e2bDA2WJT0wxseVd4lsDP4+3ONX6HpMXQa1ZhFQ7SU+GjvORCmShbCMltrtIDfkYhVHrOcPtj+KhmDBdPdZD1g==", + "engines": { + "node": ">=10" + } + }, "node_modules/serialize-javascript": { "version": "6.0.0", "resolved": "https://registry.npmjs.org/serialize-javascript/-/serialize-javascript-6.0.0.tgz", @@ -1287,6 +1458,30 @@ "randombytes": "^2.1.0" } }, + "node_modules/simple-swizzle": { + "version": "0.2.2", + "resolved": "https://registry.npmjs.org/simple-swizzle/-/simple-swizzle-0.2.2.tgz", + "integrity": "sha512-JA//kQgZtbuY83m+xT+tXJkmJncGMTFT+C+g2h2R9uxkYIrE2yy9sgmcLhCnw57/WSD+Eh3J97FPEDFnbXnDUg==", + "dependencies": { + "is-arrayish": "^0.3.1" + } + }, + "node_modules/stack-trace": { + "version": "0.0.10", + "resolved": "https://registry.npmjs.org/stack-trace/-/stack-trace-0.0.10.tgz", + "integrity": "sha512-KGzahc7puUKkzyMt+IqAep+TVNbKP+k2Lmwhub39m1AsTSkaDutx56aDCo+HLDzf/D26BIHTJWNiTG1KAJiQCg==", + "engines": { + "node": "*" + } + }, + "node_modules/string_decoder": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.3.0.tgz", + "integrity": "sha512-hkRX8U1WjJFd8LsDJ2yQ/wWWxaopEsABU1XfkM8A+j0+85JAGppt16cr1Whg6KIbb4okU6Mql6BOj+uup/wKeA==", + "dependencies": { + "safe-buffer": "~5.2.0" + } + }, "node_modules/string-width": { "version": "4.2.3", "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", @@ -1338,6 +1533,11 @@ "url": "https://github.com/chalk/supports-color?sponsor=1" } }, + "node_modules/text-hex": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/text-hex/-/text-hex-1.0.0.tgz", + "integrity": "sha512-uuVGNWzgJ4yhRaNSiubPY7OjISw4sw4E5Uv0wbjp+OzcbmVU/rsT8ujgcXJhn9ypzsgr5vlzpPqP+MBBKcGvbg==" + }, "node_modules/to-regex-range": { "version": "5.0.1", "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz", @@ -1350,6 +1550,14 @@ "node": ">=8.0" } }, + "node_modules/triple-beam": { + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/triple-beam/-/triple-beam-1.4.1.tgz", + "integrity": "sha512-aZbgViZrg1QNcG+LULa7nhZpJTZSLm/mXnHXnbAbjmN5aSa0y7V+wvv6+4WaBtpISJzThKy+PIPxc1Nq1EJ9mg==", + "engines": { + "node": ">= 14.0.0" + } + }, "node_modules/ts-node": { "version": "10.9.2", "resolved": "https://registry.npmjs.org/ts-node/-/ts-node-10.9.2.tgz", @@ -1422,12 +1630,51 @@ "dev": true, "peer": true }, + "node_modules/util-deprecate": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz", + "integrity": "sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==" + }, "node_modules/v8-compile-cache-lib": { "version": "3.0.1", "resolved": "https://registry.npmjs.org/v8-compile-cache-lib/-/v8-compile-cache-lib-3.0.1.tgz", "integrity": "sha512-wa7YjyUGfNZngI/vtK0UHAN+lgDCxBPCylVXGp0zu59Fz5aiGtNXaq3DhIov063MorB+VfufLh3JlF2KdTK3xg==", "dev": true }, + "node_modules/winston": { + "version": "3.13.0", + "resolved": "https://registry.npmjs.org/winston/-/winston-3.13.0.tgz", + "integrity": "sha512-rwidmA1w3SE4j0E5MuIufFhyJPBDG7Nu71RkZor1p2+qHvJSZ9GYDA81AyleQcZbh/+V6HjeBdfnTZJm9rSeQQ==", + "dependencies": { + "@colors/colors": "^1.6.0", + "@dabh/diagnostics": "^2.0.2", + "async": "^3.2.3", + "is-stream": "^2.0.0", + "logform": "^2.4.0", + "one-time": "^1.0.0", + "readable-stream": "^3.4.0", + "safe-stable-stringify": "^2.3.1", + "stack-trace": "0.0.x", + "triple-beam": "^1.3.0", + "winston-transport": "^4.7.0" + }, + "engines": { + "node": ">= 12.0.0" + } + }, + "node_modules/winston-transport": { + "version": "4.7.0", + "resolved": "https://registry.npmjs.org/winston-transport/-/winston-transport-4.7.0.tgz", + "integrity": "sha512-ajBj65K5I7denzer2IYW6+2bNIVqLGDHqDw3Ow8Ohh+vdW+rv4MZ6eiDvHoKhfJFZ2auyN8byXieDDJ96ViONg==", + "dependencies": { + "logform": "^2.3.2", + "readable-stream": "^3.6.0", + "triple-beam": "^1.3.0" + }, + "engines": { + "node": ">= 12.0.0" + } + }, "node_modules/workerpool": { "version": "6.2.1", "resolved": "https://registry.npmjs.org/workerpool/-/workerpool-6.2.1.tgz", diff --git a/package.json b/package.json index be45afe..884774a 100644 --- a/package.json +++ b/package.json @@ -13,6 +13,7 @@ "typescript": "^5.3.3" }, "dependencies": { + "winston": "^3.13.0", "yargs": "^17.7.2" } } diff --git a/pair.ts b/pair.ts index dab9087..7f15a3c 100644 --- a/pair.ts +++ b/pair.ts @@ -4,32 +4,33 @@ import { opt } from "./options.js"; import { discoverDevices } from "./discovery.js"; import { DevSerial } from "./impl.js"; import { Handlers, makeSession, Session, configureWifi } from "./session.js"; +import { logger } from "./logger.js"; export const pair = ({ opts, ssid, password }: { opts: opt; ssid: string; password: string }) => { - console.log(`Will configure any devices found to join ${ssid}`); + logger.info(`Will configure any devices found to join ${ssid}`); let sessions: Record = {}; - let devEv = discoverDevices(opts.debug, opts.discovery_ip); + let devEv = discoverDevices(opts.discovery_ip); if (password == "") { throw new Error("You must set a non-zero-length password"); } const onLogin = (s: Session) => { configureWifi(ssid, password)(s); - console.log(`WiFi config for camera ${s.devName} is done`); - } + logger.info(`WiFi config for camera ${s.devName} is done`); + }; devEv.on("discover", (rinfo: RemoteInfo, dev: DevSerial) => { if (dev.devId in sessions) { - console.log(`Camera ${dev.devId} at ${rinfo.address} already discovered, ignoring`); + logger.info(`Camera ${dev.devId} at ${rinfo.address} already discovered, ignoring`); return; } - console.log(`Discovered camera ${dev.devId} at ${rinfo.address}`); + logger.info(`Discovered camera ${dev.devId} at ${rinfo.address}`); const s = makeSession(Handlers, dev, rinfo, onLogin, opts); s.eventEmitter.on("disconnect", () => { - console.log(`Camera ${dev.devId} disconnected`); - console.log("Press CONTROL+C if you're done setting up your cameras"); + logger.info(`Camera ${dev.devId} disconnected`); + logger.info("Press CONTROL+C if you're done setting up your cameras"); delete sessions[dev.devId]; }); sessions[dev.devId] = s; diff --git a/session.ts b/session.ts index f75f986..232d3dd 100644 --- a/session.ts +++ b/session.ts @@ -3,9 +3,9 @@ import EventEmitter from "node:events"; import { Commands, CommandsByValue } from "./datatypes.js"; import { handle_Drw, handle_P2PAlive, handle_P2PRdy, makeP2pRdy, noop, notImpl } from "./handlers.js"; -import { hexdump } from "./hexdump.js"; import { create_P2pAlive, DevSerial, SendStartVideo, SendVideoResolution, SendWifiDetails } from "./impl.js"; import { opt } from "./options.js"; +import { logger } from "./logger.js"; export type Session = { send: (msg: DataView) => void; @@ -38,10 +38,7 @@ const handleIncoming: msgCb = (session, handlers, msg, rinfo) => { const ab = new Uint8Array(msg).buffer; const dv = new DataView(ab); const cmd = CommandsByValue[dv.readU16()]; - if (session.options.debug) { - console.log(`<< ${cmd}`); - console.log(hexdump(msg.buffer, { ansi: session.options.ansi, ansiColor: 1 })); - } + logger.log("trace", `<< ${cmd}`); handlers[cmd](session, dv, rinfo); session.lastReceivedPacket = Date.now(); }; @@ -105,12 +102,7 @@ export const makeSession = ( send: (msg: DataView) => { const raw = msg.readU16(); const cmd = CommandsByValue[raw]; - if (options.debug) { - console.log(`>> ${cmd}`); - if (raw != Commands.P2PAlive) { - console.log(hexdump(msg.buffer, { ansi: options.ansi, ansiColor: 0 })); - } - } + logger.log("trace", `>> ${cmd}`); sock.send(new Uint8Array(msg.buffer), SEND_PORT, session.dst_ip); }, dst_ip: ra.address, @@ -122,7 +114,7 @@ export const makeSession = ( }; session.eventEmitter.on("disconnect", () => { - console.log(`Disconnected from camera ${session.devName} at ${session.dst_ip}`); + logger.info(`Disconnected from camera ${session.devName} at ${session.dst_ip}`); session.dst_ip = "0.0.0.0"; session.connected = false; session.timers.forEach((x) => clearInterval(x)); @@ -130,7 +122,7 @@ export const makeSession = ( }); session.eventEmitter.on("login", () => { - console.log(`Logging in to camera ${session.devName}`); + logger.info(`Logging in to camera ${session.devName}`); onLogin(session); }); return session;