UNPKG

homebridge-smartsystem

Version:

SmartServer (Proxy Websockets to TCP sockets, Smappee MQTT, Duotecno IP Nodes, Homekit interface)

178 lines 7.6 kB
"use strict"; var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) { function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); } return new (P || (P = Promise))(function (resolve, reject) { function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } } function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } } function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); } step((generator = generator.apply(thisArg, _arguments || [])).next()); }); }; Object.defineProperty(exports, "__esModule", { value: true }); exports.SocApp = void 0; const fs = require("fs"); const mime = require("mime-types"); const net = require("net"); const ws_1 = require("ws"); const logger_1 = require("../duotecno/logger"); const support_1 = require("./support"); const webapp_1 = require("./webapp"); //////////////// // Web server // //////////////// class SocApp extends webapp_1.WebApp { constructor(system, type, port) { super(type); // hashmap with all connected clients this.clients = {}; if (this.config.debug) logger_1.logSettings["socapp"] = logger_1.LogLevel.debug; this.port = port || this.config.port || this.port || 80; this.system = system; this.support = new support_1.Support(system); } needsLogin(context) { return false; } doRequest(context) { return __awaiter(this, void 0, void 0, function* () { if (context.request === "") { return this.serveFile(context, "/index.html"); } else if (context.request === "log") { return this.renderLog(); } else if (context.request === "restart") { return this.doRestart(context.jsonrequest); } else if (context.request === "reboot") { return this.doReboot(context, context.jsonrequest); } else if (context.req.url === "/apple-touch-icon-precomposed.png") { return this.serveFile(context, "/assets/imgs/apple-touch-icon-precomposed.png"); } else { try { return this.serveFile(context, context.path); } catch (error) { (0, logger_1.err)("socapp", "Error serving URL: " + context.req.url); return this.serveFile(context, "/index.html"); // return this.notFound(context.req.url); } } }); } serve() { (0, logger_1.log)("socapp", "SocApp - Start http server on port " + this.port); super.serve(() => { const wsServer = new ws_1.Server({ server: this.server }); wsServer.on('connection', this.handleClient.bind(this)); }); } serveFile(context, fn) { var _a, _b; const data = fs.readFileSync("www" + fn); const type = mime.lookup(fn) || "application/text"; (0, logger_1.log)("socapp", "Serve - " + (((_b = (_a = context.req) === null || _a === void 0 ? void 0 : _a.socket) === null || _b === void 0 ? void 0 : _b.remoteAddress) || "no-remote") + ": " + fn + " - " + type); return { status: 200, data, type }; } renderLog() { const clientKeys = Object.keys(this.clients); const seen = clientKeys.length; const connected = clientKeys.reduce((acc, k) => acc + (this.clients[k].connected ? 1 : 0), 0); const cList = clientKeys.filter(k => this.clients[k].connected).map(k => `<li>${k}: ${this.clients[k].lastseen}</li>`).join(""); const sList = clientKeys.filter(k => !this.clients[k].connected).map(k => `<li>${k}: ${this.clients[k].lastseen}</li>`).join(""); return { status: 200, type: "text/html", data: ` <html><header><title>Akiworks SmartServer status</title></header> <body> <h1>Connections: ${seen}</h1> <h2>Open: ${connected}</h2> <ul>${cList}</ul> <h2>closed: ${seen - connected}</h2> <ul>${sList}</ul> </body> <footer><hr /><small>© Duotechno & Johan Coppieters</small></footer></html> ` }; } // Handle new incomming WebSocket client handleClient(client, req) { let target; const clientAddr = client._socket.remoteAddress; const logger = (msg) => (0, logger_1.log)('socapp', clientAddr + ': ' + msg); const info = (this.config.debug) ? (msg) => logger('socapp - ' + clientAddr + ': ' + msg) : (msg) => null; ///////////////////////////////////// // url parsing to find ip and port // ///////////////////////////////////// const url = (req ? req.url : client.upgradeReq.url).substr(1); const parts = url.split(':'); if (parts.length != 2) { logger("invalid target address: " + url); return; } const ipAddress = parts[0]; const portNr = parts[1]; logger("websocket proxy to ip: " + ipAddress + ", port: " + portNr); ///////////////////////////////// // Target connection handling // ///////////////////////////////// target = net.createConnection(portNr, ipAddress, () => { target.setNoDelay(); // disable Nagle logger("connected to target -> " + url); this.clients[url] = { connected: true, lastseen: new Date() }; }); target.on('data', (data) => { const msg = data.toString(); info("received from target: " + msg.substr(0, msg.length - 1)); try { client.send(msg); this.clients[url].lastseen = new Date(); } catch (e) { logger("client sending error: " + e.message + ", cleaning up target"); target.end(); } }); target.on('end', () => { logger('target disconnected'); client.close(); this.clients[url].connected = false; }); target.on('error', (err) => { logger('target error' + err); target.end(); client.close(); if (this.clients[url]) this.clients[url].connected = false; }); /////////////////////////////////// // websocket connection handling // /////////////////////////////////// client.on('message', (msg) => { // log('got message from websocket: ' + msg.substr(0, msg.length-1)); // Let's hope the socket is buffering even before the connection is fully open // if (! connected) log('Not yet connected!!') else const result = this.support.handle(msg); if (result.done) { if (result.answer) client.send(result.answer); } else { info('received from client: ' + msg.substr(0, msg.length - 1)); target.write(msg); } }); client.on('close', (code, reason) => { logger('websocket disconnected: ' + code + ' [' + reason + ']'); target.end(); }); client.on('error', (err) => { logger('websocket error: ' + err); target.end(); }); } } exports.SocApp = SocApp; //# sourceMappingURL=socapp.js.map