diff --git a/README.md b/README.md index f40a169..523740c 100644 --- a/README.md +++ b/README.md @@ -4,23 +4,42 @@ Anne Pro 2 RGB control through Node.js This npm package allows to control the Anne Pro 2 RGB keyboard keys without ObinsKit. Anne Pro 1 compatibility has not been tested as I don't have that keyboard. +Currently, settings are not saved and custom LED graphics set with this module are lost when the keyboard loses power or when user changes the profile with a keyboard button. This works similarly to the profile preview mode in ObinsKit. + ## How to use 1. Install as an npm package 2. `var Annemone = require('annemone')` -3. Annemone is now accessible as a class through a variable +3. `const LEDController = new Annemone()` ## Available methods -### Annemone.setMultiColorLed(arrayOfRgbValues) +### Annemone.LEDController.setMultiColorLed(ledMatrix, fast_mode) Set per-key RBG value. -Takes a flat array of RBG values for each key, left to right, top to bottom, from the Escape key to right Ctrl. - -Example arrayOfRgbValues: `[255, 0, 0, 255, 0, 0, 255, 0, 0, 255, 0, 0, 255, 0, 0, 255, 0, 0, 255, 0, 0, 255, 0, 0, 255, 0, 0, 255, 0, 0, 255, 0, 0, 255, 0, 0, 255, 0, 0, 255, 0, 0, 255, 255, 0, 255, 255, 0, 255, 255, 0, 255, 255, 0, 255, 255, 0, 255, 255, 0, 255, 255, 0, 255, 255, 0, 255, 255, 0, 255, 255, 0, 255, 255, 0, 255, 255, 0, 255, 255, 0, 255, 255, 0, 0, 255, 0, 0, 255, 0, 0, 255, 0, 0, 255, 0, 0, 255, 0, 0, 255, 0, 0, 255, 0, 0, 255, 0, 0, 255, 0, 0, 255, 0, 0, 255, 0, 0, 255, 0, 0, 255, 0, 0, 0, 0, 0, 255, 255, 0, 0, 0, 0, 255, 255, 0, 255, 255, 0, 255, 255, 0, 255, 255, 0, 255, 255, 0, 255, 255, 0, 255, 255, 0, 255, 255, 0, 255, 255, 0, 255, 255, 0, 255, 255, 0, 0, 0, 255, 0, 255, 0, 0, 0, 255, 0, 255, 255, 0, 255, 0, 0, 0, 0, 0, 0, 255, 0, 255, 0, 0, 0, 0, 0, 0, 255, 0, 255, 255, 0, 255, 255, 0, 255, 255, 0, 255, 0, 0, 0]` - -### Annemone.setSingleColorLed(rgb) +Accepts a two-dimensional matrix with RGB values corresponding to each of the 61 keys as they appear on the keyboard. + +There's a 50ms delay between commands, I'm still reverse engineering the music mode. + +Example matrix: +```js +[ + [255, 0, 0] // esc + [255, 0, 0] // 1 + [255, 0, 0] // 2 + ... +], +[ + [255, 0, 0] // tab + [255, 0, 0] // q + [255, 0, 0] // w + ... +] +... +``` + +### Annemone.LEDController.setSingleColorLed(rgb) Set all keys to one color. @@ -28,20 +47,24 @@ Takes an array with 3 elements: red, green and blue. Example rgb: `[255, 0, 0]` -### Annemone.generateMultiColor(arrayOfRgbValues, usb_host = 65, command_info = [32, 3, 255, 2]) +### Annemone.LEDController.generateMultiColor(arrayOfRgbValues, mcu_address = 65, command_info = [32, 3, 255, 2]) HID packet generator for `setMultiColorLed`. -- arrayOfRgbValues: same as for `setMultiColorLed` -- usb_host: MCU address? Possibly depends on whether this is an Anne Pro or Anne Pro 2, can only be 65 or 49 -- command_info: contents unknown, more research needed +- arrayOfRgbValues: 70 sequential rgb values for each key (skip the following keys as they do not exist on the board: *41, 43, 55, 57, 60, 61, 63, 64*) +- mcu_address: not sure what an MCU is internally, possibly depends on whether this is an Anne Pro or Anne Pro 2, code suggests this can only be 65 or 49 +- command_info: + - 32: appears to select the destination (32 is LED) + - 3: LED command type selector + - 255: unknown, doesn't do anything + - 2: selects a preset lighting profile (2 is static rainbow for example) -### Annemone.generateOneColor(rbg_color, usb_host = 65) +### Annemone.LEDController.generateOneColor(rbg_color, mcu_address = 65) HID packet generator for `setSingleColorLed`. -### Annemone.write(message) +### Annemone.LEDController.write(message) -HID message write wrapper with forced delay. +HID write wrapper with a forced delay. Needed due to Anne Pro 2 ignoring commands when they're sent faster than 50ms apart from each other. \ No newline at end of file diff --git a/index.js b/index.js index c1b693c..f9c38a7 100644 --- a/index.js +++ b/index.js @@ -1,5 +1,5 @@ // -// Annemone v0.0.1 +// Annemone v0.1.1 // var HID = require('node-hid'); @@ -23,19 +23,96 @@ class LEDController { return new HID.HID(keyboard_info.path); } })(); + this.AnnePro2Layout = [ + { key: 'esc', matrix_id: 0 }, + { key: '1', matrix_id: 1 }, + { key: '2', matrix_id: 2 }, + { key: '3', matrix_id: 3 }, + { key: '4', matrix_id: 4 }, + { key: '5', matrix_id: 5 }, + { key: '6', matrix_id: 6 }, + { key: '7', matrix_id: 7 }, + { key: '8', matrix_id: 8 }, + { key: '9', matrix_id: 9 }, + { key: '0', matrix_id: 10 }, + { key: 'minus', matrix_id: 11 }, + { key: 'equals', matrix_id: 12 }, + { key: 'backspace', matrix_id: 13 }, + { key: 'tab', matrix_id: 14 }, + { key: 'q', matrix_id: 15 }, + { key: 'w', matrix_id: 16 }, + { key: 'e', matrix_id: 17 }, + { key: 'r', matrix_id: 18 }, + { key: 't', matrix_id: 19 }, + { key: 'y', matrix_id: 20 }, + { key: 'u', matrix_id: 21 }, + { key: 'i', matrix_id: 22 }, + { key: 'o', matrix_id: 23 }, + { key: 'p', matrix_id: 24 }, + { key: 'lbracket', matrix_id: 25 }, + { key: 'rbracket', matrix_id: 26 }, + { key: 'backslash', matrix_id: 27 }, + { key: 'caps', matrix_id: 28 }, + { key: 'a', matrix_id: 29 }, + { key: 's', matrix_id: 30 }, + { key: 'd', matrix_id: 31 }, + { key: 'f', matrix_id: 32 }, + { key: 'g', matrix_id: 33 }, + { key: 'h', matrix_id: 34 }, + { key: 'j', matrix_id: 35 }, + { key: 'k', matrix_id: 36 }, + { key: 'l', matrix_id: 37 }, + { key: 'semicolon', matrix_id: 38 }, + { key: 'apostrophe', matrix_id: 39 }, + { key: 'enter', matrix_id: 40 }, + { key: 'deadkey1', matrix_id: null }, + { key: 'leftshift', matrix_id: 41 }, + { key: 'deadkey2', matrix_id: null }, + { key: 'z', matrix_id: 42 }, + { key: 'x', matrix_id: 43 }, + { key: 'c', matrix_id: 44 }, + { key: 'v', matrix_id: 45 }, + { key: 'b', matrix_id: 46 }, + { key: 'n', matrix_id: 47 }, + { key: 'm', matrix_id: 48 }, + { key: 'comma', matrix_id: 49 }, + { key: 'dot', matrix_id: 50 }, + { key: 'slash', matrix_id: 51 }, + { key: 'rightshift', matrix_id: 52 }, + { key: 'deadkey3', matrix_id: null }, + { key: 'leftctrl', matrix_id: 53 }, + { key: 'deadkey4', matrix_id: null }, + { key: 'leftsuper', matrix_id: 54 }, + { key: 'leftalt', matrix_id: 55 }, + { key: 'deadkey5', matrix_id: null }, + { key: 'daedkey6', matrix_id: null }, + { key: 'space', matrix_id: 56 }, + { key: 'deadkey7', matrix_id: null }, + { key: 'deadkey8', matrix_id: null }, + { key: 'rightalt', matrix_id: 57 }, + { key: 'fn', matrix_id: 58 }, + { key: 'context', matrix_id: 59 }, + { key: 'rightctrl', matrix_id: 60 }, + { key: 'deadkey9', matrix_id: null }, + ]; } - setMultiColorLed(arrayOfRgbValues) { - const messages = this.generateMultiColor(arrayOfRgbValues); + setMultiColorLed(ledMatrix) { + const ledMatrixFlattened = ledMatrix.flat(); + const arrayOfRgbValues = this.AnnePro2Layout.map((key) => { + if (key.matrix_id === null) { + return [0, 0, 0]; + } else { + return ledMatrixFlattened[key.matrix_id]; + } + }); + const messages = this.generateMultiColor(arrayOfRgbValues.flat()); + let sentBytes = 0; for (let i = 0; i < messages.length; i++) { sentBytes += this.write(messages[i]); } return sentBytes; - // for (let i = 0; i < messages.length; i++) { - // this.keyboard.write(messages[i]); - // console.log(messages[i]) - // } } setSingleColorLed(rgb) { @@ -45,11 +122,11 @@ class LEDController { // Reverse engineered from ObinsKit generateMultiColor( arrayOfRgbValues, - usb_host = 65, + mcu_address = 65, command_info = [32, 3, 255, 2] ) { const maxMessageLength = 55 - command_info.length, - service_data = [0, 123, 16, usb_host], + service_data = [0, 123, 16, mcu_address], static_message = [0, 0, 125], arrayOfRgbValuesCopy = arrayOfRgbValues.slice(0), messagesToSendAmount = Math.ceil( @@ -75,16 +152,15 @@ class LEDController { return hid_command; } - generateOneColor(rbg_color, usb_host = 65) { - return [0, 123, 16, usb_host, 16, 7, 0, 0, 125, 32, 3, 255, 1].concat( + generateOneColor(rbg_color, mcu_address = 65) { + return [0, 123, 16, mcu_address, 16, 7, 0, 0, 125, 32, 3, 255, 1].concat( rbg_color ); } - // This is required because the keyboard doesn't understand commands sent faster than 50ms apart write(message) { const startTime = new Date().getTime(); - while (new Date().getTime() < startTime + 50); // TODO: refactor + while (new Date().getTime() < startTime + 50); return this.keyboard.write(message); }