├── .gitignore ├── LICENSE.txt ├── README.md ├── index.js ├── lib ├── animations.js ├── commander.js ├── frames.js ├── lighting_node_pro.js └── ll_fan.js ├── package-lock.json └── package.json /.gitignore: -------------------------------------------------------------------------------- 1 | # Logs 2 | logs 3 | *.log 4 | 5 | # Runtime data 6 | pids 7 | *.pid 8 | *.seed 9 | 10 | # Directory for instrumented libs generated by jscoverage/JSCover 11 | lib-cov 12 | 13 | # Coverage directory used by tools like istanbul 14 | coverage 15 | 16 | # Grunt intermediate storage (http://gruntjs.com/creating-plugins#storing-task-files) 17 | .grunt 18 | 19 | # Compiled binary addons (http://nodejs.org/api/addons.html) 20 | build/Release 21 | 22 | # Dependency directory 23 | # Deployed apps should consider commenting this line out: 24 | # see https://npmjs.org/doc/faq.html#Should-I-check-my-node_modules-folder-into-git 25 | node_modules 26 | -------------------------------------------------------------------------------- /LICENSE.txt: -------------------------------------------------------------------------------- 1 | Copyright 2018 Benjamin Burkhart 2 | 3 | Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: 4 | 5 | The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. 6 | 7 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 8 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | Lighting Node Pro 2 | ================= 3 | 4 | I generally find LEDs distracting, but do find their use in signalling information such as various device temperatures. 5 | 6 | 7 | I reversed the protocol of the Corsair LL fan LEDs+Lighting Node Pro controller because I wanted there to be some Linux 8 | support and found there was none. 9 | 10 | 11 | I used wireshark in Windows, turned all fans off, started recording, and then set static R, G, and B values with varying 12 | fan counts to figure out how it all worked. 13 | 14 | 15 | ## How to use this as an end-user 16 | 17 | Please note this ONLY works with Lighting Node Pro controllers with Corsair LL fans. Other fans can be added but I just 18 | haven't dug too deeply as I only have these ones. 19 | 20 | 21 | 1) Make sure your udev rules allow you acces to USB devices. 22 | 23 | 2) you MUST provide fan count 24 | 25 | 3) Choose a color or animation, animations are in animations.js if you're curious how they work 26 | 27 | 4) You must leave it running (run with & at the end if you want it to go to background) 28 | 29 | 30 | #### Sets all 3 fans to be red 31 | 32 | node index.js --fan-count=3 --colors red 33 | 34 | #### Sets fans to red, white and blue 35 | 36 | node index.js --fan-count=3 --colors red,white,blue 37 | 38 | 39 | #### Tracer Animation 40 | 41 | colors are two colors to use for ring and rotating LED 42 | 43 | node index.js --fan-count=3 --animation Tracer -p "colors=blue,black" 44 | 45 | 46 | #### I want it to go faster! (-r is framerate) 47 | 48 | This uses -r for frame rate of 10 per second 49 | 50 | node index.js --fan-count=3 --animation Tracer -p "colors=red,white" -r 10 51 | 52 | 53 | ### I want different frame rates for all the different fans 54 | 55 | Ok I think that's a bit much to ask, but here it is. 56 | 57 | node index.js --fan-count=3 --animation Tracer -p "colors=red,black&fps=16,32,48" 58 | 59 | 60 | ### Silly 3 fan animation example 61 | 62 | node index.js --fan-count=3 --animation RedBlueCollision 63 | 64 | 65 | 66 | If anything ever freezes, just control+c, if it still is frozen, hitting it again will force exit, should work fine the 67 | next run. You can use the -x flag to force a USB reset. 68 | 69 | 70 | -d flag prints the bytes as they're sent down the USB bus. 71 | 72 | 73 | -s
-b allows you to specify which device if you have multiple. 74 | 75 | 76 | ## Concepts specific to this implementation 77 | 78 | The Corsair LL fans have 16 LEDs, and it just takes simple commands to set the values of those LEDs. The effects you see 79 | in Windows are all implemented in software (there's one exception and that's the default rainbow wave which it falls back to if there's no new input). 80 | 81 | If the LED configuration is the exact same each run, you can simply send {33}{ff} to "maintain" what was last displayed on the fan LEDs. 82 | 83 | So to build animations, we need to have an event stack, and a frame rate. If the frame rate is 1 (1 per second), every second, 84 | a frame will be popped off the event stack, and that's what will be sent during subsequent "set colors" requests. When the set 85 | is exhausted, it starts back over. 86 | 87 | 88 | ## Reversing information 89 | 90 | 91 | All packets are null padded to 64 bytes 92 | 93 | 94 | #### PREAMBLE/INIT 95 | 96 | Sent at start when getting into the set color mode 97 | 98 | {37} 99 | 100 | # If num fans is 0 here, it disables all LEDs, important to do if you don't want it to fall back to rainbow after exiting 101 | {35}{00}{00}{<(short)num fans>0}{00}{01}{01} 102 | {3b}{00}{01} 103 | {38}{00}{02} 104 | {34} 105 | {37}{01} 106 | {34}{01} 107 | {38}{01}{01} 108 | {33}{ff} 109 | 110 | 111 | #### Color Request 112 | 113 | 3 requests per, 1 for each color channel 114 | 115 | {32}{00}{00}{32}{00} 116 | ^ Color channel (R=0x00, G=0x01, B=0x02) 117 | 118 | 16 bytes per fan, if you had one fan, it would be 119 | 120 | {ff}{ff}{ff}{ff}{ff}{ff}{ff}{ff}{ff}{ff}{ff}{ff}{ff}{ff}{ff}{ff} 121 | 122 | 123 | 124 | This is for the first 3 fans, If there's 4 fans, only 2 bytes of the 4th fan is done on the first request, after doing 125 | all color channels, it does an extension run, that looks like: 126 | 127 | {32}{00}{32}{2e} 14 bytes remaining for 4th fan, 16 bytes for 5th fan, 16 bytes for 6th fan. 128 | 129 | 130 | Why 16 bytes? There's 16 different LEDs. 4 on the inside, and 12 on the outside. Since there's 12, I find it easiest 131 | to refer to the positions as positions on a 12-hour clock. 132 | 133 | 134 | While facing intake (with corsair sticker logo facing you) 135 | 136 | 1st - top inner 137 | 2nd - inner left 138 | 3rd - inner bottom 139 | 4th - inner right 140 | 141 | 5th - 10 O'Clock 142 | 6th - 9 O'Clock 143 | 7th - 8 O'Clock 144 | 8th - 7 O'Clock 145 | 9th - 6 O'Clock 146 | 147 | 10th - 5 O'Clock 148 | 11th - 4 O'Clock 149 | 12th - 3 O'Clock 150 | 13th - 2 O'Clock 151 | 14th - 1 O'Clock 152 | 153 | 15th - 12 O'Clock 154 | 16th - 11 O'Clock 155 | 156 | 157 | 158 | 159 | -------------------------------------------------------------------------------- /index.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | 3 | const cmdlineArgs = require('command-line-args'); 4 | const rgbColor = require('rgb-color'); 5 | 6 | const Commander = require('./lib/commander.js'); 7 | const frames = require('./lib/frames.js'); 8 | const animations = require('./lib/animations.js'); 9 | 10 | const options = cmdlineArgs([ 11 | { 12 | name: 'fan-count', alias: 'f', type: Number, 13 | }, 14 | { 15 | name: 'framerate', alias: 'r', 16 | }, 17 | { 18 | name: 'animation', alias: 'a', 19 | }, 20 | { 21 | name: 'bus', alias: 'b', type: Number 22 | }, 23 | { 24 | name: 'address', alias: 's', type: Number 25 | }, 26 | { 27 | name: 'params', alias: 'p', 28 | }, 29 | { 30 | name: 'colors', alias: 'c' 31 | }, 32 | { 33 | name: 'off', alias: 'o', 34 | }, 35 | { 36 | name: 'debug', alias: 'd', type: Boolean 37 | }, 38 | { 39 | name: 'reset', alias: 'x', type: Boolean 40 | }, 41 | ]); 42 | 43 | 44 | const run = async () => { 45 | let cmd = new Commander(); 46 | let controller = cmd.scanForController(options['bus'], options['address']); 47 | 48 | // Initialize with 3 fans 49 | await controller.initialize(options['fan-count'], options['debug'], options['reset']); 50 | 51 | let sigIntCnt = 0; 52 | 53 | process.on('SIGINT', () => { 54 | if (sigIntCnt > 0) { 55 | console.log("Could not exit cleanly."); 56 | process.exit(1); 57 | } 58 | 59 | console.log("SIGINT: please press kill again to force exit if it doesn't exit within a few seconds."); 60 | sigIntCnt++; 61 | 62 | controller.exitLoop = true; 63 | 64 | controller.shutdown() 65 | .then(() => { 66 | console.log(`Successfully exited`); 67 | process.exit(0); 68 | }) 69 | }); 70 | 71 | if (!options.colors) { 72 | controller.devices.forEach(dev => dev.eventStack = [frames.descriptiveToFrame({ 73 | topInner: 'red', 74 | bottomInner: 'red', 75 | rightInner: 'red', 76 | leftInner: 'red', 77 | 78 | elevenOClock: 'blue', 79 | twelveOClock: 'red', 80 | oneOClock: 'blue', 81 | twoOClock: 'red', 82 | threeOClock: 'blue', 83 | fourOClock: 'red', 84 | fiveOClock: 'blue', 85 | sixOClock: 'red', 86 | sevenOClock: 'blue', 87 | eightOClock: 'red', 88 | nineOClock: 'blue', 89 | tenOClock: 'red', 90 | 91 | })]); 92 | } 93 | else { 94 | let colors = options.colors.split(','); 95 | 96 | if (colors.length > 1) { 97 | for (let i = 0; i < options['fan-count']; i++) { 98 | let color = rgbColor(colors[i % colors.length]); 99 | let dev = controller.devices[i].eventStack = [frames.staticColorFrame(color)]; 100 | } 101 | } 102 | else { 103 | let color = rgbColor(colors[0]); 104 | 105 | controller.devices.forEach(dev => dev.eventStack = [frames.staticColorFrame(color)]); 106 | } 107 | 108 | } 109 | 110 | if (options.animation) { 111 | if (!animations[options.animation]) { 112 | console.error(`Could not find animation ${options.animation}`); 113 | return process.exit(1); 114 | } 115 | 116 | animations[options.animation](controller.devices, options['params']) 117 | } 118 | 119 | if (options.framerate) { 120 | for (let i = 0; i < options['fan-count']; i++) { 121 | controller.devices[i].setFramerate(options.framerate); 122 | } 123 | } 124 | 125 | // Enter loop 126 | controller.loop(); 127 | }; 128 | 129 | run(); 130 | -------------------------------------------------------------------------------- /lib/animations.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | 3 | const qs = require('querystring'); 4 | 5 | const frames = require('./frames.js'); 6 | 7 | module.exports.Tracer = (devices, params) => { 8 | let pp = qs.parse(params || ""); 9 | 10 | let primaryColor = 'red'; 11 | let secondaryColor = 'black'; 12 | 13 | if (pp.colors) { 14 | let colors = pp.colors.split(','); 15 | 16 | if (colors.length > 0) 17 | primaryColor = colors[0]; 18 | if (colors.length > 1) 19 | secondaryColor = colors[1]; 20 | } 21 | 22 | let toReverse = []; 23 | if (pp.reverse) { 24 | toReverse = pp.reverse.split(',').map(f => ~~f); 25 | } 26 | 27 | let fpsa = []; 28 | if (pp.fps) { 29 | fpsa = pp.fps.split(',').map(f => ~~f); 30 | } 31 | 32 | // 12 frames 33 | let ev = [ 34 | frames.descriptiveToFrame({ 35 | topInner: primaryColor, 36 | bottomInner: primaryColor, 37 | rightInner: primaryColor, 38 | leftInner: primaryColor, 39 | 40 | elevenOClock: secondaryColor, 41 | twelveOClock: primaryColor, 42 | oneOClock: secondaryColor, 43 | twoOClock: secondaryColor, 44 | threeOClock: secondaryColor, 45 | fourOClock: secondaryColor, 46 | fiveOClock: secondaryColor, 47 | sixOClock: secondaryColor, 48 | sevenOClock: secondaryColor, 49 | eightOClock: secondaryColor, 50 | nineOClock: secondaryColor, 51 | tenOClock: secondaryColor, 52 | }), 53 | frames.descriptiveToFrame({ 54 | topInner: primaryColor, 55 | bottomInner: primaryColor, 56 | rightInner: primaryColor, 57 | leftInner: primaryColor, 58 | 59 | elevenOClock: secondaryColor, 60 | twelveOClock: secondaryColor, 61 | oneOClock: primaryColor, 62 | twoOClock: secondaryColor, 63 | threeOClock: secondaryColor, 64 | fourOClock: secondaryColor, 65 | fiveOClock: secondaryColor, 66 | sixOClock: secondaryColor, 67 | sevenOClock: secondaryColor, 68 | eightOClock: secondaryColor, 69 | nineOClock: secondaryColor, 70 | tenOClock: secondaryColor, 71 | }), 72 | frames.descriptiveToFrame({ 73 | topInner: primaryColor, 74 | bottomInner: primaryColor, 75 | rightInner: primaryColor, 76 | leftInner: primaryColor, 77 | 78 | elevenOClock: secondaryColor, 79 | twelveOClock: secondaryColor, 80 | oneOClock: secondaryColor, 81 | twoOClock: primaryColor, 82 | threeOClock: secondaryColor, 83 | fourOClock: secondaryColor, 84 | fiveOClock: secondaryColor, 85 | sixOClock: secondaryColor, 86 | sevenOClock: secondaryColor, 87 | eightOClock: secondaryColor, 88 | nineOClock: secondaryColor, 89 | tenOClock: secondaryColor, 90 | }), 91 | frames.descriptiveToFrame({ 92 | topInner: primaryColor, 93 | bottomInner: primaryColor, 94 | rightInner: primaryColor, 95 | leftInner: primaryColor, 96 | 97 | elevenOClock: secondaryColor, 98 | twelveOClock: secondaryColor, 99 | oneOClock: secondaryColor, 100 | twoOClock: secondaryColor, 101 | threeOClock: primaryColor, 102 | fourOClock: secondaryColor, 103 | fiveOClock: secondaryColor, 104 | sixOClock: secondaryColor, 105 | sevenOClock: secondaryColor, 106 | eightOClock: secondaryColor, 107 | nineOClock: secondaryColor, 108 | tenOClock: secondaryColor, 109 | }), 110 | frames.descriptiveToFrame({ 111 | topInner: primaryColor, 112 | bottomInner: primaryColor, 113 | rightInner: primaryColor, 114 | leftInner: primaryColor, 115 | 116 | elevenOClock: secondaryColor, 117 | twelveOClock: secondaryColor, 118 | oneOClock: secondaryColor, 119 | twoOClock: secondaryColor, 120 | threeOClock: secondaryColor, 121 | fourOClock: primaryColor, 122 | fiveOClock: secondaryColor, 123 | sixOClock: secondaryColor, 124 | sevenOClock: secondaryColor, 125 | eightOClock: secondaryColor, 126 | nineOClock: secondaryColor, 127 | tenOClock: secondaryColor, 128 | }), 129 | frames.descriptiveToFrame({ 130 | topInner: primaryColor, 131 | bottomInner: primaryColor, 132 | rightInner: primaryColor, 133 | leftInner: primaryColor, 134 | 135 | elevenOClock: secondaryColor, 136 | twelveOClock: secondaryColor, 137 | oneOClock: secondaryColor, 138 | twoOClock: secondaryColor, 139 | threeOClock: secondaryColor, 140 | fourOClock: secondaryColor, 141 | fiveOClock: primaryColor, 142 | sixOClock: secondaryColor, 143 | sevenOClock: secondaryColor, 144 | eightOClock: secondaryColor, 145 | nineOClock: secondaryColor, 146 | tenOClock: secondaryColor, 147 | }), 148 | frames.descriptiveToFrame({ 149 | topInner: primaryColor, 150 | bottomInner: primaryColor, 151 | rightInner: primaryColor, 152 | leftInner: primaryColor, 153 | 154 | elevenOClock: secondaryColor, 155 | twelveOClock: secondaryColor, 156 | oneOClock: secondaryColor, 157 | twoOClock: secondaryColor, 158 | threeOClock: secondaryColor, 159 | fourOClock: secondaryColor, 160 | fiveOClock: secondaryColor, 161 | sixOClock: primaryColor, 162 | sevenOClock: secondaryColor, 163 | eightOClock: secondaryColor, 164 | nineOClock: secondaryColor, 165 | tenOClock: secondaryColor, 166 | }), 167 | frames.descriptiveToFrame({ 168 | topInner: primaryColor, 169 | bottomInner: primaryColor, 170 | rightInner: primaryColor, 171 | leftInner: primaryColor, 172 | 173 | elevenOClock: secondaryColor, 174 | twelveOClock: secondaryColor, 175 | oneOClock: secondaryColor, 176 | twoOClock: secondaryColor, 177 | threeOClock: secondaryColor, 178 | fourOClock: secondaryColor, 179 | fiveOClock: secondaryColor, 180 | sixOClock: secondaryColor, 181 | sevenOClock: primaryColor, 182 | eightOClock: secondaryColor, 183 | nineOClock: secondaryColor, 184 | tenOClock: secondaryColor, 185 | }), 186 | frames.descriptiveToFrame({ 187 | topInner: primaryColor, 188 | bottomInner: primaryColor, 189 | rightInner: primaryColor, 190 | leftInner: primaryColor, 191 | 192 | elevenOClock: secondaryColor, 193 | twelveOClock: secondaryColor, 194 | oneOClock: secondaryColor, 195 | twoOClock: secondaryColor, 196 | threeOClock: secondaryColor, 197 | fourOClock: secondaryColor, 198 | fiveOClock: secondaryColor, 199 | sixOClock: secondaryColor, 200 | sevenOClock: secondaryColor, 201 | eightOClock: primaryColor, 202 | nineOClock: secondaryColor, 203 | tenOClock: secondaryColor, 204 | }), 205 | frames.descriptiveToFrame({ 206 | topInner: primaryColor, 207 | bottomInner: primaryColor, 208 | rightInner: primaryColor, 209 | leftInner: primaryColor, 210 | 211 | elevenOClock: secondaryColor, 212 | twelveOClock: secondaryColor, 213 | oneOClock: secondaryColor, 214 | twoOClock: secondaryColor, 215 | threeOClock: secondaryColor, 216 | fourOClock: secondaryColor, 217 | fiveOClock: secondaryColor, 218 | sixOClock: secondaryColor, 219 | sevenOClock: secondaryColor, 220 | eightOClock: secondaryColor, 221 | nineOClock: primaryColor, 222 | tenOClock: secondaryColor, 223 | }), 224 | frames.descriptiveToFrame({ 225 | topInner: primaryColor, 226 | bottomInner: primaryColor, 227 | rightInner: primaryColor, 228 | leftInner: primaryColor, 229 | 230 | elevenOClock: secondaryColor, 231 | twelveOClock: secondaryColor, 232 | oneOClock: secondaryColor, 233 | twoOClock: secondaryColor, 234 | threeOClock: secondaryColor, 235 | fourOClock: secondaryColor, 236 | fiveOClock: secondaryColor, 237 | sixOClock: secondaryColor, 238 | sevenOClock: secondaryColor, 239 | eightOClock: secondaryColor, 240 | nineOClock: secondaryColor, 241 | tenOClock: primaryColor, 242 | }), 243 | frames.descriptiveToFrame({ 244 | topInner: primaryColor, 245 | bottomInner: primaryColor, 246 | rightInner: primaryColor, 247 | leftInner: primaryColor, 248 | 249 | elevenOClock: primaryColor, 250 | twelveOClock: secondaryColor, 251 | oneOClock: secondaryColor, 252 | twoOClock: secondaryColor, 253 | threeOClock: secondaryColor, 254 | fourOClock: secondaryColor, 255 | fiveOClock: secondaryColor, 256 | sixOClock: secondaryColor, 257 | sevenOClock: secondaryColor, 258 | eightOClock: secondaryColor, 259 | nineOClock: secondaryColor, 260 | tenOClock: secondaryColor, 261 | }), 262 | ]; 263 | 264 | devices.forEach((dev, i) => { 265 | let stack = ev.slice(0); 266 | 267 | if (toReverse.indexOf(i) !== -1) { 268 | stack = frames.flipFrames(stack); 269 | } 270 | 271 | if (fpsa[i%fpsa.length]) { 272 | dev.setFramerate(fpsa[i%fpsa.length]); 273 | } 274 | 275 | dev.eventStack = stack; 276 | }); 277 | }; 278 | 279 | module.exports.ColorWheel = (devices, params) => { 280 | let pp = qs.parse(params || ""); 281 | 282 | let primaryColor = 'blue'; 283 | let secondaryColor = 'white'; 284 | 285 | if (pp.colors) { 286 | let colors = pp.colors.split(','); 287 | 288 | if (colors.length > 0) 289 | primaryColor = colors[0]; 290 | if (colors.length > 1) 291 | secondaryColor = colors[1]; 292 | } 293 | 294 | let fpsa = []; 295 | if (pp.fps) { 296 | fpsa = pp.fps.split(',').map(f => ~~f); 297 | } 298 | 299 | let ev = [ 300 | frames.descriptiveToFrame({ 301 | topInner: primaryColor, 302 | bottomInner: primaryColor, 303 | rightInner: secondaryColor, 304 | leftInner: secondaryColor, 305 | 306 | elevenOClock: primaryColor, 307 | twelveOClock: secondaryColor, 308 | oneOClock: primaryColor, 309 | twoOClock: secondaryColor, 310 | threeOClock: primaryColor, 311 | fourOClock: secondaryColor, 312 | fiveOClock: primaryColor, 313 | sixOClock: secondaryColor, 314 | sevenOClock: primaryColor, 315 | eightOClock: secondaryColor, 316 | nineOClock: primaryColor, 317 | tenOClock: secondaryColor, 318 | 319 | }), 320 | frames.descriptiveToFrame({ 321 | topInner: secondaryColor, 322 | bottomInner: secondaryColor, 323 | rightInner: primaryColor, 324 | leftInner: primaryColor, 325 | 326 | elevenOClock: secondaryColor, 327 | twelveOClock: primaryColor, 328 | oneOClock: secondaryColor, 329 | twoOClock: primaryColor, 330 | threeOClock: secondaryColor, 331 | fourOClock: primaryColor, 332 | fiveOClock: secondaryColor, 333 | sixOClock: primaryColor, 334 | sevenOClock: secondaryColor, 335 | eightOClock: primaryColor, 336 | nineOClock: secondaryColor, 337 | tenOClock: primaryColor, 338 | }), 339 | ]; 340 | 341 | devices.forEach((dev, i) => { 342 | dev.eventStack = frames.flipFrames(ev.slice(0)); 343 | 344 | if (fpsa[i%fpsa.length]) { 345 | dev.setFramerate(fpsa[i%fpsa.length]); 346 | } 347 | 348 | }); 349 | }; 350 | 351 | module.exports.StopLight = (devices) => { 352 | let ev1 = [ 353 | frames.staticColorFrame({ 354 | r: 0, 355 | g: 0, 356 | b: 0, 357 | }), 358 | frames.staticColorFrame({ 359 | r: 0, 360 | g: 0, 361 | b: 0, 362 | }), 363 | frames.staticColorFrame({ 364 | r: 0, 365 | g: 0, 366 | b: 0, 367 | }), 368 | frames.staticColorFrame({ 369 | r: 0, 370 | g: 0, 371 | b: 0, 372 | }), 373 | frames.staticColorFrame({ 374 | r: 0, 375 | g: 0, 376 | b: 0, 377 | }), 378 | frames.staticColorFrame({ 379 | r: 255, 380 | g: 0, 381 | b: 0, 382 | }), 383 | frames.staticColorFrame({ 384 | r: 255, 385 | g: 0, 386 | b: 0, 387 | }), 388 | frames.staticColorFrame({ 389 | r: 255, 390 | g: 0, 391 | b: 0, 392 | }), 393 | frames.staticColorFrame({ 394 | r: 255, 395 | g: 0, 396 | b: 0, 397 | }), 398 | frames.staticColorFrame({ 399 | r: 255, 400 | g: 0, 401 | b: 0, 402 | }), 403 | ]; 404 | 405 | let ev2 = [ 406 | frames.staticColorFrame({ 407 | r: 0, 408 | g: 0, 409 | b: 0, 410 | }), 411 | frames.staticColorFrame({ 412 | r: 0, 413 | g: 0, 414 | b: 0, 415 | }), 416 | frames.staticColorFrame({ 417 | r: 0, 418 | g: 0, 419 | b: 0, 420 | }), 421 | frames.staticColorFrame({ 422 | r: 255, 423 | g: 255, 424 | b: 0, 425 | }), 426 | frames.staticColorFrame({ 427 | r: 255, 428 | g: 255, 429 | b: 0, 430 | }), 431 | frames.staticColorFrame({ 432 | r: 0, 433 | g: 0, 434 | b: 0, 435 | }), 436 | frames.staticColorFrame({ 437 | r: 0, 438 | g: 0, 439 | b: 0, 440 | }), 441 | frames.staticColorFrame({ 442 | r: 0, 443 | g: 0, 444 | b: 0, 445 | }), 446 | frames.staticColorFrame({ 447 | r: 0, 448 | g: 0, 449 | b: 0, 450 | }), 451 | frames.staticColorFrame({ 452 | r: 0, 453 | g: 0, 454 | b: 0, 455 | }), 456 | ]; 457 | 458 | let ev3 = [ 459 | frames.staticColorFrame({ 460 | r: 0, 461 | g: 255, 462 | b: 0, 463 | }), 464 | frames.staticColorFrame({ 465 | r: 0, 466 | g: 255, 467 | b: 0, 468 | }), 469 | frames.staticColorFrame({ 470 | r: 0, 471 | g: 255, 472 | b: 0, 473 | }), 474 | frames.staticColorFrame({ 475 | r: 0, 476 | g: 0, 477 | b: 0, 478 | }), 479 | frames.staticColorFrame({ 480 | r: 0, 481 | g: 0, 482 | b: 0, 483 | }), 484 | frames.staticColorFrame({ 485 | r: 0, 486 | g: 0, 487 | b: 0, 488 | }), 489 | frames.staticColorFrame({ 490 | r: 0, 491 | g: 0, 492 | b: 0, 493 | }), 494 | frames.staticColorFrame({ 495 | r: 0, 496 | g: 0, 497 | b: 0, 498 | }), 499 | frames.staticColorFrame({ 500 | r: 0, 501 | g: 0, 502 | b: 0, 503 | }), 504 | frames.staticColorFrame({ 505 | r: 0, 506 | g: 0, 507 | b: 0, 508 | }), 509 | ]; 510 | 511 | if (devices.length >= 1) 512 | devices[0].eventStack = ev1; 513 | if (devices.length >= 2) 514 | devices[1].eventStack = ev2; 515 | if (devices.length >= 3) 516 | devices[2].eventStack = ev3; 517 | }; 518 | 519 | 520 | // 3 frame animation for 3 fans where a red circle meets 521 | // a blue circle and becomes purple, really looks pink 522 | module.exports.RedBlueCollision = (devices) => { 523 | let ev1 = [ 524 | frames.staticColorFrame({ 525 | r: 255, 526 | g: 0, 527 | b: 0, 528 | }), 529 | frames.blankFrame(), 530 | frames.blankFrame(), 531 | ]; 532 | 533 | let ev2 = [ 534 | frames.blankFrame(), 535 | { 536 | r: [ 537 | 0xff, 538 | 0xff, 539 | 0x00, 540 | 0xff, 541 | 0xff, 542 | 0xff, 543 | 0x00, 544 | 0x00, 545 | 0x00, 546 | 0x00, 547 | 0x00, 548 | 0xff, 549 | 0xff, 550 | 0xff, 551 | 0xff, 552 | 0xff 553 | ], 554 | g: [ 555 | 0x00, 556 | 0x00, 557 | 0x00, 558 | 0x00, 559 | 0x00, 560 | 0x00, 561 | 0x00, 562 | 0x00, 563 | 0x00, 564 | 0x00, 565 | 0x00, 566 | 0x00, 567 | 0x00, 568 | 0x00, 569 | 0x00, 570 | 0x00 571 | ], 572 | b: [ 573 | 0x00, 574 | 0xff, 575 | 0xff, 576 | 0xff, 577 | 0x00, 578 | 0xff, 579 | 0xff, 580 | 0xff, 581 | 0xff, 582 | 0xff, 583 | 0xff, 584 | 0xff, 585 | 0x00, 586 | 0x00, 587 | 0x00, 588 | 0x00 589 | ], 590 | }, 591 | frames.staticColorFrame({ 592 | r: 255, 593 | g: 0, 594 | b: 255 595 | }) 596 | ]; 597 | 598 | let ev3 = [ 599 | frames.staticColorFrame({ 600 | r: 0, 601 | g: 0, 602 | b: 255, 603 | }), 604 | frames.blankFrame(), 605 | frames.blankFrame(), 606 | ]; 607 | 608 | if (devices.length >= 1) 609 | devices[0].eventStack = ev1; 610 | if (devices.length >= 2) 611 | devices[1].eventStack = ev2; 612 | if (devices.length >= 3) 613 | devices[2].eventStack = ev3; 614 | }; 615 | -------------------------------------------------------------------------------- /lib/commander.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | 3 | const usb = require('usb'); 4 | 5 | const LightingNodePro = require('./lighting_node_pro.js'); 6 | 7 | module.exports = class Commander { 8 | constructor() { 9 | } 10 | 11 | scanForController(bus, address) { 12 | let devices = usb.getDeviceList(); 13 | 14 | let foundDevices = devices.filter(d => { 15 | return d.deviceDescriptor.idVendor === 0x1b1c && d.deviceDescriptor.idProduct === 0x0c0b; 16 | }); 17 | 18 | let devStrings = foundDevices.map(d => `(Bus ${d.busNumber} Address ${d.deviceAddress})`); 19 | 20 | if (devStrings.length > 0) { 21 | console.log(`Found devices on ${devStrings.join(', ')}`); 22 | } 23 | 24 | let target = foundDevices.find(dev => ((!bus && !address) || (dev.busNumber == bus || dev.deviceAddress == address))); 25 | 26 | if (target) { 27 | console.log(`Found Lighting Node Pro (1b1c:0c0b)`); 28 | return new LightingNodePro(target); 29 | } 30 | 31 | return null; 32 | } 33 | }; 34 | -------------------------------------------------------------------------------- /lib/frames.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | 3 | let rgbColor = require('rgb-color'); 4 | 5 | const flipFrame = module.exports.flipFrame = frame => { 6 | let nf = { 7 | r: Array(16), 8 | g: Array(16), 9 | b: Array(16), 10 | }; 11 | 12 | ['r', 'g', 'b'].forEach(color => { 13 | for (let i = 0; i < frame[color].length; i++) { 14 | // Inner ring 15 | if (i < 4) { 16 | nf[color][i] = frame[color][(i+2)%4]; 17 | } 18 | else { 19 | nf[color][i] = frame[color][(((i-4)+6)%12)+4]; 20 | } 21 | } 22 | }); 23 | 24 | return nf; 25 | }; 26 | 27 | 28 | module.exports.flipFrames = frameArray => frameArray.map(flipFrame); 29 | 30 | module.exports.descriptiveToFrame = (obj) => { 31 | return { 32 | r: [ 33 | obj.topInner && rgbColor(obj.topInner).r || 0x00, // 0 topInner 34 | obj.leftInner && rgbColor(obj.leftInner).r || 0x00, // 1 leftInner 35 | obj.bottomInner && rgbColor(obj.bottomInner).r || 0x00, // 2 bottomInner 36 | obj.rightInner && rgbColor(obj.rightInner).r || 0x00, // 3 rightInner 37 | obj.tenOClock && rgbColor(obj.tenOClock).r || 0x00, // 4 tenOClock 38 | obj.nineOClock && rgbColor(obj.nineOClock).r || 0x00, // 5 nineOClock 39 | obj.eightOClock && rgbColor(obj.eightOClock).r || 0x00, // 6 eightOClock 40 | obj.sevenOClock && rgbColor(obj.sevenOClock).r || 0x00, // 7 sevenOClock 41 | obj.sixOClock && rgbColor(obj.sixOClock).r || 0x00, // 8 sixOClock 42 | obj.fiveOClock && rgbColor(obj.fiveOClock).r || 0x00, // 9 fiveOClock 43 | obj.fourOClock && rgbColor(obj.fourOClock).r || 0x00, // 10 fourOClock 44 | obj.threeOClock && rgbColor(obj.threeOClock).r || 0x00, // 11 threeOClock 45 | obj.twoOClock && rgbColor(obj.twoOClock).r || 0x00, // 12 twoOClock 46 | obj.oneOClock && rgbColor(obj.oneOClock).r || 0x00, // 13 oneOClock 47 | obj.twelveOClock && rgbColor(obj.twelveOClock).r || 0x00, // 14 twelveOClock 48 | obj.elevenOClock && rgbColor(obj.elevenOClock).r || 0x00, // 15 elevenOClock 49 | ], 50 | g: [ 51 | obj.topInner && rgbColor(obj.topInner).g || 0x00, // 0 topInner 52 | obj.leftInner && rgbColor(obj.leftInner).g || 0x00, // 1 leftInner 53 | obj.bottomInner && rgbColor(obj.bottomInner).g || 0x00, // 2 bottomInner 54 | obj.rightInner && rgbColor(obj.rightInner).g || 0x00, // 3 rightInner 55 | obj.tenOClock && rgbColor(obj.tenOClock).g || 0x00, // 4 tenOClock 56 | obj.nineOClock && rgbColor(obj.nineOClock).g || 0x00, // 5 nineOClock 57 | obj.eightOClock && rgbColor(obj.eightOClock).g || 0x00, // 6 eightOClock 58 | obj.sevenOClock && rgbColor(obj.sevenOClock).g || 0x00, // 7 sevenOClock 59 | obj.sixOClock && rgbColor(obj.sixOClock).g || 0x00, // 8 sixOClock 60 | obj.fiveOClock && rgbColor(obj.fiveOClock).g || 0x00, // 9 fiveOClock 61 | obj.fourOClock && rgbColor(obj.fourOClock).g || 0x00, // 10 fourOClock 62 | obj.threeOClock && rgbColor(obj.threeOClock).g || 0x00, // 11 threeOClock 63 | obj.twoOClock && rgbColor(obj.twoOClock).g || 0x00, // 12 twoOClock 64 | obj.oneOClock && rgbColor(obj.oneOClock).g || 0x00, // 13 oneOClock 65 | obj.twelveOClock && rgbColor(obj.twelveOClock).g || 0x00, // 14 twelveOClock 66 | obj.elevenOClock && rgbColor(obj.elevenOClock).g || 0x00, // 15 elevenOClock 67 | ], 68 | b: [ 69 | obj.topInner && rgbColor(obj.topInner).b || 0x00, // 0 topInner 70 | obj.leftInner && rgbColor(obj.leftInner).b || 0x00, // 1 leftInner 71 | obj.bottomInner && rgbColor(obj.bottomInner).b || 0x00, // 2 bottomInner 72 | obj.rightInner && rgbColor(obj.rightInner).b || 0x00, // 3 rightInner 73 | obj.tenOClock && rgbColor(obj.tenOClock).b || 0x00, // 4 tenOClock 74 | obj.nineOClock && rgbColor(obj.nineOClock).b || 0x00, // 5 nineOClock 75 | obj.eightOClock && rgbColor(obj.eightOClock).b || 0x00, // 6 eightOClock 76 | obj.sevenOClock && rgbColor(obj.sevenOClock).b || 0x00, // 7 sevenOClock 77 | obj.sixOClock && rgbColor(obj.sixOClock).b || 0x00, // 8 sixOClock 78 | obj.fiveOClock && rgbColor(obj.fiveOClock).b || 0x00, // 9 fiveOClock 79 | obj.fourOClock && rgbColor(obj.fourOClock).b || 0x00, // 10 fourOClock 80 | obj.threeOClock && rgbColor(obj.threeOClock).b || 0x00, // 11 threeOClock 81 | obj.twoOClock && rgbColor(obj.twoOClock).b || 0x00, // 12 twoOClock 82 | obj.oneOClock && rgbColor(obj.oneOClock).b || 0x00, // 13 oneOClock 83 | obj.twelveOClock && rgbColor(obj.twelveOClock).b || 0x00, // 14 twelveOClock 84 | obj.elevenOClock && rgbColor(obj.elevenOClock).b || 0x00, // 15 elevenOClock 85 | ], 86 | }; 87 | }; 88 | 89 | module.exports.colorfulFrame = () => { 90 | return { 91 | r: [ 92 | 0xff, 93 | 0x00, 94 | 0x00, 95 | 0xff, 96 | 0x00, 97 | 0x00, 98 | 0xff, 99 | 0x00, 100 | 0x00, 101 | 0xff, 102 | 0x00, 103 | 0x00, 104 | 0xff, 105 | 0x00, 106 | 0x00, 107 | 0xff 108 | ], 109 | g: [ 110 | 0x00, 111 | 0xff, 112 | 0x00, 113 | 0x00, 114 | 0xff, 115 | 0x00, 116 | 0x00, 117 | 0xff, 118 | 0x00, 119 | 0x00, 120 | 0xff, 121 | 0x00, 122 | 0x00, 123 | 0xff, 124 | 0x00, 125 | 0x00 126 | ], 127 | b: [ 128 | 0x00, 129 | 0x00, 130 | 0xff, 131 | 0x00, 132 | 0x00, 133 | 0xff, 134 | 0x00, 135 | 0x00, 136 | 0xff, 137 | 0x00, 138 | 0x00, 139 | 0xff, 140 | 0x00, 141 | 0x00, 142 | 0xff, 143 | 0x00 144 | ], 145 | }; 146 | }; 147 | 148 | 149 | module.exports.blankFrame = () => { 150 | return { 151 | r: [ 152 | 0x00, 153 | 0x00, 154 | 0x00, 155 | 0x00, 156 | 0x00, 157 | 0x00, 158 | 0x00, 159 | 0x00, 160 | 0x00, 161 | 0x00, 162 | 0x00, 163 | 0x00, 164 | 0x00, 165 | 0x00, 166 | 0x00, 167 | 0x00 168 | ], 169 | g: [ 170 | 0x00, 171 | 0x00, 172 | 0x00, 173 | 0x00, 174 | 0x00, 175 | 0x00, 176 | 0x00, 177 | 0x00, 178 | 0x00, 179 | 0x00, 180 | 0x00, 181 | 0x00, 182 | 0x00, 183 | 0x00, 184 | 0x00, 185 | 0x00 186 | ], 187 | b: [ 188 | 0x00, 189 | 0x00, 190 | 0x00, 191 | 0x00, 192 | 0x00, 193 | 0x00, 194 | 0x00, 195 | 0x00, 196 | 0x00, 197 | 0x00, 198 | 0x00, 199 | 0x00, 200 | 0x00, 201 | 0x00, 202 | 0x00, 203 | 0x00 204 | ], 205 | }; 206 | }; 207 | 208 | module.exports.staticColorFrame = (rgb) => { 209 | let frame = {}; 210 | 211 | frame.r = Array(16).fill(rgb.r); 212 | frame.g = Array(16).fill(rgb.g); 213 | frame.b = Array(16).fill(rgb.b); 214 | 215 | return frame; 216 | } 217 | 218 | -------------------------------------------------------------------------------- /lib/lighting_node_pro.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | const usb = require('usb'); 3 | 4 | const LLFan = require('./ll_fan.js'); 5 | 6 | const animations = require('./animations.js'); 7 | 8 | module.exports = class LightingNodePro { 9 | constructor(usbDevice) { 10 | this.usbDevice = usbDevice; 11 | this.iface = null; 12 | this.outEndpoint = null; 13 | this.inEndpoint = null; 14 | 15 | this.debug = false; 16 | 17 | this.kernelDriverWasAttached = false; 18 | this.devices = []; 19 | 20 | this.exitLoop = false; 21 | } 22 | 23 | resetDevice() { 24 | return new Promise((resolve, reject) => { 25 | this.usbDevice.reset(err => { 26 | console.log("Device reset"); 27 | if (err) 28 | return reject(err); 29 | 30 | return resolve(); 31 | }); 32 | }); 33 | } 34 | 35 | async initialize(numFans, debug, reset) { 36 | this.debug = debug || false; 37 | 38 | this.numDevices = numFans; 39 | this.numDevicesShort = parseInt(numFans)<<4; 40 | 41 | for (let i = 0; i < this.numDevices; i++) { 42 | let dev = new LLFan(); 43 | this.devices.push(dev); 44 | 45 | dev.setFramerate(1); 46 | } 47 | 48 | console.log("Opening USB port."); 49 | this.usbDevice.open(); 50 | 51 | if (reset) { 52 | console.log("Resetting device"); 53 | await this.resetDevice(); 54 | 55 | console.log("Waiting for 2 seconds."); 56 | await this.delay(2000); 57 | 58 | console.log("Opening USB port again."); 59 | this.usbDevice.open(); 60 | } 61 | 62 | this.iface = this.usbDevice.interface(0); 63 | 64 | if (this.iface.isKernelDriverActive()) { 65 | console.log("Detaching kernel driver"); 66 | this.kernelDriverWasAttached = true; 67 | this.iface.detachKernelDriver(); 68 | } 69 | 70 | console.log("Claiming interface."); 71 | this.iface.claim(); 72 | 73 | this.outEndpoint = this.iface.endpoint(1); 74 | } 75 | 76 | 77 | async sendPreamble() { 78 | if (this.debug) 79 | console.log("Sending preamble"); 80 | 81 | await this.sendPkt([0x37]); 82 | 83 | // 0x35 - init 84 | await this.sendPkt([0x35, 0x00, 0x00, this.numDevicesShort, 0x00, 0x01, 0x01]); 85 | await this.sendPkt([0x3b, 0x00, 0x01]); 86 | await this.sendPkt([0x38, 0x00, 0x02]); 87 | await this.sendPkt([0x34]); 88 | await this.sendPkt([0x37, 0x01]); 89 | await this.sendPkt([0x34, 0x01]); 90 | await this.sendPkt([0x38, 0x01, 0x01]); 91 | await this.sendPkt([0x33, 0xff]); 92 | } 93 | 94 | delay(ms) { 95 | return new Promise(resolve => setTimeout(resolve, ms)); 96 | } 97 | 98 | prettyPrint(byteArray) { 99 | let chq = byteArray; //this.make64(byteArray); 100 | console.log(`[${chq.map(bv => `'${bv.toString(16)}'`).join(', ')}]`); 101 | }; 102 | 103 | setColors() { 104 | // s, t, u are R, G, B extended for fans 3-6 105 | ['r', 'g', 'b', 's', 't', 'u'].forEach(cval => { 106 | let bytes = []; 107 | 108 | if (['r','g','b'].indexOf(cval) !== -1) { 109 | bytes.push(0x32); 110 | bytes.push(0x00); 111 | bytes.push(0x00); 112 | bytes.push(0x32); 113 | 114 | if (cval == 'r') 115 | bytes.push(0x00); 116 | else if (cval == 'g') 117 | bytes.push(0x01); 118 | else if (cval == 'b') 119 | bytes.push(0x02); 120 | 121 | for (let i = 0; i < this.numDevices && i < 3; i++) { 122 | bytes = bytes.concat(this.devices[i].getFrame()[cval]); 123 | } 124 | 125 | // If we have 4 or more fans, we need to take 2 bytes from the 4th fan and put 126 | // it on this message. 127 | if (this.numDevices > 3) { 128 | bytes = bytes.concat(this.devices[3].getFrame()[cval].slice(0, 3)); 129 | } 130 | } else { 131 | bytes.push(0x32); 132 | bytes.push(0x00); 133 | bytes.push(0x32); 134 | bytes.push(0x2e); 135 | 136 | let check; 137 | 138 | if (cval == 's') { 139 | bytes.push(0x00); 140 | check = 'r'; 141 | } 142 | else if (cval == 't') { 143 | bytes.push(0x01); 144 | check = 'g'; 145 | } 146 | else if (cval == 'u') { 147 | bytes.push(0x02); 148 | check = 'b'; 149 | } 150 | 151 | if (this.numDevices > 3) { 152 | // Continuation of 4th fan from r/g/b runs 153 | bytes = bytes.concat(this.devices[3].getFrame()[check].slice(3)); 154 | 155 | for (let i = 4; i < this.numDevices && i < 6; i++) { 156 | bytes = bytes.concat(this.devices[i].getFrame()[check]); 157 | } 158 | } 159 | } 160 | 161 | this.sendPkt(bytes); 162 | }); 163 | } 164 | 165 | 166 | sendPkt(byteArray) { 167 | if (this.debug) 168 | this.prettyPrint(byteArray); 169 | 170 | let fullByteArray = this.make64(byteArray); 171 | 172 | return new Promise((resolve, reject) => { 173 | this.outEndpoint.transfer(Buffer.from(fullByteArray), err => { 174 | if (err) { 175 | return reject(err); 176 | } 177 | 178 | return resolve(); 179 | }); 180 | }); 181 | } 182 | 183 | make64(byteArray) { 184 | while (byteArray.length < 64) { 185 | byteArray.push(0x00); 186 | } 187 | 188 | return byteArray; 189 | } 190 | 191 | async loop() { 192 | await this.sendPreamble(); 193 | 194 | while (!this.exitLoop) { 195 | if (this.devices.some(d => d.changed)) { 196 | await this.setColors(); 197 | } 198 | 199 | await this.sendPkt([0x33, 0xff]); 200 | } 201 | } 202 | 203 | async shutdown() { 204 | this.numDevices = 0; 205 | this.numDevicesShort = 0; 206 | 207 | await this.sendPreamble(); 208 | // await this.setColors(); 209 | //await this.sendPkt([0x33, 0xff]); 210 | 211 | // Reattach like a nice program 212 | // if we detached it. 213 | if (this.kernelDriverWasAttached) { 214 | this.iface.attachKernelDriver(); 215 | } 216 | 217 | return new Promise((resolve, reject) => { 218 | this.iface.release(true, err => { 219 | if (err) { 220 | console.error(err); 221 | return reject(err); 222 | } 223 | 224 | this.usbDevice.close(); 225 | return resolve(); 226 | }); 227 | }); 228 | } 229 | } 230 | -------------------------------------------------------------------------------- /lib/ll_fan.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | 3 | const frames = require('./frames.js'); 4 | 5 | module.exports = class LLFan { 6 | constructor() { 7 | this.eventStack = []; 8 | 9 | this.framePointer = 0; 10 | this.frameRate = 1; 11 | 12 | this.changed = true; 13 | this.intervalTimer = null; 14 | } 15 | 16 | nextFrame() { 17 | if (this.eventStack.length == 1) 18 | return; 19 | 20 | if ((this.framePointer+1) == this.eventStack.length) { 21 | this.framePointer = 0; 22 | 23 | this.changed = true; 24 | } 25 | else { 26 | this.framePointer++; 27 | 28 | this.changed = true; 29 | } 30 | } 31 | 32 | setFramerate(fps) { 33 | this.frameRate = fps; 34 | 35 | if (this.intervalTimer) { 36 | clearInterval(this.intervalTimer); 37 | } 38 | 39 | this.intervalTimer = setInterval(() => { 40 | this.nextFrame(); 41 | }, Math.floor((1/fps)*1000)); 42 | } 43 | 44 | getFrame() { 45 | if (this.eventStack.length == 0) 46 | return frames.blankFrame(); 47 | 48 | if (this.framePointer >= this.eventStack.length) { 49 | this.framePointer = 0; 50 | } 51 | 52 | 53 | if (this.changed) 54 | this.changed = false; 55 | 56 | return this.eventStack[this.framePointer]; 57 | } 58 | 59 | turnOff() { 60 | this.framePointer = 0; 61 | this.eventStack = [frames.blankFrame()]; 62 | } 63 | 64 | }; 65 | -------------------------------------------------------------------------------- /package-lock.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "lighting-node-pro", 3 | "version": "1.0.0", 4 | "lockfileVersion": 1, 5 | "requires": true, 6 | "dependencies": { 7 | "abbrev": { 8 | "version": "1.1.1", 9 | "resolved": "https://registry.npmjs.org/abbrev/-/abbrev-1.1.1.tgz", 10 | "integrity": "sha512-nne9/IiQ/hzIhY6pdDnbBtz7DjPTKrY00P/zvPSm5pOFkl6xuGrGnXn/VtTNNfNtAfZ9/1RtehkszU9qcTii0Q==" 11 | }, 12 | "ansi-regex": { 13 | "version": "2.1.1", 14 | "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-2.1.1.tgz", 15 | "integrity": "sha1-w7M6te42DYbg5ijwRorn7yfWVN8=" 16 | }, 17 | "aproba": { 18 | "version": "1.2.0", 19 | "resolved": "https://registry.npmjs.org/aproba/-/aproba-1.2.0.tgz", 20 | "integrity": "sha512-Y9J6ZjXtoYh8RnXVCMOU/ttDmk1aBjunq9vO0ta5x85WDQiQfUF9sIPBITdbiiIVcBo03Hi3jMxigBtsddlXRw==" 21 | }, 22 | "are-we-there-yet": { 23 | "version": "1.1.5", 24 | "resolved": "https://registry.npmjs.org/are-we-there-yet/-/are-we-there-yet-1.1.5.tgz", 25 | "integrity": "sha512-5hYdAkZlcG8tOLujVDTgCT+uPX0VnpAH28gWsLfzpXYm7wP6mp5Q/gYyR7YQ0cKVJcXJnl3j2kpBan13PtQf6w==", 26 | "requires": { 27 | "delegates": "1.0.0", 28 | "readable-stream": "2.3.6" 29 | } 30 | }, 31 | "argv-tools": { 32 | "version": "0.1.1", 33 | "resolved": "https://registry.npmjs.org/argv-tools/-/argv-tools-0.1.1.tgz", 34 | "integrity": "sha512-Cc0dBvx4dvrjjKpyDA6w8RlNAw8Su30NvZbWl/Tv9ZALEVlLVkWQiHMi84Q0xNfpVuSaiQbYkdmWK8g1PLGhKw==", 35 | "requires": { 36 | "array-back": "2.0.0", 37 | "find-replace": "2.0.1" 38 | } 39 | }, 40 | "array-back": { 41 | "version": "2.0.0", 42 | "resolved": "https://registry.npmjs.org/array-back/-/array-back-2.0.0.tgz", 43 | "integrity": "sha512-eJv4pLLufP3g5kcZry0j6WXpIbzYw9GUB4mVJZno9wfwiBxbizTnHCw3VJb07cBihbFX48Y7oSrW9y+gt4glyw==", 44 | "requires": { 45 | "typical": "2.6.1" 46 | } 47 | }, 48 | "balanced-match": { 49 | "version": "1.0.0", 50 | "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.0.tgz", 51 | "integrity": "sha1-ibTRmasr7kneFk6gK4nORi1xt2c=" 52 | }, 53 | "brace-expansion": { 54 | "version": "1.1.11", 55 | "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", 56 | "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", 57 | "requires": { 58 | "balanced-match": "1.0.0", 59 | "concat-map": "0.0.1" 60 | } 61 | }, 62 | "chownr": { 63 | "version": "1.1.1", 64 | "resolved": "https://registry.npmjs.org/chownr/-/chownr-1.1.1.tgz", 65 | "integrity": "sha512-j38EvO5+LHX84jlo6h4UzmOwi0UgW61WRyPtJz4qaadK5eY3BTS5TY/S1Stc3Uk2lIM6TPevAlULiEJwie860g==" 66 | }, 67 | "code-point-at": { 68 | "version": "1.1.0", 69 | "resolved": "https://registry.npmjs.org/code-point-at/-/code-point-at-1.1.0.tgz", 70 | "integrity": "sha1-DQcLTQQ6W+ozovGkDi7bPZpMz3c=" 71 | }, 72 | "command-line-args": { 73 | "version": "5.0.2", 74 | "resolved": "https://registry.npmjs.org/command-line-args/-/command-line-args-5.0.2.tgz", 75 | "integrity": "sha512-/qPcbL8zpqg53x4rAaqMFlRV4opN3pbla7I7k9x8kyOBMQoGT6WltjN6sXZuxOXw6DgdK7Ad+ijYS5gjcr7vlA==", 76 | "requires": { 77 | "argv-tools": "0.1.1", 78 | "array-back": "2.0.0", 79 | "find-replace": "2.0.1", 80 | "lodash.camelcase": "4.3.0", 81 | "typical": "2.6.1" 82 | } 83 | }, 84 | "concat-map": { 85 | "version": "0.0.1", 86 | "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", 87 | "integrity": "sha1-2Klr13/Wjfd5OnMDajug1UBdR3s=" 88 | }, 89 | "console-control-strings": { 90 | "version": "1.1.0", 91 | "resolved": "https://registry.npmjs.org/console-control-strings/-/console-control-strings-1.1.0.tgz", 92 | "integrity": "sha1-PXz0Rk22RG6mRL9LOVB/mFEAjo4=" 93 | }, 94 | "core-util-is": { 95 | "version": "1.0.2", 96 | "resolved": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.2.tgz", 97 | "integrity": "sha1-tf1UIgqivFq1eqtxQMlAdUUDwac=" 98 | }, 99 | "debug": { 100 | "version": "2.6.9", 101 | "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", 102 | "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", 103 | "requires": { 104 | "ms": "2.0.0" 105 | } 106 | }, 107 | "deep-extend": { 108 | "version": "0.6.0", 109 | "resolved": "https://registry.npmjs.org/deep-extend/-/deep-extend-0.6.0.tgz", 110 | "integrity": "sha512-LOHxIOaPYdHlJRtCQfDIVZtfw/ufM8+rVj649RIHzcm/vGwQRXFt6OPqIFWsm2XEMrNIEtWR64sY1LEKD2vAOA==" 111 | }, 112 | "delegates": { 113 | "version": "1.0.0", 114 | "resolved": "https://registry.npmjs.org/delegates/-/delegates-1.0.0.tgz", 115 | "integrity": "sha1-hMbhWbgZBP3KWaDvRM2HDTElD5o=" 116 | }, 117 | "detect-libc": { 118 | "version": "1.0.3", 119 | "resolved": "https://registry.npmjs.org/detect-libc/-/detect-libc-1.0.3.tgz", 120 | "integrity": "sha1-+hN8S9aY7fVc1c0CrFWfkaTEups=" 121 | }, 122 | "find-replace": { 123 | "version": "2.0.1", 124 | "resolved": "https://registry.npmjs.org/find-replace/-/find-replace-2.0.1.tgz", 125 | "integrity": "sha512-LzDo3Fpa30FLIBsh6DCDnMN1KW2g4QKkqKmejlImgWY67dDFPX/x9Kh/op/GK522DchQXEvDi/wD48HKW49XOQ==", 126 | "requires": { 127 | "array-back": "2.0.0", 128 | "test-value": "3.0.0" 129 | } 130 | }, 131 | "fs-minipass": { 132 | "version": "1.2.5", 133 | "resolved": "https://registry.npmjs.org/fs-minipass/-/fs-minipass-1.2.5.tgz", 134 | "integrity": "sha512-JhBl0skXjUPCFH7x6x61gQxrKyXsxB5gcgePLZCwfyCGGsTISMoIeObbrvVeP6Xmyaudw4TT43qV2Gz+iyd2oQ==", 135 | "requires": { 136 | "minipass": "2.3.5" 137 | } 138 | }, 139 | "fs.realpath": { 140 | "version": "1.0.0", 141 | "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz", 142 | "integrity": "sha1-FQStJSMVjKpA20onh8sBQRmU6k8=" 143 | }, 144 | "gauge": { 145 | "version": "2.7.4", 146 | "resolved": "https://registry.npmjs.org/gauge/-/gauge-2.7.4.tgz", 147 | "integrity": "sha1-LANAXHU4w51+s3sxcCLjJfsBi/c=", 148 | "requires": { 149 | "aproba": "1.2.0", 150 | "console-control-strings": "1.1.0", 151 | "has-unicode": "2.0.1", 152 | "object-assign": "4.1.1", 153 | "signal-exit": "3.0.2", 154 | "string-width": "1.0.2", 155 | "strip-ansi": "3.0.1", 156 | "wide-align": "1.1.3" 157 | } 158 | }, 159 | "glob": { 160 | "version": "7.1.3", 161 | "resolved": "https://registry.npmjs.org/glob/-/glob-7.1.3.tgz", 162 | "integrity": "sha512-vcfuiIxogLV4DlGBHIUOwI0IbrJ8HWPc4MU7HzviGeNho/UJDfi6B5p3sHeWIQ0KGIU0Jpxi5ZHxemQfLkkAwQ==", 163 | "requires": { 164 | "fs.realpath": "1.0.0", 165 | "inflight": "1.0.6", 166 | "inherits": "2.0.3", 167 | "minimatch": "3.0.4", 168 | "once": "1.4.0", 169 | "path-is-absolute": "1.0.1" 170 | } 171 | }, 172 | "has-unicode": { 173 | "version": "2.0.1", 174 | "resolved": "https://registry.npmjs.org/has-unicode/-/has-unicode-2.0.1.tgz", 175 | "integrity": "sha1-4Ob+aijPUROIVeCG0Wkedx3iqLk=" 176 | }, 177 | "iconv-lite": { 178 | "version": "0.4.24", 179 | "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.24.tgz", 180 | "integrity": "sha512-v3MXnZAcvnywkTUEZomIActle7RXXeedOR31wwl7VlyoXO4Qi9arvSenNQWne1TcRwhCL1HwLI21bEqdpj8/rA==", 181 | "requires": { 182 | "safer-buffer": "2.1.2" 183 | } 184 | }, 185 | "ignore-walk": { 186 | "version": "3.0.1", 187 | "resolved": "https://registry.npmjs.org/ignore-walk/-/ignore-walk-3.0.1.tgz", 188 | "integrity": "sha512-DTVlMx3IYPe0/JJcYP7Gxg7ttZZu3IInhuEhbchuqneY9wWe5Ojy2mXLBaQFUQmo0AW2r3qG7m1mg86js+gnlQ==", 189 | "requires": { 190 | "minimatch": "3.0.4" 191 | } 192 | }, 193 | "inflight": { 194 | "version": "1.0.6", 195 | "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz", 196 | "integrity": "sha1-Sb1jMdfQLQwJvJEKEHW6gWW1bfk=", 197 | "requires": { 198 | "once": "1.4.0", 199 | "wrappy": "1.0.2" 200 | } 201 | }, 202 | "inherits": { 203 | "version": "2.0.3", 204 | "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.3.tgz", 205 | "integrity": "sha1-Yzwsg+PaQqUC9SRmAiSA9CCCYd4=" 206 | }, 207 | "ini": { 208 | "version": "1.3.5", 209 | "resolved": "https://registry.npmjs.org/ini/-/ini-1.3.5.tgz", 210 | "integrity": "sha512-RZY5huIKCMRWDUqZlEi72f/lmXKMvuszcMBduliQ3nnWbx9X/ZBQO7DijMEYS9EhHBb2qacRUMtC7svLwe0lcw==" 211 | }, 212 | "is-fullwidth-code-point": { 213 | "version": "1.0.0", 214 | "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-1.0.0.tgz", 215 | "integrity": "sha1-754xOG8DGn8NZDr4L95QxFfvAMs=", 216 | "requires": { 217 | "number-is-nan": "1.0.1" 218 | } 219 | }, 220 | "isarray": { 221 | "version": "1.0.0", 222 | "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz", 223 | "integrity": "sha1-u5NdSFgsuhaMBoNJV6VKPgcSTxE=" 224 | }, 225 | "lodash.camelcase": { 226 | "version": "4.3.0", 227 | "resolved": "https://registry.npmjs.org/lodash.camelcase/-/lodash.camelcase-4.3.0.tgz", 228 | "integrity": "sha1-soqmKIorn8ZRA1x3EfZathkDMaY=" 229 | }, 230 | "minimatch": { 231 | "version": "3.0.4", 232 | "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.0.4.tgz", 233 | "integrity": "sha512-yJHVQEhyqPLUTgt9B83PXu6W3rx4MvvHvSUvToogpwoGDOUQ+yDrR0HRot+yOCdCO7u4hX3pWft6kWBBcqh0UA==", 234 | "requires": { 235 | "brace-expansion": "1.1.11" 236 | } 237 | }, 238 | "minimist": { 239 | "version": "0.0.8", 240 | "resolved": "http://registry.npmjs.org/minimist/-/minimist-0.0.8.tgz", 241 | "integrity": "sha1-hX/Kv8M5fSYluCKCYuhqp6ARsF0=" 242 | }, 243 | "minipass": { 244 | "version": "2.3.5", 245 | "resolved": "https://registry.npmjs.org/minipass/-/minipass-2.3.5.tgz", 246 | "integrity": "sha512-Gi1W4k059gyRbyVUZQ4mEqLm0YIUiGYfvxhF6SIlk3ui1WVxMTGfGdQ2SInh3PDrRTVvPKgULkpJtT4RH10+VA==", 247 | "requires": { 248 | "safe-buffer": "5.1.2", 249 | "yallist": "3.0.2" 250 | } 251 | }, 252 | "minizlib": { 253 | "version": "1.1.1", 254 | "resolved": "https://registry.npmjs.org/minizlib/-/minizlib-1.1.1.tgz", 255 | "integrity": "sha512-TrfjCjk4jLhcJyGMYymBH6oTXcWjYbUAXTHDbtnWHjZC25h0cdajHuPE1zxb4DVmu8crfh+HwH/WMuyLG0nHBg==", 256 | "requires": { 257 | "minipass": "2.3.5" 258 | } 259 | }, 260 | "mkdirp": { 261 | "version": "0.5.1", 262 | "resolved": "http://registry.npmjs.org/mkdirp/-/mkdirp-0.5.1.tgz", 263 | "integrity": "sha1-MAV0OOrGz3+MR2fzhkjWaX11yQM=", 264 | "requires": { 265 | "minimist": "0.0.8" 266 | } 267 | }, 268 | "ms": { 269 | "version": "2.0.0", 270 | "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", 271 | "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=" 272 | }, 273 | "nan": { 274 | "version": "2.11.1", 275 | "resolved": "https://registry.npmjs.org/nan/-/nan-2.11.1.tgz", 276 | "integrity": "sha512-iji6k87OSXa0CcrLl9z+ZiYSuR2o+c0bGuNmXdrhTQTakxytAFsC56SArGYoiHlJlFoHSnvmhpceZJaXkVuOtA==" 277 | }, 278 | "needle": { 279 | "version": "2.2.4", 280 | "resolved": "https://registry.npmjs.org/needle/-/needle-2.2.4.tgz", 281 | "integrity": "sha512-HyoqEb4wr/rsoaIDfTH2aVL9nWtQqba2/HvMv+++m8u0dz808MaagKILxtfeSN7QU7nvbQ79zk3vYOJp9zsNEA==", 282 | "requires": { 283 | "debug": "2.6.9", 284 | "iconv-lite": "0.4.24", 285 | "sax": "1.2.4" 286 | } 287 | }, 288 | "node-pre-gyp": { 289 | "version": "0.10.3", 290 | "resolved": "https://registry.npmjs.org/node-pre-gyp/-/node-pre-gyp-0.10.3.tgz", 291 | "integrity": "sha512-d1xFs+C/IPS8Id0qPTZ4bUT8wWryfR/OzzAFxweG+uLN85oPzyo2Iw6bVlLQ/JOdgNonXLCoRyqDzDWq4iw72A==", 292 | "requires": { 293 | "detect-libc": "1.0.3", 294 | "mkdirp": "0.5.1", 295 | "needle": "2.2.4", 296 | "nopt": "4.0.1", 297 | "npm-packlist": "1.1.12", 298 | "npmlog": "4.1.2", 299 | "rc": "1.2.8", 300 | "rimraf": "2.6.2", 301 | "semver": "5.6.0", 302 | "tar": "4.4.6" 303 | } 304 | }, 305 | "nopt": { 306 | "version": "4.0.1", 307 | "resolved": "https://registry.npmjs.org/nopt/-/nopt-4.0.1.tgz", 308 | "integrity": "sha1-0NRoWv1UFRk8jHUFYC0NF81kR00=", 309 | "requires": { 310 | "abbrev": "1.1.1", 311 | "osenv": "0.1.5" 312 | } 313 | }, 314 | "npm-bundled": { 315 | "version": "1.0.5", 316 | "resolved": "https://registry.npmjs.org/npm-bundled/-/npm-bundled-1.0.5.tgz", 317 | "integrity": "sha512-m/e6jgWu8/v5niCUKQi9qQl8QdeEduFA96xHDDzFGqly0OOjI7c+60KM/2sppfnUU9JJagf+zs+yGhqSOFj71g==" 318 | }, 319 | "npm-packlist": { 320 | "version": "1.1.12", 321 | "resolved": "https://registry.npmjs.org/npm-packlist/-/npm-packlist-1.1.12.tgz", 322 | "integrity": "sha512-WJKFOVMeAlsU/pjXuqVdzU0WfgtIBCupkEVwn+1Y0ERAbUfWw8R4GjgVbaKnUjRoD2FoQbHOCbOyT5Mbs9Lw4g==", 323 | "requires": { 324 | "ignore-walk": "3.0.1", 325 | "npm-bundled": "1.0.5" 326 | } 327 | }, 328 | "npmlog": { 329 | "version": "4.1.2", 330 | "resolved": "https://registry.npmjs.org/npmlog/-/npmlog-4.1.2.tgz", 331 | "integrity": "sha512-2uUqazuKlTaSI/dC8AzicUck7+IrEaOnN/e0jd3Xtt1KcGpwx30v50mL7oPyr/h9bL3E4aZccVwpwP+5W9Vjkg==", 332 | "requires": { 333 | "are-we-there-yet": "1.1.5", 334 | "console-control-strings": "1.1.0", 335 | "gauge": "2.7.4", 336 | "set-blocking": "2.0.0" 337 | } 338 | }, 339 | "number-is-nan": { 340 | "version": "1.0.1", 341 | "resolved": "https://registry.npmjs.org/number-is-nan/-/number-is-nan-1.0.1.tgz", 342 | "integrity": "sha1-CXtgK1NCKlIsGvuHkDGDNpQaAR0=" 343 | }, 344 | "object-assign": { 345 | "version": "4.1.1", 346 | "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz", 347 | "integrity": "sha1-IQmtx5ZYh8/AXLvUQsrIv7s2CGM=" 348 | }, 349 | "once": { 350 | "version": "1.4.0", 351 | "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", 352 | "integrity": "sha1-WDsap3WWHUsROsF9nFC6753Xa9E=", 353 | "requires": { 354 | "wrappy": "1.0.2" 355 | } 356 | }, 357 | "os-homedir": { 358 | "version": "1.0.2", 359 | "resolved": "https://registry.npmjs.org/os-homedir/-/os-homedir-1.0.2.tgz", 360 | "integrity": "sha1-/7xJiDNuDoM94MFox+8VISGqf7M=" 361 | }, 362 | "os-tmpdir": { 363 | "version": "1.0.2", 364 | "resolved": "https://registry.npmjs.org/os-tmpdir/-/os-tmpdir-1.0.2.tgz", 365 | "integrity": "sha1-u+Z0BseaqFxc/sdm/lc0VV36EnQ=" 366 | }, 367 | "osenv": { 368 | "version": "0.1.5", 369 | "resolved": "https://registry.npmjs.org/osenv/-/osenv-0.1.5.tgz", 370 | "integrity": "sha512-0CWcCECdMVc2Rw3U5w9ZjqX6ga6ubk1xDVKxtBQPK7wis/0F2r9T6k4ydGYhecl7YUBxBVxhL5oisPsNxAPe2g==", 371 | "requires": { 372 | "os-homedir": "1.0.2", 373 | "os-tmpdir": "1.0.2" 374 | } 375 | }, 376 | "path-is-absolute": { 377 | "version": "1.0.1", 378 | "resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz", 379 | "integrity": "sha1-F0uSaHNVNP+8es5r9TpanhtcX18=" 380 | }, 381 | "process-nextick-args": { 382 | "version": "2.0.0", 383 | "resolved": "https://registry.npmjs.org/process-nextick-args/-/process-nextick-args-2.0.0.tgz", 384 | "integrity": "sha512-MtEC1TqN0EU5nephaJ4rAtThHtC86dNN9qCuEhtshvpVBkAW5ZO7BASN9REnF9eoXGcRub+pFuKEpOHE+HbEMw==" 385 | }, 386 | "rc": { 387 | "version": "1.2.8", 388 | "resolved": "https://registry.npmjs.org/rc/-/rc-1.2.8.tgz", 389 | "integrity": "sha512-y3bGgqKj3QBdxLbLkomlohkvsA8gdAiUQlSBJnBhfn+BPxg4bc62d8TcBW15wavDfgexCgccckhcZvywyQYPOw==", 390 | "requires": { 391 | "deep-extend": "0.6.0", 392 | "ini": "1.3.5", 393 | "minimist": "1.2.0", 394 | "strip-json-comments": "2.0.1" 395 | }, 396 | "dependencies": { 397 | "minimist": { 398 | "version": "1.2.0", 399 | "resolved": "http://registry.npmjs.org/minimist/-/minimist-1.2.0.tgz", 400 | "integrity": "sha1-o1AIsg9BOD7sH7kU9M1d95omQoQ=" 401 | } 402 | } 403 | }, 404 | "readable-stream": { 405 | "version": "2.3.6", 406 | "resolved": "http://registry.npmjs.org/readable-stream/-/readable-stream-2.3.6.tgz", 407 | "integrity": "sha512-tQtKA9WIAhBF3+VLAseyMqZeBjW0AHJoxOtYqSUZNJxauErmLbVm2FW1y+J/YA9dUrAC39ITejlZWhVIwawkKw==", 408 | "requires": { 409 | "core-util-is": "1.0.2", 410 | "inherits": "2.0.3", 411 | "isarray": "1.0.0", 412 | "process-nextick-args": "2.0.0", 413 | "safe-buffer": "5.1.2", 414 | "string_decoder": "1.1.1", 415 | "util-deprecate": "1.0.2" 416 | } 417 | }, 418 | "rgb-color": { 419 | "version": "2.1.2", 420 | "resolved": "https://registry.npmjs.org/rgb-color/-/rgb-color-2.1.2.tgz", 421 | "integrity": "sha512-KnyoQtNgZBn1tk9FQNLdFokLK/ozRHXcqI+VWR0AFUQX9OFUdw3Za89vwVGwmQrRBl9/cA3epAeJPiJ6es9gow==" 422 | }, 423 | "rimraf": { 424 | "version": "2.6.2", 425 | "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-2.6.2.tgz", 426 | "integrity": "sha512-lreewLK/BlghmxtfH36YYVg1i8IAce4TI7oao75I1g245+6BctqTVQiBP3YUJ9C6DQOXJmkYR9X9fCLtCOJc5w==", 427 | "requires": { 428 | "glob": "7.1.3" 429 | } 430 | }, 431 | "safe-buffer": { 432 | "version": "5.1.2", 433 | "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", 434 | "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==" 435 | }, 436 | "safer-buffer": { 437 | "version": "2.1.2", 438 | "resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz", 439 | "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==" 440 | }, 441 | "sax": { 442 | "version": "1.2.4", 443 | "resolved": "https://registry.npmjs.org/sax/-/sax-1.2.4.tgz", 444 | "integrity": "sha512-NqVDv9TpANUjFm0N8uM5GxL36UgKi9/atZw+x7YFnQ8ckwFGKrl4xX4yWtrey3UJm5nP1kUbnYgLopqWNSRhWw==" 445 | }, 446 | "semver": { 447 | "version": "5.6.0", 448 | "resolved": "https://registry.npmjs.org/semver/-/semver-5.6.0.tgz", 449 | "integrity": "sha512-RS9R6R35NYgQn++fkDWaOmqGoj4Ek9gGs+DPxNUZKuwE183xjJroKvyo1IzVFeXvUrvmALy6FWD5xrdJT25gMg==" 450 | }, 451 | "set-blocking": { 452 | "version": "2.0.0", 453 | "resolved": "https://registry.npmjs.org/set-blocking/-/set-blocking-2.0.0.tgz", 454 | "integrity": "sha1-BF+XgtARrppoA93TgrJDkrPYkPc=" 455 | }, 456 | "signal-exit": { 457 | "version": "3.0.2", 458 | "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.2.tgz", 459 | "integrity": "sha1-tf3AjxKH6hF4Yo5BXiUTK3NkbG0=" 460 | }, 461 | "string-width": { 462 | "version": "1.0.2", 463 | "resolved": "https://registry.npmjs.org/string-width/-/string-width-1.0.2.tgz", 464 | "integrity": "sha1-EYvfW4zcUaKn5w0hHgfisLmxB9M=", 465 | "requires": { 466 | "code-point-at": "1.1.0", 467 | "is-fullwidth-code-point": "1.0.0", 468 | "strip-ansi": "3.0.1" 469 | } 470 | }, 471 | "string_decoder": { 472 | "version": "1.1.1", 473 | "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz", 474 | "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==", 475 | "requires": { 476 | "safe-buffer": "5.1.2" 477 | } 478 | }, 479 | "strip-ansi": { 480 | "version": "3.0.1", 481 | "resolved": "http://registry.npmjs.org/strip-ansi/-/strip-ansi-3.0.1.tgz", 482 | "integrity": "sha1-ajhfuIU9lS1f8F0Oiq+UJ43GPc8=", 483 | "requires": { 484 | "ansi-regex": "2.1.1" 485 | } 486 | }, 487 | "strip-json-comments": { 488 | "version": "2.0.1", 489 | "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-2.0.1.tgz", 490 | "integrity": "sha1-PFMZQukIwml8DsNEhYwobHygpgo=" 491 | }, 492 | "tar": { 493 | "version": "4.4.6", 494 | "resolved": "https://registry.npmjs.org/tar/-/tar-4.4.6.tgz", 495 | "integrity": "sha512-tMkTnh9EdzxyfW+6GK6fCahagXsnYk6kE6S9Gr9pjVdys769+laCTbodXDhPAjzVtEBazRgP0gYqOjnk9dQzLg==", 496 | "requires": { 497 | "chownr": "1.1.1", 498 | "fs-minipass": "1.2.5", 499 | "minipass": "2.3.5", 500 | "minizlib": "1.1.1", 501 | "mkdirp": "0.5.1", 502 | "safe-buffer": "5.1.2", 503 | "yallist": "3.0.2" 504 | } 505 | }, 506 | "test-value": { 507 | "version": "3.0.0", 508 | "resolved": "https://registry.npmjs.org/test-value/-/test-value-3.0.0.tgz", 509 | "integrity": "sha512-sVACdAWcZkSU9x7AOmJo5TqE+GyNJknHaHsMrR6ZnhjVlVN9Yx6FjHrsKZ3BjIpPCT68zYesPWkakrNupwfOTQ==", 510 | "requires": { 511 | "array-back": "2.0.0", 512 | "typical": "2.6.1" 513 | } 514 | }, 515 | "typical": { 516 | "version": "2.6.1", 517 | "resolved": "https://registry.npmjs.org/typical/-/typical-2.6.1.tgz", 518 | "integrity": "sha1-XAgOXWYcu+OCWdLnCjxyU+hziB0=" 519 | }, 520 | "usb": { 521 | "version": "1.3.3", 522 | "resolved": "https://registry.npmjs.org/usb/-/usb-1.3.3.tgz", 523 | "integrity": "sha512-WRBxI54yEs2QPj28G6kITI3Wu7VxrtHbqiDvDRUDKdg97lcK1pTP8y9LoDWF22OiCCrEvrdeq0lNcr84QOzjXQ==", 524 | "requires": { 525 | "nan": "2.11.1", 526 | "node-pre-gyp": "0.10.3" 527 | } 528 | }, 529 | "util-deprecate": { 530 | "version": "1.0.2", 531 | "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz", 532 | "integrity": "sha1-RQ1Nyfpw3nMnYvvS1KKJgUGaDM8=" 533 | }, 534 | "wide-align": { 535 | "version": "1.1.3", 536 | "resolved": "https://registry.npmjs.org/wide-align/-/wide-align-1.1.3.tgz", 537 | "integrity": "sha512-QGkOQc8XL6Bt5PwnsExKBPuMKBxnGxWWW3fU55Xt4feHozMUhdUMaBCk290qpm/wG5u/RSKzwdAC4i51YigihA==", 538 | "requires": { 539 | "string-width": "1.0.2" 540 | } 541 | }, 542 | "wrappy": { 543 | "version": "1.0.2", 544 | "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", 545 | "integrity": "sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8=" 546 | }, 547 | "yallist": { 548 | "version": "3.0.2", 549 | "resolved": "https://registry.npmjs.org/yallist/-/yallist-3.0.2.tgz", 550 | "integrity": "sha1-hFK0u36Dx8GI2AQcGoN8dz1ti7k=" 551 | } 552 | } 553 | } 554 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "lighting-node-pro", 3 | "version": "1.0.0", 4 | "description": "Lighting Node Pro controller", 5 | "main": "index.js", 6 | "directories": { 7 | "lib": "lib" 8 | }, 9 | "dependencies": { 10 | "command-line-args": "^5.0.2", 11 | "rgb-color": "^2.1.2", 12 | "usb": "^1.3.3" 13 | }, 14 | "devDependencies": {}, 15 | "scripts": { 16 | "test": "echo \"Error: no test specified\" && exit 1" 17 | }, 18 | "keywords": [], 19 | "author": "", 20 | "license": "MIT" 21 | } 22 | --------------------------------------------------------------------------------