UNPKG

@twilio/plugin-microvisor

Version:

Interact with your Twilio Microvisor devices

192 lines (191 loc) 7.65 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.processResponse = exports.checkUserPermissions = exports.crc16 = exports.didTimeout = exports.getDataFromDevice = void 0; const serialport_1 = require("serialport"); const proto_1 = require("../../../proto_gen/proto"); const { execSync } = require('child_process'); const { DelimiterParser } = require('@serialport/parser-delimiter'); const EOL = (process.platform == "linux" || process.platform == "darwin") ? "\n" : "\r\n"; function getDataFromDevice(passThis) { // Check user perms on Ubuntu/Linux if (!checkUserPermissions()) { passThis.logger.error(`${passThis.mainError}. Please re-run as root or as a member of dialout.${EOL}`); process.exit(1); } // Open the serial port const port = new serialport_1.SerialPort({ path: passThis.args.devicePath, baudRate: 115200 }); port.on("open", function () { // Clear the pipes port.flush(); // Assemble the command packet const packet = makeDataLessPacket(passThis.requestType); // Prepare a listener for lines ending in \n const parser = port.pipe(new DelimiterParser({ delimiter: '\r\n' })); parser.on('data', function (rxData) { if (processResponse(passThis, rxData)) { clearTimeout(timer); port.close(); } }); // Set a timeout value const timer = setTimeout(didTimeout, 5000, passThis, port); // Send the command to the device via the serial port port.write(packet, "utf8", function (err) { if (err instanceof Error) { clearTimeout(timer); port.close(); passThis.logger.error(`${passThis.mainError} (${err.message})\n`); } }); }); port.on("error", function (err) { passThis.logger.error(`${passThis.mainError} (${err.message})\n`); }); } exports.getDataFromDevice = getDataFromDevice; function didTimeout(passThis, port) { port.close(); passThis.logger.error(`${passThis.mainError} (Error: operation timed out).${EOL}`); } exports.didTimeout = didTimeout; function crc16(data) { var crc16 = 0xFFFF; let poly_reversed = 0xA001; for (const byte of data) { crc16 ^= byte; for (var j = 0; j < 8; j++) { let carry = crc16 & 0x0001; crc16 = crc16 >> 1; if (carry) crc16 ^= poly_reversed; } } return (crc16 & 0xFFFF); } exports.crc16 = crc16; function checkUserPermissions() { if (process.platform == "linux") { if (process.getuid() != 0) { let username = process.env.USER || ""; return String(execSync(`groups ${username}`)).includes("dialout"); } } return true; } exports.checkUserPermissions = checkUserPermissions; function makeDataLessPacket(cmdType) { // Build the binary command const msg = new Uint8Array(3); msg[0] = cmdType; const b64Data = Buffer.from(msg).toString('base64'); const utf8Encoder = new TextEncoder(); const payload = utf8Encoder.encode("CMD:" + b64Data); // Assemble packet const header = new Uint8Array([0x0D, 0x0A]); let outro = new Uint8Array([0x3A, 0x00, 0x00, 0x00, 0x00, 0x0D, 0x0A]); // Insert CRC16 const crcn = crc16(payload); const crcs = crcn.toString(16).padStart(4, '0'); const crc_data = utf8Encoder.encode(crcs); outro.set(crc_data, 1); const packet = new Uint8Array(header.length + payload.length + outro.length); packet.set(header); packet.set(payload, header.length); packet.set(outro, header.length + payload.length); return packet; } function processResponse(passThis, data) { const rx = data.toString(); if (rx.startsWith("OK:") || rx.startsWith("ERROR:")) { const parts = rx.split(":"); if (parts.length != 3) return true; if (rx.startsWith("OK:")) { if (!checkRespCRC(passThis, data) || !decodeMessage(passThis, parts[1])) { passThis.logger.error(`Could not parse response from device.${EOL}`); } } else { passThis.logger.info(`${passThis.mainError} (error: ${+parts[1]})`); } return true; } // No valid response yet return false; } exports.processResponse = processResponse; function decodeMessage(passThis, data) { const protoMsgBuf = checkResponse(data, passThis.requestType); if (typeof protoMsgBuf == "number") return false; switch (passThis.requestType) { case proto_1.provision.ProvisioningCommands_MessageTypes_F2D.TYPE_F2D_GET_VERSION_REQUEST: { const protoMsg = proto_1.provision.GetVersionResponse.decode(protoMsgBuf); const version = new TextDecoder().decode(protoMsg.version); const parts = version.split(' - '); const version_parts = parts[0].split('+'); const msg = parts.length > 1 ? `Microvisor version: ${version_parts[0]}` : "Microvisor version: unknown"; passThis.logger.info(msg); if (parts.length > 1) passThis.logger.info(`Build date: ${parts[1]}`); } return true; ; case proto_1.provision.ProvisioningCommands_MessageTypes_F2D.TYPE_F2D_GET_WIFI_CONFIGURATION_REQUEST: { const protoMsg = proto_1.provision.GetWifiConfigurationResponse.decode(protoMsgBuf); const ssid = new TextDecoder().decode(protoMsg.ssid); const pwd = protoMsg.hasPassword ? "<set>" : "<not set>"; if (ssid == "" && pwd == "<not set>") { passThis.logger.info("WiFi credentials not set"); return true; } if (!protoMsg.success) return false; passThis.logger.info(`SSID: ${ssid}${EOL}Password: ${pwd}`); } return true; case proto_1.provision.ProvisioningCommands_MessageTypes_F2D.TYPE_F2D_SET_WIFI_CONFIGURATION_REQUEST: { const protoMsg = proto_1.provision.SetWifiConfigurationResponse.decode(protoMsgBuf); passThis.logger.info(protoMsg.success ? "WiFi configuration set" : "WiFi configuration not set"); } return true; case proto_1.provision.ProvisioningCommands_MessageTypes_F2D.TYPE_F2D_GET_SID_REQUEST: { const protoMsg = proto_1.provision.GetDeviceSidResponse.decode(protoMsgBuf); const sid = new TextDecoder().decode(protoMsg.sid); passThis.logger.info(`Device SID: ${sid}`); } return true; default: break; } return false; } function checkRespCRC(passThis, data) { var pcrc = 1; var ccrc = 0; for (var i = data.length - 1; i >= 0; i--) { if (data[i] == 0x3A) { pcrc = crc16(data.slice(0, i)); const ccrcs = data.slice(i + 1, i + 5).toString(); ccrc = Number("0x" + ccrcs); break; } } return (ccrc == pcrc); } function checkResponse(data, cmdType) { let buffer = Buffer.from(data, 'base64'); let len = (buffer[1] << 8) | buffer[2]; if (buffer[0] != cmdType) return -1; if (len + 3 != buffer.length) return -1; let protoMsgBuf = new Uint8Array(buffer.length - 3); for (var i = 3; i < buffer.length; i++) protoMsgBuf[i - 3] = buffer[i]; return protoMsgBuf; }