diff --git a/zefie_wtvp_minisrv/modem_proxy.js b/zefie_wtvp_minisrv/modem_proxy.js new file mode 100644 index 00000000..3c28fc2f --- /dev/null +++ b/zefie_wtvp_minisrv/modem_proxy.js @@ -0,0 +1,361 @@ +// Rockwell to USRobotics Modem Proxy for MAME Bitbanger +const net = require('net'); +const { SerialPort } = require('serialport'); +const { ReadlineParser } = require('@serialport/parser-readline'); + +// Configuration +const TCP_IP = '127.0.0.1'; +const TCP_PORT = 57388; +const SERIAL_PORT = 'COM3'; +const SERIAL_BAUDRATE = 115200; + +let NEXT_RECV_IS_LAST_ASCII = false; +let DATA_MODE = false; + +const THINGS_TO_STRIP = ["S95=36", "&Q5", "S51=31"]; +const THINGS_TO_REPLACE = [ + ["M0", "M1"], // M1 = Speaker on + ["S11=110", "S11=50"], // S11 = Dial speed + ["S11=200", "S11=50"], // S11 = Dial speed + ["18004653537", "5736666"], + ["18006138199", "5736666"] +]; + +// Global variables +let serialPort = null; +let server = null; +let currentClient = null; + +// Initialize serial port +function initSerial() { + return new Promise((resolve, reject) => { + try { + serialPort = new SerialPort({ + path: SERIAL_PORT, + baudRate: SERIAL_BAUDRATE, + autoOpen: false, + // Disable buffering for immediate data flow + highWaterMark: 1, + // Set minimal timeouts + dataBits: 8, + stopBits: 1, + parity: 'none', + rtscts: false, + xon: false, + xoff: false, + xany: false + }); + + serialPort.open((err) => { + if (err) { + console.error('Error opening serial port:', err.message); + reject(err); + return; + } + console.log(`Serial port ${SERIAL_PORT} opened at ${SERIAL_BAUDRATE} baud`); + + // Disable any internal buffering + serialPort.set({ + brk: false, + cts: false, + dtr: true, + rts: true + }); + + // Add a small delay to ensure port is ready + setTimeout(() => { + resolve(serialPort); + }, 100); + }); + + } catch (error) { + console.error('Error initializing serial port:', error); + reject(error); + } + }); +} + +// Reset modem to command mode and hang up +function resetModemToCommandMode() { + try { + // Send escape sequence to exit data mode + serialPort.write(Buffer.from('+++', 'ascii')); + setTimeout(() => { + // Send hang up command + serialPort.write(Buffer.from('ATH\r', 'ascii')); + console.log("Sent modem reset commands: +++ and ATH"); + }, 500); + } catch (error) { + console.error('Error resetting modem:', error); + } +} + +// Handle data from socket to serial +function handleSocketToSerial(socket) { + let buffer = Buffer.alloc(0); + + socket.on('data', (data) => { + if (!DATA_MODE) { + buffer = Buffer.concat([buffer, data]); + + // Process commands only after receiving a complete command ending in '\r' + // Use Buffer.indexOf to find CR byte (0x0D) for binary safety + const crIndex = buffer.indexOf(0x0D); // '\r' = 0x0D + + if (crIndex === -1) { + return; // Wait for complete command + } + + // Extract command as buffer first, then convert to string only for processing + const commandBuffer = buffer.slice(0, crIndex); + let command = commandBuffer.toString('ascii').trim(); + buffer = buffer.slice(crIndex + 1); + + // Apply string stripping and replacement + THINGS_TO_STRIP.forEach(s => { + command = command.replace(s, ""); + }); + + THINGS_TO_REPLACE.forEach(([find, replace]) => { + command = command.replace(find, replace); + }); + + const commandBytes = Buffer.from(command + '\r', 'ascii'); + console.log("WEBTV COMMAND:", commandBytes.toString('ascii').trim()); + + if (command + '\r' === 'ATD\r') { + NEXT_RECV_IS_LAST_ASCII = true; + console.log("ATD detected - next serial response will trigger data mode"); + } + + try { + serialPort.write(commandBytes, (err) => { + if (err) { + console.error('Error writing command to serial port:', err); + } else { + // Force immediate transmission of commands too + serialPort.drain(); + } + }); + } catch (error) { + console.error('Error writing to serial port:', error); + } + } else { + // In data mode, pass through binary data unchanged with immediate write + try { + serialPort.write(data, (err) => { + if (err) { + console.error('Error writing to serial port:', err); + } else { + // Force immediate transmission + serialPort.drain((drainErr) => { + if (drainErr) { + console.error('Error draining serial port:', drainErr); + } + }); + } + }); + } catch (error) { + console.error('Error writing to serial port:', error); + } + } + }); + + socket.on('error', (err) => { + console.error('Socket error:', err); + if (DATA_MODE) { + console.log("Exiting data mode due to socket error"); + DATA_MODE = false; + NEXT_RECV_IS_LAST_ASCII = false; + resetModemToCommandMode(); + } + }); + + socket.on('close', () => { + console.log("Socket closed by remote"); + if (DATA_MODE) { + console.log("Exiting data mode due to socket disconnect"); + DATA_MODE = false; + NEXT_RECV_IS_LAST_ASCII = false; + resetModemToCommandMode(); + } + currentClient = null; + }); +} + +// Handle data from serial to socket +function handleSerialToSocket(socket) { + const dataHandler = (data) => { + + // Check if we're switching to data mode + if (!DATA_MODE && NEXT_RECV_IS_LAST_ASCII) { + DATA_MODE = true; + NEXT_RECV_IS_LAST_ASCII = false; + // Provide connection result and enable data mode + data = Buffer.from('79\r\n67\r\n19\r\n', 'ascii'); + console.log("MODEM CONNECT RESULT:", data.toString('ascii')); + console.log("Data mode enabled"); + } else if (!DATA_MODE) { + console.log("MODEM COMMAND RESPONSE:", data.toString('ascii')); + } + + // Check for disabling data mode if unsupported or exit flags are received + // Use Buffer.equals for binary-safe comparison + const escapeSeq = Buffer.from("+++\r", 'ascii'); + const exitCode = Buffer.from("3\r", 'ascii'); + + if (data.equals(escapeSeq) || data.equals(exitCode)) { + console.log("Data mode disabled by serial response"); + DATA_MODE = false; + } + + try { + if (socket && !socket.destroyed) { + socket.write(data); + } else { + console.log("[SERIAL->SOCKET] Cannot send - socket destroyed or null"); + } + } catch (error) { + console.error('Send error:', error); + if (DATA_MODE) { + console.log("Exiting data mode due to send error"); + DATA_MODE = false; + NEXT_RECV_IS_LAST_ASCII = false; + resetModemToCommandMode(); + } + } + }; + + const errorHandler = (err) => { + console.error('Serial port error:', err); + if (DATA_MODE) { + console.log("Exiting data mode due to serial error"); + DATA_MODE = false; + NEXT_RECV_IS_LAST_ASCII = false; + resetModemToCommandMode(); + } + }; + + console.log("[SERIAL] Setting up data and error handlers"); + serialPort.on('data', dataHandler); + serialPort.on('error', errorHandler); + + // Return cleanup function + return () => { + console.log("[SERIAL] Cleaning up event handlers"); + serialPort.removeListener('data', dataHandler); + serialPort.removeListener('error', errorHandler); + }; +} + +// Handle new client connection +function handleClient(socket) { + // Reset state for new connection + DATA_MODE = false; + NEXT_RECV_IS_LAST_ASCII = false; + console.log("Reset modem state for new connection"); + + // Disable TCP buffering for immediate data flow + socket.setNoDelay(true); + socket.setTimeout(0); + + currentClient = socket; + + handleSocketToSerial(socket); + const cleanupSerial = handleSerialToSocket(socket); + + socket.on('close', () => { + // Ensure data mode is reset when client disconnects + if (DATA_MODE) { + console.log("Client disconnected, exiting data mode"); + DATA_MODE = false; + NEXT_RECV_IS_LAST_ASCII = false; + resetModemToCommandMode(); + } + + // Clean up serial event listeners + cleanupSerial(); + currentClient = null; + }); + + socket.on('error', (err) => { + console.error('Client socket error:', err); + cleanupSerial(); + currentClient = null; + }); +} + +// Clean shutdown procedure +function cleanup() { + console.log("Cleaning up..."); + + // Reset modem if we're in data mode + if (DATA_MODE) { + console.log("Resetting modem before shutdown"); + DATA_MODE = false; + NEXT_RECV_IS_LAST_ASCII = false; + resetModemToCommandMode(); + } + + if (currentClient) { + currentClient.destroy(); + } + + if (server) { + server.close(); + } + + if (serialPort && serialPort.isOpen) { + serialPort.close(); + } +} + +// Signal handlers +process.on('SIGINT', () => { + console.log('\nReceived interrupt signal, shutting down...'); + cleanup(); + process.exit(0); +}); + +process.on('SIGTERM', () => { + console.log('\nReceived terminate signal, shutting down...'); + cleanup(); + process.exit(0); +}); + +// Main execution +async function main() { + try { + // Initialize serial port and wait for it to be ready + await initSerial(); + + // Start TCP server + server = net.createServer((socket) => { + console.log(`Connection from ${socket.remoteAddress}:${socket.remotePort}`); + handleClient(socket); + }); + + server.listen(TCP_PORT, TCP_IP, () => { + console.log(`Listening on ${TCP_IP}:${TCP_PORT}...`); + }); + + server.on('error', (err) => { + console.error('Server error:', err); + cleanup(); + process.exit(1); + }); + } catch (error) { + console.error('Failed to initialize:', error); + process.exit(1); + } +} + +// Start the proxy +if (require.main === module) { + main(); +} + +module.exports = { + main, + cleanup +}; diff --git a/zefie_wtvp_minisrv/package-lock.json b/zefie_wtvp_minisrv/package-lock.json index 05932d73..d99faf52 100644 --- a/zefie_wtvp_minisrv/package-lock.json +++ b/zefie_wtvp_minisrv/package-lock.json @@ -1,14 +1,15 @@ { "name": "zefie_wtvp_minisrv", - "version": "0.9.64-dev", + "version": "0.9.66-dev", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "zefie_wtvp_minisrv", - "version": "0.9.64-dev", + "version": "0.9.66-dev", "license": "GPL3", "dependencies": { + "@serialport/parser-readline": "^13.0.0", "adm-zip": "^0.5.12", "cross-env": "^7.0.3", "crypto-js": "^4.2.0", @@ -28,6 +29,7 @@ "proxy-agent": "^6.4.0", "rc4-crypto": "^1.5.0", "sanitize-html": "^2.13.0", + "serialport": "^13.0.0", "sharp": "^0.34.3", "starttls": "^1.0.1", "strftime": "^0.10.2", @@ -608,6 +610,245 @@ "node": ">= 8" } }, + "node_modules/@serialport/binding-mock": { + "version": "10.2.2", + "resolved": "https://registry.npmjs.org/@serialport/binding-mock/-/binding-mock-10.2.2.tgz", + "integrity": "sha512-HAFzGhk9OuFMpuor7aT5G1ChPgn5qSsklTFOTUX72Rl6p0xwcSVsRtG/xaGp6bxpN7fI9D/S8THLBWbBgS6ldw==", + "license": "MIT", + "dependencies": { + "@serialport/bindings-interface": "^1.2.1", + "debug": "^4.3.3" + }, + "engines": { + "node": ">=12.0.0" + } + }, + "node_modules/@serialport/bindings-cpp": { + "version": "13.0.0", + "resolved": "https://registry.npmjs.org/@serialport/bindings-cpp/-/bindings-cpp-13.0.0.tgz", + "integrity": "sha512-r25o4Bk/vaO1LyUfY/ulR6hCg/aWiN6Wo2ljVlb4Pj5bqWGcSRC4Vse4a9AcapuAu/FeBzHCbKMvRQeCuKjzIQ==", + "hasInstallScript": true, + "license": "MIT", + "dependencies": { + "@serialport/bindings-interface": "1.2.2", + "@serialport/parser-readline": "12.0.0", + "debug": "4.4.0", + "node-addon-api": "8.3.0", + "node-gyp-build": "4.8.4" + }, + "engines": { + "node": ">=18.0.0" + }, + "funding": { + "url": "https://opencollective.com/serialport/donate" + } + }, + "node_modules/@serialport/bindings-cpp/node_modules/@serialport/parser-delimiter": { + "version": "12.0.0", + "resolved": "https://registry.npmjs.org/@serialport/parser-delimiter/-/parser-delimiter-12.0.0.tgz", + "integrity": "sha512-gu26tVt5lQoybhorLTPsH2j2LnX3AOP2x/34+DUSTNaUTzu2fBXw+isVjQJpUBFWu6aeQRZw5bJol5X9Gxjblw==", + "license": "MIT", + "engines": { + "node": ">=12.0.0" + }, + "funding": { + "url": "https://opencollective.com/serialport/donate" + } + }, + "node_modules/@serialport/bindings-cpp/node_modules/@serialport/parser-readline": { + "version": "12.0.0", + "resolved": "https://registry.npmjs.org/@serialport/parser-readline/-/parser-readline-12.0.0.tgz", + "integrity": "sha512-O7cywCWC8PiOMvo/gglEBfAkLjp/SENEML46BXDykfKP5mTPM46XMaX1L0waWU6DXJpBgjaL7+yX6VriVPbN4w==", + "license": "MIT", + "dependencies": { + "@serialport/parser-delimiter": "12.0.0" + }, + "engines": { + "node": ">=12.0.0" + }, + "funding": { + "url": "https://opencollective.com/serialport/donate" + } + }, + "node_modules/@serialport/bindings-cpp/node_modules/debug": { + "version": "4.4.0", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.4.0.tgz", + "integrity": "sha512-6WTZ/IxCY/T6BALoZHaE4ctp9xm+Z5kY/pzYaCHRFeyVhojxlrm+46y68HA6hr0TcwEssoxNiDEUJQjfPZ/RYA==", + "license": "MIT", + "dependencies": { + "ms": "^2.1.3" + }, + "engines": { + "node": ">=6.0" + }, + "peerDependenciesMeta": { + "supports-color": { + "optional": true + } + } + }, + "node_modules/@serialport/bindings-interface": { + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/@serialport/bindings-interface/-/bindings-interface-1.2.2.tgz", + "integrity": "sha512-CJaUd5bLvtM9c5dmO9rPBHPXTa9R2UwpkJ0wdh9JCYcbrPWsKz+ErvR0hBLeo7NPeiFdjFO4sonRljiw4d2XiA==", + "license": "MIT", + "engines": { + "node": "^12.22 || ^14.13 || >=16" + } + }, + "node_modules/@serialport/parser-byte-length": { + "version": "13.0.0", + "resolved": "https://registry.npmjs.org/@serialport/parser-byte-length/-/parser-byte-length-13.0.0.tgz", + "integrity": "sha512-32yvqeTAqJzAEtX5zCrN1Mej56GJ5h/cVFsCDPbF9S1ZSC9FWjOqNAgtByseHfFTSTs/4ZBQZZcZBpolt8sUng==", + "license": "MIT", + "engines": { + "node": ">=20.0.0" + }, + "funding": { + "url": "https://opencollective.com/serialport/donate" + } + }, + "node_modules/@serialport/parser-cctalk": { + "version": "13.0.0", + "resolved": "https://registry.npmjs.org/@serialport/parser-cctalk/-/parser-cctalk-13.0.0.tgz", + "integrity": "sha512-RErAe57g9gvnlieVYGIn1xymb1bzNXb2QtUQd14FpmbQQYlcrmuRnJwKa1BgTCujoCkhtaTtgHlbBWOxm8U2uA==", + "license": "MIT", + "engines": { + "node": ">=20.0.0" + }, + "funding": { + "url": "https://opencollective.com/serialport/donate" + } + }, + "node_modules/@serialport/parser-delimiter": { + "version": "13.0.0", + "resolved": "https://registry.npmjs.org/@serialport/parser-delimiter/-/parser-delimiter-13.0.0.tgz", + "integrity": "sha512-Qqyb0FX1avs3XabQqNaZSivyVbl/yl0jywImp7ePvfZKLwx7jBZjvL+Hawt9wIG6tfq6zbFM24vzCCK7REMUig==", + "license": "MIT", + "engines": { + "node": ">=20.0.0" + }, + "funding": { + "url": "https://opencollective.com/serialport/donate" + } + }, + "node_modules/@serialport/parser-inter-byte-timeout": { + "version": "13.0.0", + "resolved": "https://registry.npmjs.org/@serialport/parser-inter-byte-timeout/-/parser-inter-byte-timeout-13.0.0.tgz", + "integrity": "sha512-a0w0WecTW7bD2YHWrpTz1uyiWA2fDNym0kjmPeNSwZ2XCP+JbirZt31l43m2ey6qXItTYVuQBthm75sPVeHnGA==", + "license": "MIT", + "engines": { + "node": ">=20.0.0" + }, + "funding": { + "url": "https://opencollective.com/serialport/donate" + } + }, + "node_modules/@serialport/parser-packet-length": { + "version": "13.0.0", + "resolved": "https://registry.npmjs.org/@serialport/parser-packet-length/-/parser-packet-length-13.0.0.tgz", + "integrity": "sha512-60ZDDIqYRi0Xs2SPZUo4Jr5LLIjtb+rvzPKMJCohrO6tAqSDponcNpcB1O4W21mKTxYjqInSz+eMrtk0LLfZIg==", + "license": "MIT", + "engines": { + "node": ">=8.6.0" + } + }, + "node_modules/@serialport/parser-readline": { + "version": "13.0.0", + "resolved": "https://registry.npmjs.org/@serialport/parser-readline/-/parser-readline-13.0.0.tgz", + "integrity": "sha512-dov3zYoyf0dt1Sudd1q42VVYQ4WlliF0MYvAMA3MOyiU1IeG4hl0J6buBA2w4gl3DOCC05tGgLDN/3yIL81gsA==", + "license": "MIT", + "dependencies": { + "@serialport/parser-delimiter": "13.0.0" + }, + "engines": { + "node": ">=20.0.0" + }, + "funding": { + "url": "https://opencollective.com/serialport/donate" + } + }, + "node_modules/@serialport/parser-ready": { + "version": "13.0.0", + "resolved": "https://registry.npmjs.org/@serialport/parser-ready/-/parser-ready-13.0.0.tgz", + "integrity": "sha512-JNUQA+y2Rfs4bU+cGYNqOPnNMAcayhhW+XJZihSLQXOHcZsFnOa2F9YtMg9VXRWIcnHldHYtisp62Etjlw24bw==", + "license": "MIT", + "engines": { + "node": ">=20.0.0" + }, + "funding": { + "url": "https://opencollective.com/serialport/donate" + } + }, + "node_modules/@serialport/parser-regex": { + "version": "13.0.0", + "resolved": "https://registry.npmjs.org/@serialport/parser-regex/-/parser-regex-13.0.0.tgz", + "integrity": "sha512-m7HpIf56G5XcuDdA3DB34Z0pJiwxNRakThEHjSa4mG05OnWYv0IG8l2oUyYfuGMowQWaVnQ+8r+brlPxGVH+eA==", + "license": "MIT", + "engines": { + "node": ">=20.0.0" + }, + "funding": { + "url": "https://opencollective.com/serialport/donate" + } + }, + "node_modules/@serialport/parser-slip-encoder": { + "version": "13.0.0", + "resolved": "https://registry.npmjs.org/@serialport/parser-slip-encoder/-/parser-slip-encoder-13.0.0.tgz", + "integrity": "sha512-fUHZEExm6izJ7rg0A1yjXwu4sOzeBkPAjDZPfb+XQoqgtKAk+s+HfICiYn7N2QU9gyaeCO8VKgWwi+b/DowYOg==", + "license": "MIT", + "engines": { + "node": ">=20.0.0" + }, + "funding": { + "url": "https://opencollective.com/serialport/donate" + } + }, + "node_modules/@serialport/parser-spacepacket": { + "version": "13.0.0", + "resolved": "https://registry.npmjs.org/@serialport/parser-spacepacket/-/parser-spacepacket-13.0.0.tgz", + "integrity": "sha512-DoXJ3mFYmyD8X/8931agJvrBPxqTaYDsPoly9/cwQSeh/q4EjQND9ySXBxpWz5WcpyCU4jOuusqCSAPsbB30Eg==", + "license": "MIT", + "engines": { + "node": ">=20.0.0" + }, + "funding": { + "url": "https://opencollective.com/serialport/donate" + } + }, + "node_modules/@serialport/stream": { + "version": "13.0.0", + "resolved": "https://registry.npmjs.org/@serialport/stream/-/stream-13.0.0.tgz", + "integrity": "sha512-F7xLJKsjGo2WuEWMSEO1SimRcOA+WtWICsY13r0ahx8s2SecPQH06338g28OT7cW7uRXI7oEQAk62qh5gHJW3g==", + "license": "MIT", + "dependencies": { + "@serialport/bindings-interface": "1.2.2", + "debug": "4.4.0" + }, + "engines": { + "node": ">=20.0.0" + }, + "funding": { + "url": "https://opencollective.com/serialport/donate" + } + }, + "node_modules/@serialport/stream/node_modules/debug": { + "version": "4.4.0", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.4.0.tgz", + "integrity": "sha512-6WTZ/IxCY/T6BALoZHaE4ctp9xm+Z5kY/pzYaCHRFeyVhojxlrm+46y68HA6hr0TcwEssoxNiDEUJQjfPZ/RYA==", + "license": "MIT", + "dependencies": { + "ms": "^2.1.3" + }, + "engines": { + "node": ">=6.0" + }, + "peerDependenciesMeta": { + "supports-color": { + "optional": true + } + } + }, "node_modules/@tootallnate/quickjs-emscripten": { "version": "0.23.0", "resolved": "https://registry.npmjs.org/@tootallnate/quickjs-emscripten/-/quickjs-emscripten-0.23.0.tgz", @@ -2364,6 +2605,26 @@ "split2": "^4.1.0" } }, + "node_modules/node-addon-api": { + "version": "8.3.0", + "resolved": "https://registry.npmjs.org/node-addon-api/-/node-addon-api-8.3.0.tgz", + "integrity": "sha512-8VOpLHFrOQlAH+qA0ZzuGRlALRA6/LVh8QJldbrC4DY0hXoMP0l4Acq8TzFC018HztWiRqyCEj2aTWY2UvnJUg==", + "license": "MIT", + "engines": { + "node": "^18 || ^20 || >= 21" + } + }, + "node_modules/node-gyp-build": { + "version": "4.8.4", + "resolved": "https://registry.npmjs.org/node-gyp-build/-/node-gyp-build-4.8.4.tgz", + "integrity": "sha512-LA4ZjwlnUblHVgq0oBF3Jl/6h/Nvs5fzBLwdEF4nuxnFdsfajde4WfxtJr3CaiH+F6ewcIB/q4jQ4UzPyid+CQ==", + "license": "MIT", + "bin": { + "node-gyp-build": "bin.js", + "node-gyp-build-optional": "optional.js", + "node-gyp-build-test": "build-test.js" + } + }, "node_modules/nunjucks": { "version": "3.2.4", "resolved": "https://registry.npmjs.org/nunjucks/-/nunjucks-3.2.4.tgz", @@ -2945,6 +3206,51 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/serialport": { + "version": "13.0.0", + "resolved": "https://registry.npmjs.org/serialport/-/serialport-13.0.0.tgz", + "integrity": "sha512-PHpnTd8isMGPfFTZNCzOZp9m4mAJSNWle9Jxu6BPTcWq7YXl5qN7tp8Sgn0h+WIGcD6JFz5QDgixC2s4VW7vzg==", + "license": "MIT", + "dependencies": { + "@serialport/binding-mock": "10.2.2", + "@serialport/bindings-cpp": "13.0.0", + "@serialport/parser-byte-length": "13.0.0", + "@serialport/parser-cctalk": "13.0.0", + "@serialport/parser-delimiter": "13.0.0", + "@serialport/parser-inter-byte-timeout": "13.0.0", + "@serialport/parser-packet-length": "13.0.0", + "@serialport/parser-readline": "13.0.0", + "@serialport/parser-ready": "13.0.0", + "@serialport/parser-regex": "13.0.0", + "@serialport/parser-slip-encoder": "13.0.0", + "@serialport/parser-spacepacket": "13.0.0", + "@serialport/stream": "13.0.0", + "debug": "4.4.0" + }, + "engines": { + "node": ">=20.0.0" + }, + "funding": { + "url": "https://opencollective.com/serialport/donate" + } + }, + "node_modules/serialport/node_modules/debug": { + "version": "4.4.0", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.4.0.tgz", + "integrity": "sha512-6WTZ/IxCY/T6BALoZHaE4ctp9xm+Z5kY/pzYaCHRFeyVhojxlrm+46y68HA6hr0TcwEssoxNiDEUJQjfPZ/RYA==", + "license": "MIT", + "dependencies": { + "ms": "^2.1.3" + }, + "engines": { + "node": ">=6.0" + }, + "peerDependenciesMeta": { + "supports-color": { + "optional": true + } + } + }, "node_modules/serve-static": { "version": "1.16.2", "resolved": "https://registry.npmjs.org/serve-static/-/serve-static-1.16.2.tgz", diff --git a/zefie_wtvp_minisrv/package.json b/zefie_wtvp_minisrv/package.json index 5a95c042..69db2659 100644 --- a/zefie_wtvp_minisrv/package.json +++ b/zefie_wtvp_minisrv/package.json @@ -29,6 +29,7 @@ "url": "https://github.com/zefie/zefie_wtvp_minisrv.git" }, "dependencies": { + "@serialport/parser-readline": "^13.0.0", "adm-zip": "^0.5.12", "cross-env": "^7.0.3", "crypto-js": "^4.2.0", @@ -48,6 +49,7 @@ "proxy-agent": "^6.4.0", "rc4-crypto": "^1.5.0", "sanitize-html": "^2.13.0", + "serialport": "^13.0.0", "sharp": "^0.34.3", "starttls": "^1.0.1", "strftime": "^0.10.2",