├── .eslintrc.json ├── .gitignore ├── .nvmrc ├── LICENSE ├── README.md ├── package.json ├── pnpm-lock.yaml ├── src └── index.ts └── tsconfig.json /.eslintrc.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": ["awzzm-ts", "awzzm-node"] 3 | } 4 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | node_modules/ 2 | dist/ 3 | .idea/ 4 | /test.mjs -------------------------------------------------------------------------------- /.nvmrc: -------------------------------------------------------------------------------- 1 | 18 -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2019-2021 Moritz Ruth 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # node-enttec-open-dmx-usb 🔌 2 | > A Node.js library for interacting with the 3 | > [Enttec Open DMX USB interface](https://www.enttec.co.uk/en/product/controls/dmx-usb-interfaces/open-dmx-usb/) 4 | 5 | As it uses `serialport` under the hood, it should also work in 6 | [these environments](https://serialport.io/docs/guide-platform-support#supported-platforms-and-architectures). 7 | 8 | ## Install 9 | ![npm](https://img.shields.io/npm/v/enttec-open-dmx-usb?style=flat-square) 10 | 11 | The minimum required Node.js version is `v18.0.0`. 12 | 13 | ```sh 14 | yarn add enttec-open-dmx-usb 15 | # or 16 | npm install enttec-open-dmx-usb 17 | ``` 18 | 19 | ## Usage 20 | [**View documentation on jsdocs.io**](https://www.jsdocs.io/package/enttec-open-dmx-usb#EnttecOpenDMXUSBDevice) 21 | 22 | ```js 23 | import { EnttecOpenDMXUSBDevice as DMXDevice } from "enttec-open-dmx-usb" 24 | 25 | (async () => { 26 | const device = new DMXDevice(await DMXDevice.getFirstAvailableDevice()) 27 | 28 | device.setChannels({ 29 | 1: 0xFF, 30 | 2: 0x44 31 | }) 32 | 33 | // same as 34 | device.setChannels([0xFF, 0x44]) 35 | 36 | // same as 37 | device.setChannels(Buffer.from([0xFF, 0x44])) 38 | })() 39 | ``` 40 | 41 | ### What to do when this doesn’t work 42 | 43 | TLDR: Because `setTimeout` is imprecise, install [`easy-sleep`](https://github.com/qufei1993/easy-sleep) and 44 | pass the `usleep` function provided by it as the third parameter to the constructor of `EnttecOpenDMXUSBDevice`. 45 | See the example below. 46 | 47 | From [the Node.js documentation](https://nodejs.org/api/timers.html#settimeoutcallback-delay-args) regarding `setTimeout`: 48 | > Node.js makes no guarantees about the exact timing of when callbacks will fire, nor of their ordering. 49 | > The callback will be called as close as possible to the time specified. 50 | 51 | Because of this and the passive nature of the Enttec Open DMX USB interface, sometimes the timing requirements of the DMX specification are not met. 52 | Using a library such as [`easy-sleep`](https://github.com/qufei1993/easy-sleep) which allows sleeping (i. e. blocking the event loop) for a precise 53 | amount of microseconds is a possible workaround. 54 | 55 | You may pass a function sleeping for `n` *micro*seconds as the third parameter to the constructor of `EnttecOpenDMXUSBDevice`. 56 | 57 | For example, using `easy-sleep`: 58 | ```js 59 | import { EnttecOpenDMXUSBDevice as DMXDevice } from "enttec-open-dmx-usb" 60 | import easySleep from "easy-sleep" 61 | 62 | new DMXDevice(await DMXDevice.getFirstAvailableDevice(), true, easySleep.Thread.usleep) 63 | ``` 64 | 65 | ## Events 66 | `ready` - `startSending` may be called. 67 | 68 | `error` - An error occurred. `error` events from `serialport` are passed through. 69 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "enttec-open-dmx-usb", 3 | "version": "4.0.1", 4 | "description": "A Node.js library for interacting with the Enttec Open DMX USB interface", 5 | "repository": "https://github.com/moritzruth/node-enttec-open-dmx-usb.git", 6 | "author": "Moritz Ruth ", 7 | "license": "MIT", 8 | "main": "dist/index.js", 9 | "types": "dist/index.d.ts", 10 | "keywords": [ 11 | "dmx", 12 | "enttec", 13 | "usb" 14 | ], 15 | "scripts": { 16 | "build": "tsc" 17 | }, 18 | "files": [ 19 | "dist" 20 | ], 21 | "engines": { 22 | "node": ">=18.0.0" 23 | }, 24 | "devDependencies": { 25 | "@types/node": "^18.16.10", 26 | "@types/serialport": "^8.0.2", 27 | "typescript": "^5.0.4" 28 | }, 29 | "dependencies": { 30 | "eventemitter3": "^5.0.1", 31 | "serialport": "^11.0.0" 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /pnpm-lock.yaml: -------------------------------------------------------------------------------- 1 | lockfileVersion: '6.0' 2 | 3 | dependencies: 4 | easy-sleep: 5 | specifier: ^1.2.2 6 | version: 1.2.2 7 | eventemitter3: 8 | specifier: ^5.0.1 9 | version: 5.0.1 10 | serialport: 11 | specifier: ^11.0.0 12 | version: 11.0.0 13 | 14 | devDependencies: 15 | '@types/node': 16 | specifier: ^18.16.10 17 | version: 18.16.10 18 | '@types/serialport': 19 | specifier: ^8.0.2 20 | version: 8.0.2 21 | typescript: 22 | specifier: ^5.0.4 23 | version: 5.0.4 24 | 25 | packages: 26 | 27 | /@serialport/binding-mock@10.2.2: 28 | resolution: {integrity: sha512-HAFzGhk9OuFMpuor7aT5G1ChPgn5qSsklTFOTUX72Rl6p0xwcSVsRtG/xaGp6bxpN7fI9D/S8THLBWbBgS6ldw==} 29 | engines: {node: '>=12.0.0'} 30 | dependencies: 31 | '@serialport/bindings-interface': 1.2.2 32 | debug: 4.3.4 33 | transitivePeerDependencies: 34 | - supports-color 35 | dev: false 36 | 37 | /@serialport/bindings-cpp@11.0.1: 38 | resolution: {integrity: sha512-3I1mniVg3osYuIUXxU0jB5AHPsxWmErmc3JC3WfUSlfXsjWMHkHfFzbW9Scuv/z/6DLCJIDyltabRa2FoW2qsQ==} 39 | engines: {node: '>=14.0.0'} 40 | requiresBuild: true 41 | dependencies: 42 | '@serialport/bindings-interface': 1.2.2 43 | '@serialport/parser-readline': 10.5.0 44 | debug: 4.3.4 45 | node-addon-api: 6.1.0 46 | node-gyp-build: 4.6.0 47 | transitivePeerDependencies: 48 | - supports-color 49 | dev: false 50 | 51 | /@serialport/bindings-interface@1.2.2: 52 | resolution: {integrity: sha512-CJaUd5bLvtM9c5dmO9rPBHPXTa9R2UwpkJ0wdh9JCYcbrPWsKz+ErvR0hBLeo7NPeiFdjFO4sonRljiw4d2XiA==} 53 | engines: {node: ^12.22 || ^14.13 || >=16} 54 | dev: false 55 | 56 | /@serialport/parser-byte-length@11.0.0: 57 | resolution: {integrity: sha512-rExsdFKdzOIHOBqTwzxUF1A9nrluVIZKZOtvMq5i0Hc3euooGdmkx0VXYNRlI2rd6kJLTL2P+uIR+ZtCTRyT+w==} 58 | engines: {node: '>=12.0.0'} 59 | dev: false 60 | 61 | /@serialport/parser-cctalk@11.0.0: 62 | resolution: {integrity: sha512-eN1MvEIFwI4GedWJhte6eWF+NZtrjchZbMf0CE6NV9TRzJI1KLnFf90ZOj/mhGuANojX4sqWfJKQXwN6E8VSHQ==} 63 | engines: {node: '>=12.0.0'} 64 | dev: false 65 | 66 | /@serialport/parser-delimiter@10.5.0: 67 | resolution: {integrity: sha512-/uR/yT3jmrcwnl2FJU/2ySvwgo5+XpksDUR4NF/nwTS5i3CcuKS+FKi/tLzy1k8F+rCx5JzpiK+koqPqOUWArA==} 68 | engines: {node: '>=12.0.0'} 69 | dev: false 70 | 71 | /@serialport/parser-delimiter@11.0.0: 72 | resolution: {integrity: sha512-aZLJhlRTjSmEwllLG7S4J8s8ctRAS0cbvCpO87smLvl3e4BgzbVgF6Z6zaJd3Aji2uSiYgfedCdNc4L6W+1E2g==} 73 | engines: {node: '>=12.0.0'} 74 | dev: false 75 | 76 | /@serialport/parser-inter-byte-timeout@11.0.0: 77 | resolution: {integrity: sha512-RLgqZC50IET6FtEIt6Oi0vdRsesSBWLNwB7ldzR9OzyXKgK0XHRzqKqbB0u5Q+tC5OScdWeiQ2AO6jooKUZtsw==} 78 | engines: {node: '>=12.0.0'} 79 | dev: false 80 | 81 | /@serialport/parser-packet-length@11.0.0: 82 | resolution: {integrity: sha512-6ZkOiaCooabpV/EM7ttSRbisbDWpGEf7Yxyr13t28LicYR43THRdjdMZcRnWxEM/jpwfskkLLXAR6wziVpKrlw==} 83 | engines: {node: '>=8.6.0'} 84 | dev: false 85 | 86 | /@serialport/parser-readline@10.5.0: 87 | resolution: {integrity: sha512-0aXJknodcl94W9zSjvU+sLdXiyEG2rqjQmvBWZCr8wJZjWEtv3RgrnYiWq4i2OTOyC8C/oPK8ZjpBjQptRsoJQ==} 88 | engines: {node: '>=12.0.0'} 89 | dependencies: 90 | '@serialport/parser-delimiter': 10.5.0 91 | dev: false 92 | 93 | /@serialport/parser-readline@11.0.0: 94 | resolution: {integrity: sha512-rRAivhRkT3YO28WjmmG4FQX6L+KMb5/ikhyylRfzWPw0nSXy97+u07peS9CbHqaNvJkMhH1locp2H36aGMOEIA==} 95 | engines: {node: '>=12.0.0'} 96 | dependencies: 97 | '@serialport/parser-delimiter': 11.0.0 98 | dev: false 99 | 100 | /@serialport/parser-ready@11.0.0: 101 | resolution: {integrity: sha512-lSsCPIctoc5kADCKnZDYBz1j69TsFqtnaWUicBzUAIAoUXpYKeYld8YX5NrvjViuVfIJeiqLZeGjxOWe5fqQqQ==} 102 | engines: {node: '>=12.0.0'} 103 | dev: false 104 | 105 | /@serialport/parser-regex@11.0.0: 106 | resolution: {integrity: sha512-aKuc/+/KE9swahTbYpSuOsQa7LggPx7jhfobJLPVVbAic80OpfCIY+MKr6Ax4R6UtQwF90O5Yk6OEmbbvtEmiA==} 107 | engines: {node: '>=12.0.0'} 108 | dev: false 109 | 110 | /@serialport/parser-slip-encoder@11.0.0: 111 | resolution: {integrity: sha512-3ZI/swd2it20vmu2tzqDbkyE4dqy+kExEDY6T33YQ210HDKPVhqj7FAVGo5P++MZ3dup1of11t4P9UPBNkuJnQ==} 112 | engines: {node: '>=12.0.0'} 113 | dev: false 114 | 115 | /@serialport/parser-spacepacket@11.0.0: 116 | resolution: {integrity: sha512-+hqRckrTEqz+/uAUZY0Tq6YIRyCl4oQOH1MeVzKiFiGNjZP7hDJCDoY7LTr9CeJhxvcT0ItTbtjGBqGumV8fxg==} 117 | engines: {node: '>=12.0.0'} 118 | dev: false 119 | 120 | /@serialport/stream@11.0.0: 121 | resolution: {integrity: sha512-Zty7B8C1H2XRnay2mVmW1ygEHXRHXQDcaC5wAVvOZMbQSc7ye03rMlPvviDS+pGxU2t2A2bMo34CUrRduSBong==} 122 | engines: {node: '>=12.0.0'} 123 | dependencies: 124 | '@serialport/bindings-interface': 1.2.2 125 | debug: 4.3.4 126 | transitivePeerDependencies: 127 | - supports-color 128 | dev: false 129 | 130 | /@types/node@18.16.10: 131 | resolution: {integrity: sha512-sMo3EngB6QkMBlB9rBe1lFdKSLqljyWPPWv6/FzSxh/IDlyVWSzE9RiF4eAuerQHybrWdqBgAGb03PM89qOasA==} 132 | dev: true 133 | 134 | /@types/serialport@8.0.2: 135 | resolution: {integrity: sha512-z4b1I8/vdZE3upgCcAL9VAWlVVFUVn5uo3faAHavkVfK/Hb1LUxKwp9YCtA5AZqEUCWoSWl20SRTOvAI/5fQWQ==} 136 | dependencies: 137 | '@types/node': 18.16.10 138 | dev: true 139 | 140 | /abbrev@1.1.1: 141 | resolution: {integrity: sha512-nne9/IiQ/hzIhY6pdDnbBtz7DjPTKrY00P/zvPSm5pOFkl6xuGrGnXn/VtTNNfNtAfZ9/1RtehkszU9qcTii0Q==} 142 | dev: false 143 | 144 | /ansi-regex@2.1.1: 145 | resolution: {integrity: sha512-TIGnTpdo+E3+pCyAluZvtED5p5wCqLdezCyhPZzKPcxvFplEt4i+W7OONCKgeZFT3+y5NZZfOOS/Bdcanm1MYA==} 146 | engines: {node: '>=0.10.0'} 147 | dev: false 148 | 149 | /aproba@1.2.0: 150 | resolution: {integrity: sha512-Y9J6ZjXtoYh8RnXVCMOU/ttDmk1aBjunq9vO0ta5x85WDQiQfUF9sIPBITdbiiIVcBo03Hi3jMxigBtsddlXRw==} 151 | dev: false 152 | 153 | /are-we-there-yet@1.1.7: 154 | resolution: {integrity: sha512-nxwy40TuMiUGqMyRHgCSWZ9FM4VAoRP4xUYSTv5ImRog+h9yISPbVH7H8fASCIzYn9wlEv4zvFL7uKDMCFQm3g==} 155 | dependencies: 156 | delegates: 1.0.0 157 | readable-stream: 2.3.8 158 | dev: false 159 | 160 | /balanced-match@1.0.2: 161 | resolution: {integrity: sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==} 162 | dev: false 163 | 164 | /brace-expansion@1.1.11: 165 | resolution: {integrity: sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==} 166 | dependencies: 167 | balanced-match: 1.0.2 168 | concat-map: 0.0.1 169 | dev: false 170 | 171 | /chownr@1.1.4: 172 | resolution: {integrity: sha512-jJ0bqzaylmJtVnNgzTeSOs8DPavpbYgEr/b0YL8/2GO3xJEhInFmhKMUnEJQjZumK7KXGFhUy89PrsJWlakBVg==} 173 | dev: false 174 | 175 | /code-point-at@1.1.0: 176 | resolution: {integrity: sha512-RpAVKQA5T63xEj6/giIbUEtZwJ4UFIc3ZtvEkiaUERylqe8xb5IvqcgOurZLahv93CLKfxcw5YI+DZcUBRyLXA==} 177 | engines: {node: '>=0.10.0'} 178 | dev: false 179 | 180 | /concat-map@0.0.1: 181 | resolution: {integrity: sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==} 182 | dev: false 183 | 184 | /console-control-strings@1.1.0: 185 | resolution: {integrity: sha512-ty/fTekppD2fIwRvnZAVdeOiGd1c7YXEixbgJTNzqcxJWKQnjJ/V1bNEEE6hygpM3WjwHFUVK6HTjWSzV4a8sQ==} 186 | dev: false 187 | 188 | /core-util-is@1.0.3: 189 | resolution: {integrity: sha512-ZQBvi1DcpJ4GDqanjucZ2Hj3wEO5pZDS89BWbkcrvdxksJorwUDDZamX9ldFkp9aw2lmBDLgkObEA4DWNJ9FYQ==} 190 | dev: false 191 | 192 | /debug@3.2.7: 193 | resolution: {integrity: sha512-CFjzYYAi4ThfiQvizrFQevTTXHtnCqWfe7x1AhgEscTz6ZbLbfoLRLPugTQyBth6f8ZERVUSyWHFD/7Wu4t1XQ==} 194 | peerDependencies: 195 | supports-color: '*' 196 | peerDependenciesMeta: 197 | supports-color: 198 | optional: true 199 | dependencies: 200 | ms: 2.1.2 201 | dev: false 202 | 203 | /debug@4.3.4: 204 | resolution: {integrity: sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==} 205 | engines: {node: '>=6.0'} 206 | peerDependencies: 207 | supports-color: '*' 208 | peerDependenciesMeta: 209 | supports-color: 210 | optional: true 211 | dependencies: 212 | ms: 2.1.2 213 | dev: false 214 | 215 | /deep-extend@0.6.0: 216 | resolution: {integrity: sha512-LOHxIOaPYdHlJRtCQfDIVZtfw/ufM8+rVj649RIHzcm/vGwQRXFt6OPqIFWsm2XEMrNIEtWR64sY1LEKD2vAOA==} 217 | engines: {node: '>=4.0.0'} 218 | dev: false 219 | 220 | /delegates@1.0.0: 221 | resolution: {integrity: sha512-bd2L678uiWATM6m5Z1VzNCErI3jiGzt6HGY8OVICs40JQq/HALfbyNJmp0UDakEY4pMMaN0Ly5om/B1VI/+xfQ==} 222 | dev: false 223 | 224 | /detect-libc@1.0.3: 225 | resolution: {integrity: sha512-pGjwhsmsp4kL2RTz08wcOlGN83otlqHeD/Z5T8GXZB+/YcpQ/dgo+lbU8ZsGxV0HIvqqxo9l7mqYwyYMD9bKDg==} 226 | engines: {node: '>=0.10'} 227 | hasBin: true 228 | dev: false 229 | 230 | /easy-sleep@1.2.2: 231 | resolution: {integrity: sha512-mn+XCd6xKQWkLKnFFyg/3QZ2QvXqIFgUZNo/gWUYRilYHYdg9BdxPO0fPVWm+rhx3wzDaKJePK5tYC8x5aKIKg==} 232 | requiresBuild: true 233 | dependencies: 234 | node-pre-gyp: 0.17.0 235 | transitivePeerDependencies: 236 | - supports-color 237 | dev: false 238 | 239 | /eventemitter3@5.0.1: 240 | resolution: {integrity: sha512-GWkBvjiSZK87ELrYOSESUYeVIc9mvLLf/nXalMOS5dYrgZq9o5OVkbZAVM06CVxYsCwH9BDZFPlQTlPA1j4ahA==} 241 | dev: false 242 | 243 | /fs-minipass@1.2.7: 244 | resolution: {integrity: sha512-GWSSJGFy4e9GUeCcbIkED+bgAoFyj7XF1mV8rma3QW4NIqX9Kyx79N/PF61H5udOV3aY1IaMLs6pGbH71nlCTA==} 245 | dependencies: 246 | minipass: 2.9.0 247 | dev: false 248 | 249 | /fs.realpath@1.0.0: 250 | resolution: {integrity: sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw==} 251 | dev: false 252 | 253 | /gauge@2.7.4: 254 | resolution: {integrity: sha512-14x4kjc6lkD3ltw589k0NrPD6cCNTD6CWoVUNpB85+DrtONoZn+Rug6xZU5RvSC4+TZPxA5AnBibQYAvZn41Hg==} 255 | dependencies: 256 | aproba: 1.2.0 257 | console-control-strings: 1.1.0 258 | has-unicode: 2.0.1 259 | object-assign: 4.1.1 260 | signal-exit: 3.0.7 261 | string-width: 1.0.2 262 | strip-ansi: 3.0.1 263 | wide-align: 1.1.5 264 | dev: false 265 | 266 | /glob@7.2.3: 267 | resolution: {integrity: sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==} 268 | dependencies: 269 | fs.realpath: 1.0.0 270 | inflight: 1.0.6 271 | inherits: 2.0.4 272 | minimatch: 3.1.2 273 | once: 1.4.0 274 | path-is-absolute: 1.0.1 275 | dev: false 276 | 277 | /has-unicode@2.0.1: 278 | resolution: {integrity: sha512-8Rf9Y83NBReMnx0gFzA8JImQACstCYWUplepDa9xprwwtmgEZUF0h/i5xSA625zB/I37EtrswSST6OXxwaaIJQ==} 279 | dev: false 280 | 281 | /iconv-lite@0.4.24: 282 | resolution: {integrity: sha512-v3MXnZAcvnywkTUEZomIActle7RXXeedOR31wwl7VlyoXO4Qi9arvSenNQWne1TcRwhCL1HwLI21bEqdpj8/rA==} 283 | engines: {node: '>=0.10.0'} 284 | dependencies: 285 | safer-buffer: 2.1.2 286 | dev: false 287 | 288 | /ignore-walk@3.0.4: 289 | resolution: {integrity: sha512-PY6Ii8o1jMRA1z4F2hRkH/xN59ox43DavKvD3oDpfurRlOJyAHpifIwpbdv1n4jt4ov0jSpw3kQ4GhJnpBL6WQ==} 290 | dependencies: 291 | minimatch: 3.1.2 292 | dev: false 293 | 294 | /inflight@1.0.6: 295 | resolution: {integrity: sha512-k92I/b08q4wvFscXCLvqfsHCrjrF7yiXsQuIVvVE7N82W3+aqpzuUdBbfhWcy/FZR3/4IgflMgKLOsvPDrGCJA==} 296 | dependencies: 297 | once: 1.4.0 298 | wrappy: 1.0.2 299 | dev: false 300 | 301 | /inherits@2.0.4: 302 | resolution: {integrity: sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==} 303 | dev: false 304 | 305 | /ini@1.3.8: 306 | resolution: {integrity: sha512-JV/yugV2uzW5iMRSiZAyDtQd+nxtUnjeLt0acNdw98kKLrvuRVyB80tsREOE7yvGVgalhZ6RNXCmEHkUKBKxew==} 307 | dev: false 308 | 309 | /is-fullwidth-code-point@1.0.0: 310 | resolution: {integrity: sha512-1pqUqRjkhPJ9miNq9SwMfdvi6lBJcd6eFxvfaivQhaH3SgisfiuudvFntdKOmxuee/77l+FPjKrQjWvmPjWrRw==} 311 | engines: {node: '>=0.10.0'} 312 | dependencies: 313 | number-is-nan: 1.0.1 314 | dev: false 315 | 316 | /isarray@1.0.0: 317 | resolution: {integrity: sha512-VLghIWNM6ELQzo7zwmcg0NmTVyWKYjvIeM83yjp0wRDTmUnrM678fQbcKBo6n2CJEF0szoG//ytg+TKla89ALQ==} 318 | dev: false 319 | 320 | /minimatch@3.1.2: 321 | resolution: {integrity: sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==} 322 | dependencies: 323 | brace-expansion: 1.1.11 324 | dev: false 325 | 326 | /minimist@1.2.8: 327 | resolution: {integrity: sha512-2yyAR8qBkN3YuheJanUpWC5U3bb5osDywNB8RzDVlDwDHbocAJveqqj1u8+SVD7jkWT4yvsHCpWqqWqAxb0zCA==} 328 | dev: false 329 | 330 | /minipass@2.9.0: 331 | resolution: {integrity: sha512-wxfUjg9WebH+CUDX/CdbRlh5SmfZiy/hpkxaRI16Y9W56Pa75sWgd/rvFilSgrauD9NyFymP/+JFV3KwzIsJeg==} 332 | dependencies: 333 | safe-buffer: 5.2.1 334 | yallist: 3.1.1 335 | dev: false 336 | 337 | /minizlib@1.3.3: 338 | resolution: {integrity: sha512-6ZYMOEnmVsdCeTJVE0W9ZD+pVnE8h9Hma/iOwwRDsdQoePpoX56/8B6z3P9VNwppJuBKNRuFDRNRqRWexT9G9Q==} 339 | dependencies: 340 | minipass: 2.9.0 341 | dev: false 342 | 343 | /mkdirp@0.5.6: 344 | resolution: {integrity: sha512-FP+p8RB8OWpF3YZBCrP5gtADmtXApB5AMLn+vdyA+PyxCjrCs00mjyUozssO33cwDeT3wNGdLxJ5M//YqtHAJw==} 345 | hasBin: true 346 | dependencies: 347 | minimist: 1.2.8 348 | dev: false 349 | 350 | /ms@2.1.2: 351 | resolution: {integrity: sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==} 352 | dev: false 353 | 354 | /needle@2.9.1: 355 | resolution: {integrity: sha512-6R9fqJ5Zcmf+uYaFgdIHmLwNldn5HbK8L5ybn7Uz+ylX/rnOsSp1AHcvQSrCaFN+qNM1wpymHqD7mVasEOlHGQ==} 356 | engines: {node: '>= 4.4.x'} 357 | hasBin: true 358 | dependencies: 359 | debug: 3.2.7 360 | iconv-lite: 0.4.24 361 | sax: 1.2.4 362 | transitivePeerDependencies: 363 | - supports-color 364 | dev: false 365 | 366 | /node-addon-api@6.1.0: 367 | resolution: {integrity: sha512-+eawOlIgy680F0kBzPUNFhMZGtJ1YmqM6l4+Crf4IkImjYrO/mqPwRMh352g23uIaQKFItcQ64I7KMaJxHgAVA==} 368 | dev: false 369 | 370 | /node-gyp-build@4.6.0: 371 | resolution: {integrity: sha512-NTZVKn9IylLwUzaKjkas1e4u2DLNcV4rdYagA4PWdPwW87Bi7z+BznyKSRwS/761tV/lzCGXplWsiaMjLqP2zQ==} 372 | hasBin: true 373 | dev: false 374 | 375 | /node-pre-gyp@0.17.0: 376 | resolution: {integrity: sha512-abzZt1hmOjkZez29ppg+5gGqdPLUuJeAEwVPtHYEJgx0qzttCbcKFpxrCQn2HYbwCv2c+7JwH4BgEzFkUGpn4A==} 377 | deprecated: 'Please upgrade to @mapbox/node-pre-gyp: the non-scoped node-pre-gyp package is deprecated and only the @mapbox scoped package will recieve updates in the future' 378 | hasBin: true 379 | dependencies: 380 | detect-libc: 1.0.3 381 | mkdirp: 0.5.6 382 | needle: 2.9.1 383 | nopt: 4.0.3 384 | npm-packlist: 1.4.8 385 | npmlog: 4.1.2 386 | rc: 1.2.8 387 | rimraf: 2.7.1 388 | semver: 5.7.1 389 | tar: 4.4.19 390 | transitivePeerDependencies: 391 | - supports-color 392 | dev: false 393 | 394 | /nopt@4.0.3: 395 | resolution: {integrity: sha512-CvaGwVMztSMJLOeXPrez7fyfObdZqNUK1cPAEzLHrTybIua9pMdmmPR5YwtfNftIOMv3DPUhFaxsZMNTQO20Kg==} 396 | hasBin: true 397 | dependencies: 398 | abbrev: 1.1.1 399 | osenv: 0.1.5 400 | dev: false 401 | 402 | /npm-bundled@1.1.2: 403 | resolution: {integrity: sha512-x5DHup0SuyQcmL3s7Rx/YQ8sbw/Hzg0rj48eN0dV7hf5cmQq5PXIeioroH3raV1QC1yh3uTYuMThvEQF3iKgGQ==} 404 | dependencies: 405 | npm-normalize-package-bin: 1.0.1 406 | dev: false 407 | 408 | /npm-normalize-package-bin@1.0.1: 409 | resolution: {integrity: sha512-EPfafl6JL5/rU+ot6P3gRSCpPDW5VmIzX959Ob1+ySFUuuYHWHekXpwdUZcKP5C+DS4GEtdJluwBjnsNDl+fSA==} 410 | dev: false 411 | 412 | /npm-packlist@1.4.8: 413 | resolution: {integrity: sha512-5+AZgwru5IevF5ZdnFglB5wNlHG1AOOuw28WhUq8/8emhBmLv6jX5by4WJCh7lW0uSYZYS6DXqIsyZVIXRZU9A==} 414 | dependencies: 415 | ignore-walk: 3.0.4 416 | npm-bundled: 1.1.2 417 | npm-normalize-package-bin: 1.0.1 418 | dev: false 419 | 420 | /npmlog@4.1.2: 421 | resolution: {integrity: sha512-2uUqazuKlTaSI/dC8AzicUck7+IrEaOnN/e0jd3Xtt1KcGpwx30v50mL7oPyr/h9bL3E4aZccVwpwP+5W9Vjkg==} 422 | dependencies: 423 | are-we-there-yet: 1.1.7 424 | console-control-strings: 1.1.0 425 | gauge: 2.7.4 426 | set-blocking: 2.0.0 427 | dev: false 428 | 429 | /number-is-nan@1.0.1: 430 | resolution: {integrity: sha512-4jbtZXNAsfZbAHiiqjLPBiCl16dES1zI4Hpzzxw61Tk+loF+sBDBKx1ICKKKwIqQ7M0mFn1TmkN7euSncWgHiQ==} 431 | engines: {node: '>=0.10.0'} 432 | dev: false 433 | 434 | /object-assign@4.1.1: 435 | resolution: {integrity: sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg==} 436 | engines: {node: '>=0.10.0'} 437 | dev: false 438 | 439 | /once@1.4.0: 440 | resolution: {integrity: sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w==} 441 | dependencies: 442 | wrappy: 1.0.2 443 | dev: false 444 | 445 | /os-homedir@1.0.2: 446 | resolution: {integrity: sha512-B5JU3cabzk8c67mRRd3ECmROafjYMXbuzlwtqdM8IbS8ktlTix8aFGb2bAGKrSRIlnfKwovGUUr72JUPyOb6kQ==} 447 | engines: {node: '>=0.10.0'} 448 | dev: false 449 | 450 | /os-tmpdir@1.0.2: 451 | resolution: {integrity: sha512-D2FR03Vir7FIu45XBY20mTb+/ZSWB00sjU9jdQXt83gDrI4Ztz5Fs7/yy74g2N5SVQY4xY1qDr4rNddwYRVX0g==} 452 | engines: {node: '>=0.10.0'} 453 | dev: false 454 | 455 | /osenv@0.1.5: 456 | resolution: {integrity: sha512-0CWcCECdMVc2Rw3U5w9ZjqX6ga6ubk1xDVKxtBQPK7wis/0F2r9T6k4ydGYhecl7YUBxBVxhL5oisPsNxAPe2g==} 457 | dependencies: 458 | os-homedir: 1.0.2 459 | os-tmpdir: 1.0.2 460 | dev: false 461 | 462 | /path-is-absolute@1.0.1: 463 | resolution: {integrity: sha512-AVbw3UJ2e9bq64vSaS9Am0fje1Pa8pbGqTTsmXfaIiMpnr5DlDhfJOuLj9Sf95ZPVDAUerDfEk88MPmPe7UCQg==} 464 | engines: {node: '>=0.10.0'} 465 | dev: false 466 | 467 | /process-nextick-args@2.0.1: 468 | resolution: {integrity: sha512-3ouUOpQhtgrbOa17J7+uxOTpITYWaGP7/AhoR3+A+/1e9skrzelGi/dXzEYyvbxubEF6Wn2ypscTKiKJFFn1ag==} 469 | dev: false 470 | 471 | /rc@1.2.8: 472 | resolution: {integrity: sha512-y3bGgqKj3QBdxLbLkomlohkvsA8gdAiUQlSBJnBhfn+BPxg4bc62d8TcBW15wavDfgexCgccckhcZvywyQYPOw==} 473 | hasBin: true 474 | dependencies: 475 | deep-extend: 0.6.0 476 | ini: 1.3.8 477 | minimist: 1.2.8 478 | strip-json-comments: 2.0.1 479 | dev: false 480 | 481 | /readable-stream@2.3.8: 482 | resolution: {integrity: sha512-8p0AUk4XODgIewSi0l8Epjs+EVnWiK7NoDIEGU0HhE7+ZyY8D1IMY7odu5lRrFXGg71L15KG8QrPmum45RTtdA==} 483 | dependencies: 484 | core-util-is: 1.0.3 485 | inherits: 2.0.4 486 | isarray: 1.0.0 487 | process-nextick-args: 2.0.1 488 | safe-buffer: 5.1.2 489 | string_decoder: 1.1.1 490 | util-deprecate: 1.0.2 491 | dev: false 492 | 493 | /rimraf@2.7.1: 494 | resolution: {integrity: sha512-uWjbaKIK3T1OSVptzX7Nl6PvQ3qAGtKEtVRjRuazjfL3Bx5eI409VZSqgND+4UNnmzLVdPj9FqFJNPqBZFve4w==} 495 | hasBin: true 496 | dependencies: 497 | glob: 7.2.3 498 | dev: false 499 | 500 | /safe-buffer@5.1.2: 501 | resolution: {integrity: sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==} 502 | dev: false 503 | 504 | /safe-buffer@5.2.1: 505 | resolution: {integrity: sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==} 506 | dev: false 507 | 508 | /safer-buffer@2.1.2: 509 | resolution: {integrity: sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==} 510 | dev: false 511 | 512 | /sax@1.2.4: 513 | resolution: {integrity: sha512-NqVDv9TpANUjFm0N8uM5GxL36UgKi9/atZw+x7YFnQ8ckwFGKrl4xX4yWtrey3UJm5nP1kUbnYgLopqWNSRhWw==} 514 | dev: false 515 | 516 | /semver@5.7.1: 517 | resolution: {integrity: sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ==} 518 | hasBin: true 519 | dev: false 520 | 521 | /serialport@11.0.0: 522 | resolution: {integrity: sha512-bxs3XejQcOHWpzPAaXMhxVRlbem6fjNUrux3ToqrGvFR6BcjOYhqE5CsHOuutv37kmhmnuHrn+/hN+1BpTmaFg==} 523 | engines: {node: '>=12.0.0'} 524 | dependencies: 525 | '@serialport/binding-mock': 10.2.2 526 | '@serialport/bindings-cpp': 11.0.1 527 | '@serialport/parser-byte-length': 11.0.0 528 | '@serialport/parser-cctalk': 11.0.0 529 | '@serialport/parser-delimiter': 11.0.0 530 | '@serialport/parser-inter-byte-timeout': 11.0.0 531 | '@serialport/parser-packet-length': 11.0.0 532 | '@serialport/parser-readline': 11.0.0 533 | '@serialport/parser-ready': 11.0.0 534 | '@serialport/parser-regex': 11.0.0 535 | '@serialport/parser-slip-encoder': 11.0.0 536 | '@serialport/parser-spacepacket': 11.0.0 537 | '@serialport/stream': 11.0.0 538 | debug: 4.3.4 539 | transitivePeerDependencies: 540 | - supports-color 541 | dev: false 542 | 543 | /set-blocking@2.0.0: 544 | resolution: {integrity: sha512-KiKBS8AnWGEyLzofFfmvKwpdPzqiy16LvQfK3yv/fVH7Bj13/wl3JSR1J+rfgRE9q7xUJK4qvgS8raSOeLUehw==} 545 | dev: false 546 | 547 | /signal-exit@3.0.7: 548 | resolution: {integrity: sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ==} 549 | dev: false 550 | 551 | /string-width@1.0.2: 552 | resolution: {integrity: sha512-0XsVpQLnVCXHJfyEs8tC0zpTVIr5PKKsQtkT29IwupnPTjtPmQ3xT/4yCREF9hYkV/3M3kzcUTSAZT6a6h81tw==} 553 | engines: {node: '>=0.10.0'} 554 | dependencies: 555 | code-point-at: 1.1.0 556 | is-fullwidth-code-point: 1.0.0 557 | strip-ansi: 3.0.1 558 | dev: false 559 | 560 | /string_decoder@1.1.1: 561 | resolution: {integrity: sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==} 562 | dependencies: 563 | safe-buffer: 5.1.2 564 | dev: false 565 | 566 | /strip-ansi@3.0.1: 567 | resolution: {integrity: sha512-VhumSSbBqDTP8p2ZLKj40UjBCV4+v8bUSEpUb4KjRgWk9pbqGF4REFj6KEagidb2f/M6AzC0EmFyDNGaw9OCzg==} 568 | engines: {node: '>=0.10.0'} 569 | dependencies: 570 | ansi-regex: 2.1.1 571 | dev: false 572 | 573 | /strip-json-comments@2.0.1: 574 | resolution: {integrity: sha512-4gB8na07fecVVkOI6Rs4e7T6NOTki5EmL7TUduTs6bu3EdnSycntVJ4re8kgZA+wx9IueI2Y11bfbgwtzuE0KQ==} 575 | engines: {node: '>=0.10.0'} 576 | dev: false 577 | 578 | /tar@4.4.19: 579 | resolution: {integrity: sha512-a20gEsvHnWe0ygBY8JbxoM4w3SJdhc7ZAuxkLqh+nvNQN2IOt0B5lLgM490X5Hl8FF0dl0tOf2ewFYAlIFgzVA==} 580 | engines: {node: '>=4.5'} 581 | dependencies: 582 | chownr: 1.1.4 583 | fs-minipass: 1.2.7 584 | minipass: 2.9.0 585 | minizlib: 1.3.3 586 | mkdirp: 0.5.6 587 | safe-buffer: 5.2.1 588 | yallist: 3.1.1 589 | dev: false 590 | 591 | /typescript@5.0.4: 592 | resolution: {integrity: sha512-cW9T5W9xY37cc+jfEnaUvX91foxtHkza3Nw3wkoF4sSlKn0MONdkdEndig/qPBWXNkmplh3NzayQzCiHM4/hqw==} 593 | engines: {node: '>=12.20'} 594 | hasBin: true 595 | dev: true 596 | 597 | /util-deprecate@1.0.2: 598 | resolution: {integrity: sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==} 599 | dev: false 600 | 601 | /wide-align@1.1.5: 602 | resolution: {integrity: sha512-eDMORYaPNZ4sQIuuYPDHdQvf4gyCF9rEEV/yPxGfwPkRodwEgiMUUXTx/dex+Me0wxx53S+NgUHaP7y3MGlDmg==} 603 | dependencies: 604 | string-width: 1.0.2 605 | dev: false 606 | 607 | /wrappy@1.0.2: 608 | resolution: {integrity: sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==} 609 | dev: false 610 | 611 | /yallist@3.1.1: 612 | resolution: {integrity: sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g==} 613 | dev: false 614 | -------------------------------------------------------------------------------- /src/index.ts: -------------------------------------------------------------------------------- 1 | import { EventEmitter } from "eventemitter3" 2 | import { SerialPort } from "serialport" 3 | 4 | export const VENDOR_ID = "0403" // Enttec 5 | export const PRODUCT_ID = "6001" // Open DMX USB 6 | 7 | interface Events { 8 | ready: [] 9 | error: [Error] 10 | } 11 | 12 | type Usleep = (microSeconds: number) => unknown 13 | 14 | export class EnttecOpenDMXUSBDevice extends EventEmitter { 15 | private shouldBeSending = false 16 | private sendTimeout: ReturnType | null = null 17 | private buffer = Buffer.alloc(513) 18 | private readonly port: SerialPort 19 | private readonly usleep: Usleep | null 20 | 21 | /** 22 | * @param path A path returned by {@link EnttecOpenDMXUSBDevice.listDevices} or 23 | * {@link EnttecOpenDMXUSBDevice.getFirstAvailableDevice}. 24 | * @param [startSending=true] Whether the device should start sending as soon as it is ready. 25 | * @param [usleep=null] A function blocking the event loop for `n` microseconds. See the README.md for more information. 26 | */ 27 | constructor(path: string, startSending = true, usleep: Usleep | null = null) { 28 | super() 29 | 30 | this.port = new SerialPort({ 31 | path, 32 | baudRate: 250000, 33 | dataBits: 8, 34 | stopBits: 2, 35 | parity: "none", 36 | autoOpen: true 37 | }) 38 | 39 | this.port.on("open", () => { 40 | this.emit("ready") 41 | if (startSending) this.startSending(0) 42 | }) 43 | 44 | // Without this, errors would be uncaught. 45 | this.port.on("error", (error: Error) => { 46 | this.emit("error", error) 47 | }) 48 | 49 | this.usleep = usleep 50 | } 51 | 52 | /** 53 | * Start sending. 54 | * @param [interval=0] The milliseconds between each attempt to send. Most of the time `0` works fine. 55 | * @throws When the device is not ready yet. 56 | */ 57 | startSending(interval = 0) { 58 | if (!this.port.isOpen) throw new Error("The device is not ready yet. Wait for the 'ready' event.") 59 | 60 | this.shouldBeSending = true 61 | 62 | // eslint-disable-next-line unicorn/consistent-function-scoping 63 | const send = () => { 64 | this._sendUniverse() 65 | .then(() => { 66 | if (this.shouldBeSending) 67 | // eslint-disable-next-line @typescript-eslint/no-misused-promises 68 | this.sendTimeout = setTimeout(send, interval) 69 | }) 70 | .catch(error => this.emit("error", error)) 71 | } 72 | 73 | send() 74 | } 75 | 76 | /** 77 | * Stop sending. 78 | */ 79 | stopSending() { 80 | this.shouldBeSending = false 81 | 82 | if (this.sendTimeout !== null) clearTimeout(this.sendTimeout) 83 | } 84 | 85 | /** 86 | * Set channel values. 87 | * If channels is an Object, the keys are the channel numbers. 88 | * 89 | * @param channels 90 | * @param [clear=false] Whether all previously assigned channels should be set to `0` 91 | */ 92 | setChannels(channels: Buffer | number[] | Record, clear = false) { 93 | if (clear) { 94 | this.buffer = Buffer.alloc(513) 95 | this.buffer[0] = 0 96 | } 97 | 98 | if (Buffer.isBuffer(channels)) { 99 | if (channels.length > 512) throw new Error("The maximum size of an DMX universe is 512 channels.") 100 | channels.copy(this.buffer, 1) 101 | } else if (Array.isArray(channels)) { 102 | if (channels.length > 512) throw new Error("The maximum size of an DMX universe is 512 channels.") 103 | 104 | channels.forEach((value, index) => { 105 | if (value > 0xFF || value < 0) throw new Error("All values must be between 0 and 255.") 106 | this.buffer[index + 1] = value 107 | }) 108 | } else if (typeof channels === "object") { 109 | Object.entries(channels).forEach(([channel, value]) => { 110 | let channelNumber 111 | 112 | try { 113 | channelNumber = Number.parseInt(channel, 10) 114 | } catch { 115 | throw new Error("Only channel numbers are supported.") 116 | } 117 | 118 | if (channelNumber > 512 || channelNumber < 1) 119 | throw new Error("All channel numbers must be between 1 and 512.") 120 | else if (value > 0xFF || value < 0) 121 | throw new Error("All values must be between 0 and 255.") 122 | 123 | this.buffer[channelNumber] = value 124 | }) 125 | } else throw new TypeError("data must be of type Buffer, Object or Array.") 126 | } 127 | 128 | /** 129 | * @returns A Promise resolved when the whole universe was sent. 130 | * @private 131 | */ 132 | async _sendUniverse(): Promise { 133 | return new Promise(resolve => { 134 | this.port.set({ brk: true, rts: false }, () => { 135 | if (this.usleep === null) { 136 | setTimeout(() => { 137 | this.port.set({ brk: false, rts: false }, () => { 138 | setTimeout(() => { 139 | this.port.write(this.buffer, () => resolve()) 140 | }, 1) 141 | }) 142 | }, 1) 143 | } else { 144 | this.usleep(92) 145 | 146 | this.port.set({ brk: false, rts: false }, () => { 147 | this.usleep!(12) 148 | this.port.write(this.buffer, () => resolve()) 149 | }) 150 | } 151 | }) 152 | }) 153 | } 154 | 155 | /** 156 | * Get the paths of all available devices. 157 | */ 158 | static async listDevices(): Promise { 159 | const allPorts = await SerialPort.list() 160 | return allPorts 161 | .filter(device => device.vendorId === VENDOR_ID && device.productId === PRODUCT_ID) 162 | .map(device => device.path) 163 | } 164 | 165 | /** 166 | * Get the path of the first available device. 167 | * @throws When no device is found. 168 | */ 169 | static async getFirstAvailableDevice(): Promise { 170 | const devices = await EnttecOpenDMXUSBDevice.listDevices() 171 | 172 | if (devices.length === 0) throw new Error("No device found.") 173 | else return devices[0] 174 | } 175 | } 176 | -------------------------------------------------------------------------------- /tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "target": "es2019", 4 | "module": "commonjs", 5 | "moduleResolution": "node", 6 | "strict": true, 7 | "esModuleInterop": true, 8 | "outDir": "dist", 9 | "declaration": true, 10 | "types": ["@types/node"], 11 | "lib": ["es2019"] 12 | } 13 | } 14 | --------------------------------------------------------------------------------