const path = require("node:path"); const fs = require("node:fs"); const http = require("node:http"); const ws = require("ws"); let driver; let mappings; const webServer = new http.Server(); const socketServer = new ws.WebSocketServer({ "noServer": true }); function sendToClients(message) { socketServer.clients.forEach(function (client) { if (client.readyState === ws.OPEN) { client.send(message); } }); } webServer.on("request", function (request, response) { switch (request.method) { case "GET": switch (request.url) { default: var filePath = path.join(__dirname, "view", request.url); if (!fs.existsSync(filePath)) { response.writeHead(404); return response.end("No entry. "); } if (fs.statSync(filePath).isDirectory()) { var entryPath = path.join(filePath, "index.html"); if (!fs.existsSync(entryPath)) { response.writeHead(404); return response.end("No entry. "); } response.writeHead(200); fs.createReadStream(entryPath).pipe(response); } else { response.writeHead(200); fs.createReadStream(filePath).pipe(response); } break; } break; default: response.writeHead(404); return response.end("No entry. "); break; } }); webServer.on("upgrade", function (request, socket, head) { if (request.url === "/socket") { socketServer.handleUpgrade(request, socket, head, function (ws) { socketServer.emit("connection", ws, request); }); } else { socket.destroy(); } }); socketServer.on("connection", function (socket) { socket.on("error", console.error); socket.on("message", function (event) { var data = JSON.parse(event); switch (data.event) { case "load-driver": if (driver) { driver.close(); delete driver; } var driverPath = path.join(__dirname, "drivers", path.basename(data.data.name) + ".js"); driver = require(driverPath); break; case "load-mappings": if (mappings) { delete mappings; } var mappingsPath = path.join(__dirname, "mappings", path.basename(data.data.name) + ".json"); mappings = JSON.parse(fs.readFileSync(mappingsPath)); break; case "list-devices": socket.send(JSON.stringify({ "event": "device-list", "data": { "devices": driver.list() } })); break; case "open-device": driver.open(data.data.name, function (message) { var byteArray = new Array(...message); var hexStringifiedMessage = byteArray.map(b => b.toString(16)).map(c => ("00".substring(c.length) + c)).join(" "); for (var mappingRegExpString in mappings.inbound) { if (mappings.inbound.hasOwnProperty(mappingRegExpString)) { var mappingRegExp = new RegExp(mappingRegExpString); var mappingMatches = hexStringifiedMessage.match(mappingRegExp); if (!mappingMatches) continue; var mappingName = mappings.inbound[mappingRegExpString]; mappingMatches.shift(); var value = parseInt(mappingMatches[0], 16); sendToClients(JSON.stringify({ "event": "signal", "data": { "name": mappingName, "value": value } })); } } }); break; case "control": if (!driver) return; if (!mappings.outbound[data.data.name]) return; var message = mappings.outbound[data.data.name].map(function (byte) { if (typeof byte != "number") { return data.data.value; } else { return byte; } }); driver.send(message); break; case "close-device": driver.close(); break; default: } }); }); webServer.listen(23272);