UNPKG

enttec-open-dmx-usb

Version:

A Node.js library for interacting with the Enttec Open DMX USB interface

161 lines (160 loc) 6.11 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.EnttecOpenDMXUSBDevice = exports.PRODUCT_ID = exports.VENDOR_ID = void 0; const eventemitter3_1 = require("eventemitter3"); const serialport_1 = require("serialport"); exports.VENDOR_ID = "0403"; // Enttec exports.PRODUCT_ID = "6001"; // Open DMX USB class EnttecOpenDMXUSBDevice extends eventemitter3_1.EventEmitter { /** * @param path A path returned by {@link EnttecOpenDMXUSBDevice.listDevices} or * {@link EnttecOpenDMXUSBDevice.getFirstAvailableDevice}. * @param [startSending=true] Whether the device should start sending as soon as it is ready. * @param [usleep=null] A function blocking the event loop for `n` microseconds. See the README.md for more information. */ constructor(path, startSending = true, usleep = null) { super(); this.shouldBeSending = false; this.sendTimeout = null; this.buffer = Buffer.alloc(513); this.port = new serialport_1.SerialPort({ path, baudRate: 250000, dataBits: 8, stopBits: 2, parity: "none", autoOpen: true }); this.port.on("open", () => { this.emit("ready"); if (startSending) this.startSending(0); }); // Without this, errors would be uncaught. this.port.on("error", (error) => { this.emit("error", error); }); this.usleep = usleep; } /** * Start sending. * @param [interval=0] The milliseconds between each attempt to send. Most of the time `0` works fine. * @throws When the device is not ready yet. */ startSending(interval = 0) { if (!this.port.isOpen) throw new Error("The device is not ready yet. Wait for the 'ready' event."); this.shouldBeSending = true; // eslint-disable-next-line unicorn/consistent-function-scoping const send = () => { this._sendUniverse() .then(() => { if (this.shouldBeSending) // eslint-disable-next-line @typescript-eslint/no-misused-promises this.sendTimeout = setTimeout(send, interval); }) .catch(error => this.emit("error", error)); }; send(); } /** * Stop sending. */ stopSending() { this.shouldBeSending = false; if (this.sendTimeout !== null) clearTimeout(this.sendTimeout); } /** * Set channel values. * If channels is an Object, the keys are the channel numbers. * * @param channels * @param [clear=false] Whether all previously assigned channels should be set to `0` */ setChannels(channels, clear = false) { if (clear) { this.buffer = Buffer.alloc(513); this.buffer[0] = 0; } if (Buffer.isBuffer(channels)) { if (channels.length > 512) throw new Error("The maximum size of an DMX universe is 512 channels."); channels.copy(this.buffer, 1); } else if (Array.isArray(channels)) { if (channels.length > 512) throw new Error("The maximum size of an DMX universe is 512 channels."); channels.forEach((value, index) => { if (value > 0xFF || value < 0) throw new Error("All values must be between 0 and 255."); this.buffer[index + 1] = value; }); } else if (typeof channels === "object") { Object.entries(channels).forEach(([channel, value]) => { let channelNumber; try { channelNumber = Number.parseInt(channel, 10); } catch { throw new Error("Only channel numbers are supported."); } if (channelNumber > 512 || channelNumber < 1) throw new Error("All channel numbers must be between 1 and 512."); else if (value > 0xFF || value < 0) throw new Error("All values must be between 0 and 255."); this.buffer[channelNumber] = value; }); } else throw new TypeError("data must be of type Buffer, Object or Array."); } /** * @returns A Promise resolved when the whole universe was sent. * @private */ async _sendUniverse() { return new Promise(resolve => { this.port.set({ brk: true, rts: false }, () => { if (this.usleep === null) { setTimeout(() => { this.port.set({ brk: false, rts: false }, () => { setTimeout(() => { this.port.write(this.buffer, () => resolve()); }, 1); }); }, 1); } else { this.usleep(92); this.port.set({ brk: false, rts: false }, () => { this.usleep(12); this.port.write(this.buffer, () => resolve()); }); } }); }); } /** * Get the paths of all available devices. */ static async listDevices() { const allPorts = await serialport_1.SerialPort.list(); return allPorts .filter(device => device.vendorId === exports.VENDOR_ID && device.productId === exports.PRODUCT_ID) .map(device => device.path); } /** * Get the path of the first available device. * @throws When no device is found. */ static async getFirstAvailableDevice() { const devices = await EnttecOpenDMXUSBDevice.listDevices(); if (devices.length === 0) throw new Error("No device found."); else return devices[0]; } } exports.EnttecOpenDMXUSBDevice = EnttecOpenDMXUSBDevice;