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
JavaScript
;
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;