UNPKG

ns-joycon

Version:

Extract data from connected Jon-Con of Nintendo Switch.

199 lines 8.8 kB
"use strict"; var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) { 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) : new P(function (resolve) { resolve(result.value); }).then(fulfilled, rejected); } step((generator = generator.apply(thisArg, _arguments || [])).next()); }); }; var __importStar = (this && this.__importStar) || function (mod) { if (mod && mod.__esModule) return mod; var result = {}; if (mod != null) for (var k in mod) if (Object.hasOwnProperty.call(mod, k)) result[k] = mod[k]; result["default"] = mod; return result; }; Object.defineProperty(exports, "__esModule", { value: true }); // Node modules. const node_hid_1 = require("node-hid"); const PacketParser = __importStar(require("./utils/packet-parser")); const SubcommandSender = __importStar(require("./utils/subcommand-sender")); function getType(product) { if (product === undefined) { return 'unknown'; } switch (true) { case /Pro Controller/i.test(product): return 'pro-controller'; case /Joy-Con \([LR]\)/i.test(product): return 'joy-con'; default: return 'unknown'; } } class NsSwitchHID { constructor(device) { this.listeners = []; this.vendorId = device.vendorId; this.productId = device.productId; this.serialNumber = device.serialNumber; this.product = device.product; this.type = getType(device.product); this.path = device.path; this.usage = device.usage; this.hid = new node_hid_1.HID(device.vendorId, device.productId); // System handler. if (this.type === 'joy-con') { this.activateJoyConStream(); } } get meta() { return { vendorId: this.vendorId, productId: this.productId, serialNumber: this.serialNumber, product: this.product, type: this.type, path: this.path, usage: this.usage, }; } /** * Add / remove a handler to recevice packets when device send streaming data. */ manageHandler(action, callback) { if (action === 'add') { this.listeners.push(callback); } else { this.listeners = this.listeners.filter((listener) => listener !== callback); } } /** * Request device info to Jon-Con. */ requestDeviceInfo() { return __awaiter(this, void 0, void 0, function* () { if (this.type === 'joy-con') { const manageHandler = this.manageHandler.bind(this); const deviceInfo = yield SubcommandSender.requestDeviceInfo(this.hid, manageHandler); return deviceInfo; } }); } /** * Enable IMU data will make Jon-Con sends **Input Report 0x30**. */ enableIMU() { return __awaiter(this, void 0, void 0, function* () { if (this.type === 'joy-con') { yield SubcommandSender.enableIMU(this.hid, this.manageHandler.bind(this), true); yield SubcommandSender.setInputReportMode(this.hid, this.manageHandler.bind(this), 'standard-full-mode'); console.info(`Device ${this.product} (${this.serialNumber}) enabled IMU.`); } }); } /** * Disable IMU data will cancel Jon-Con to send **Input Report 0x30**. */ disableIMU() { return __awaiter(this, void 0, void 0, function* () { if (this.type === 'joy-con') { yield SubcommandSender.enableIMU(this.hid, this.manageHandler.bind(this), false); yield SubcommandSender.setInputReportMode(this.hid, this.manageHandler.bind(this), 'simple-hid-mode'); console.info(`Device ${this.product} (${this.serialNumber}) disabled IMU.`); } }); } /** * Enable Jon-Con's vibration. */ enableVibration() { return __awaiter(this, void 0, void 0, function* () { if (this.type === 'joy-con') { yield SubcommandSender.enableVibration(this.hid, this.manageHandler.bind(this), true); console.info(`Device ${this.product} (${this.serialNumber}) enabled vibration.`); } }); } /** * Disable Jon-Con's vibration. */ disableVibration() { return __awaiter(this, void 0, void 0, function* () { if (this.type === 'joy-con') { yield SubcommandSender.enableVibration(this.hid, this.manageHandler.bind(this), false); console.info(`Device ${this.product} (${this.serialNumber}) disabled vibration.`); } }); } activateJoyConStream() { return __awaiter(this, void 0, void 0, function* () { this.hid.on('data', (rawData) => { const data = rawData.toString('hex').match(/.{2}/g); if (!data) { return; } const inputReportID = parseInt(data[0], 16); let packet = { inputReportID: PacketParser.parseInputReportID(rawData, data), }; switch (inputReportID) { case 0x3f: { packet = Object.assign({}, packet, { buttonStatus: PacketParser.parseButtonStatus(rawData, data), analogStick: PacketParser.parseAnalogStick(rawData, data), filter: PacketParser.parseFilter(rawData, data) }); break; } case 0x21: case 0x30: { packet = Object.assign({}, packet, { timer: PacketParser.parseTimer(rawData, data), batteryLevel: PacketParser.parseBatteryLevel(rawData, data), connectionInfo: PacketParser.parseConnectionInfo(rawData, data), buttonStatus: PacketParser.parseCompleteButtonStatus(rawData, data), analogStickLeft: PacketParser.parseAnalogStickLeft(rawData, data), analogStickRight: PacketParser.parseAnalogStickRight(rawData, data), vibrator: PacketParser.parseVibrator(rawData, data) }); if (inputReportID === 0x21) { packet = Object.assign({}, packet, { ack: PacketParser.parseAck(rawData, data), subcommandID: PacketParser.parseSubcommandID(rawData, data), subcommandReplyData: PacketParser.parseSubcommandReplyData(rawData, data) }); } if (inputReportID === 0x30) { const accelerometers = PacketParser.parseAccelerometers(rawData, data); const gyroscopes = PacketParser.parseGyroscopes(rawData, data); packet = Object.assign({}, packet, { accelerometers, gyroscopes, actualAccelerometer: { acc: PacketParser.calculateActualAccelerometer(accelerometers.map(a => [a.x.acc, a.y.acc, a.z.acc])), }, actualGyroscope: { dps: PacketParser.calculateActualGyroscope(gyroscopes.map(g => g.map(v => v.dps))), rps: PacketParser.calculateActualGyroscope(gyroscopes.map(g => g.map(v => v.rps))), } }); } break; } } // Broadcast. this.listeners.forEach((listener) => listener(packet)); }); this.hid.on('error', (error) => { console.warn(Object.assign({}, this.meta, { error })); }); }); } } function findControllers(callback) { let deviceList = new Set(); const work = () => { const tempDeviceList = new Set(); const devices = node_hid_1.devices().reduce((prev, d) => { if (getType(d.product) !== 'unknown') { prev.push(new NsSwitchHID(d)); } return prev; }, []); devices.forEach((d) => { const distinctId = `${d.meta.vendorId},${d.meta.productId},${d.meta.type}`; tempDeviceList.add(distinctId); if (!deviceList.has(distinctId)) { callback(devices); } }); deviceList = tempDeviceList; }; work(); setInterval(work, 1000); } exports.findControllers = findControllers; //# sourceMappingURL=index.js.map