UNPKG

6.11 kBJavaScriptView Raw
1"use strict";
2Object.defineProperty(exports, "__esModule", { value: true });
3exports.EnttecOpenDMXUSBDevice = exports.PRODUCT_ID = exports.VENDOR_ID = void 0;
4const eventemitter3_1 = require("eventemitter3");
5const serialport_1 = require("serialport");
6exports.VENDOR_ID = "0403"; // Enttec
7exports.PRODUCT_ID = "6001"; // Open DMX USB
8class EnttecOpenDMXUSBDevice extends eventemitter3_1.EventEmitter {
9 /**
10 * @param path A path returned by {@link EnttecOpenDMXUSBDevice.listDevices} or
11 * {@link EnttecOpenDMXUSBDevice.getFirstAvailableDevice}.
12 * @param [startSending=true] Whether the device should start sending as soon as it is ready.
13 * @param [usleep=null] A function blocking the event loop for `n` microseconds. See the README.md for more information.
14 */
15 constructor(path, startSending = true, usleep = null) {
16 super();
17 this.shouldBeSending = false;
18 this.sendTimeout = null;
19 this.buffer = Buffer.alloc(513);
20 this.port = new serialport_1.SerialPort({
21 path,
22 baudRate: 250000,
23 dataBits: 8,
24 stopBits: 2,
25 parity: "none",
26 autoOpen: true
27 });
28 this.port.on("open", () => {
29 this.emit("ready");
30 if (startSending)
31 this.startSending(0);
32 });
33 // Without this, errors would be uncaught.
34 this.port.on("error", (error) => {
35 this.emit("error", error);
36 });
37 this.usleep = usleep;
38 }
39 /**
40 * Start sending.
41 * @param [interval=0] The milliseconds between each attempt to send. Most of the time `0` works fine.
42 * @throws When the device is not ready yet.
43 */
44 startSending(interval = 0) {
45 if (!this.port.isOpen)
46 throw new Error("The device is not ready yet. Wait for the 'ready' event.");
47 this.shouldBeSending = true;
48 // eslint-disable-next-line unicorn/consistent-function-scoping
49 const send = () => {
50 this._sendUniverse()
51 .then(() => {
52 if (this.shouldBeSending)
53 // eslint-disable-next-line @typescript-eslint/no-misused-promises
54 this.sendTimeout = setTimeout(send, interval);
55 })
56 .catch(error => this.emit("error", error));
57 };
58 send();
59 }
60 /**
61 * Stop sending.
62 */
63 stopSending() {
64 this.shouldBeSending = false;
65 if (this.sendTimeout !== null)
66 clearTimeout(this.sendTimeout);
67 }
68 /**
69 * Set channel values.
70 * If channels is an Object, the keys are the channel numbers.
71 *
72 * @param channels
73 * @param [clear=false] Whether all previously assigned channels should be set to `0`
74 */
75 setChannels(channels, clear = false) {
76 if (clear) {
77 this.buffer = Buffer.alloc(513);
78 this.buffer[0] = 0;
79 }
80 if (Buffer.isBuffer(channels)) {
81 if (channels.length > 512)
82 throw new Error("The maximum size of an DMX universe is 512 channels.");
83 channels.copy(this.buffer, 1);
84 }
85 else if (Array.isArray(channels)) {
86 if (channels.length > 512)
87 throw new Error("The maximum size of an DMX universe is 512 channels.");
88 channels.forEach((value, index) => {
89 if (value > 0xFF || value < 0)
90 throw new Error("All values must be between 0 and 255.");
91 this.buffer[index + 1] = value;
92 });
93 }
94 else if (typeof channels === "object") {
95 Object.entries(channels).forEach(([channel, value]) => {
96 let channelNumber;
97 try {
98 channelNumber = Number.parseInt(channel, 10);
99 }
100 catch {
101 throw new Error("Only channel numbers are supported.");
102 }
103 if (channelNumber > 512 || channelNumber < 1)
104 throw new Error("All channel numbers must be between 1 and 512.");
105 else if (value > 0xFF || value < 0)
106 throw new Error("All values must be between 0 and 255.");
107 this.buffer[channelNumber] = value;
108 });
109 }
110 else
111 throw new TypeError("data must be of type Buffer, Object or Array.");
112 }
113 /**
114 * @returns A Promise resolved when the whole universe was sent.
115 * @private
116 */
117 async _sendUniverse() {
118 return new Promise(resolve => {
119 this.port.set({ brk: true, rts: false }, () => {
120 if (this.usleep === null) {
121 setTimeout(() => {
122 this.port.set({ brk: false, rts: false }, () => {
123 setTimeout(() => {
124 this.port.write(this.buffer, () => resolve());
125 }, 1);
126 });
127 }, 1);
128 }
129 else {
130 this.usleep(92);
131 this.port.set({ brk: false, rts: false }, () => {
132 this.usleep(12);
133 this.port.write(this.buffer, () => resolve());
134 });
135 }
136 });
137 });
138 }
139 /**
140 * Get the paths of all available devices.
141 */
142 static async listDevices() {
143 const allPorts = await serialport_1.SerialPort.list();
144 return allPorts
145 .filter(device => device.vendorId === exports.VENDOR_ID && device.productId === exports.PRODUCT_ID)
146 .map(device => device.path);
147 }
148 /**
149 * Get the path of the first available device.
150 * @throws When no device is found.
151 */
152 static async getFirstAvailableDevice() {
153 const devices = await EnttecOpenDMXUSBDevice.listDevices();
154 if (devices.length === 0)
155 throw new Error("No device found.");
156 else
157 return devices[0];
158 }
159}
160exports.EnttecOpenDMXUSBDevice = EnttecOpenDMXUSBDevice;