elgato-stream-deck
Advanced tools
Comparing version 1.1.0 to 1.2.0
@@ -0,1 +1,22 @@ | ||
<a name="1.2.0"></a> | ||
# [1.2.0](https://github.com/Lange/node-elgato-stream-deck/compare/v1.1.0...v1.2.0) (2017-06-23) | ||
### Features | ||
* add `clearKey` method #4 | ||
* add Typescript typings #13 | ||
* add `setBrightness` and `sendFeatureReport` [4d904f0](https://github.com/Lange/node-elgato-stream-deck/commit/4d904f0c7d40154186914599d877b5879179d48b) | ||
### Bug Fixes | ||
* throw an error when no stream decks are present [c44a1bf](https://github.com/Lange/node-elgato-stream-deck/commit/c44a1bf3ae51bfdc7e9963f131a2ce02746b2975 | ||
) | ||
* fix device detection on linux [e0b128c](https://github.com/Lange/node-elgato-stream-deck/commit/e0b128c82aa6e5075e3f8a77d9fca43103b83bc4) | ||
* `fillImage` fix blue and red channels being swapped [8efdb6b](https://github.com/Lange/node-elgato-stream-deck/commit/8efdb6bf0cb1fde3920c850c6b57d25e56648e09) | ||
### Misc | ||
* Full test coverage | ||
<a name="1.1.0"></a> | ||
@@ -2,0 +23,0 @@ # [1.1.0](https://github.com/Lange/node-elgato-stream-deck/compare/v1.0.0...v1.1.0) (2017-05-18) |
182
index.js
@@ -16,8 +16,9 @@ 'use strict'; | ||
const NUM_TOTAL_PIXELS = NUM_FIRST_PAGE_PIXELS + NUM_SECOND_PAGE_PIXELS; | ||
const keyState = new Array(NUM_KEYS).fill(false); | ||
const devices = HID.devices(); | ||
const connectedStreamDecks = devices.filter(device => { | ||
return device.product === 'Stream Deck' && device.manufacturer === 'Elgato Systems'; | ||
return device.vendorId === 0x0fd9 && device.productId === 0x0060; | ||
}); | ||
/* istanbul ignore if */ | ||
if (connectedStreamDecks.length > 1) { | ||
@@ -27,2 +28,7 @@ throw new Error('More than one Stream Deck is connected. This is unsupported at this time.'); | ||
/* istanbul ignore if */ | ||
if (connectedStreamDecks.length < 1) { | ||
throw new Error('No Stream Decks are connected.'); | ||
} | ||
class StreamDeck extends EventEmitter { | ||
@@ -32,2 +38,3 @@ constructor(device) { | ||
this.device = device; | ||
this.keyState = new Array(NUM_KEYS).fill(false); | ||
@@ -41,3 +48,3 @@ this.device.on('data', data => { | ||
const keyPressed = Boolean(data[i]); | ||
if (keyPressed !== keyState[i]) { | ||
if (keyPressed !== this.keyState[i]) { | ||
if (keyPressed) { | ||
@@ -50,3 +57,3 @@ this.emit('down', i); | ||
keyState[i] = keyPressed; | ||
this.keyState[i] = keyPressed; | ||
} | ||
@@ -60,2 +67,8 @@ }); | ||
/** | ||
* Writes a Buffer to the Stream Deck. | ||
* | ||
* @param {Buffer} buffer The buffer written to the Stream Deck | ||
* @returns undefined | ||
*/ | ||
write(buffer) { | ||
@@ -65,3 +78,27 @@ return this.device.write(StreamDeck.bufferToIntArray(buffer)); | ||
/** | ||
* Sends a HID feature report to the Stream Deck. | ||
* | ||
* @param {Buffer} buffer The buffer send to the Stream Deck. | ||
* @returns undefined | ||
*/ | ||
sendFeatureReport(buffer) { | ||
return this.device.sendFeatureReport(StreamDeck.bufferToIntArray(buffer)); | ||
} | ||
/** | ||
* Fills the given key with a solid color. | ||
* | ||
* @param {number} keyIndex The key to fill 0 - 14 | ||
* @param {number} r The color's red value. 0 - 255 | ||
* @param {number} g The color's green value. 0 - 255 | ||
* @param {number} b The color's blue value. 0 -255 | ||
*/ | ||
fillColor(keyIndex, r, g, b) { | ||
StreamDeck.checkValidKeyIndex(keyIndex); | ||
StreamDeck.checkRGBValue(r); | ||
StreamDeck.checkRGBValue(g); | ||
StreamDeck.checkRGBValue(b); | ||
const pixel = Buffer.from([b, g, r]); | ||
@@ -72,5 +109,37 @@ this._writePage1(keyIndex, Buffer.alloc(NUM_FIRST_PAGE_PIXELS * 3, pixel)); | ||
/** | ||
* Checks a value is a valid RGB value. A number between 0 and 255. | ||
* | ||
* @static | ||
* @param {number} value The number to check | ||
*/ | ||
static checkRGBValue(value) { | ||
if (value < 0 || value > 255) { | ||
throw new TypeError('Expected a valid color RGB value 0 - 255'); | ||
} | ||
} | ||
/** | ||
* Checks a keyIndex is a valid key for a stream deck. A number between 0 and 14. | ||
* | ||
* @static | ||
* @param {number} keyIndex The keyIndex to check | ||
*/ | ||
static checkValidKeyIndex(keyIndex) { | ||
if (keyIndex < 0 || keyIndex > 14) { | ||
throw new TypeError('Expected a valid keyIndex 0 - 14'); | ||
} | ||
} | ||
/** | ||
* Fills the given key with an image in a Buffer. | ||
* | ||
* @param {number} keyIndex The key to fill 0 - 14 | ||
* @param {Buffer} imageBuffer | ||
*/ | ||
fillImage(keyIndex, imageBuffer) { | ||
StreamDeck.checkValidKeyIndex(keyIndex); | ||
if (imageBuffer.length !== 15552) { | ||
throw new Error(`Expected image buffer of length 15552, got length ${imageBuffer.length}`); | ||
throw new RangeError(`Expected image buffer of length 15552, got length ${imageBuffer.length}`); | ||
} | ||
@@ -86,3 +155,3 @@ | ||
const b = imageBuffer.readUInt8(i + 2); | ||
row.push(b, g, r); | ||
row.push(r, g, b); | ||
} | ||
@@ -98,6 +167,15 @@ pixels = pixels.concat(row.reverse()); | ||
/** | ||
* Fill's the given key with an image from a file. | ||
* | ||
* @param {number} keyIndex The key to fill 0 - 14 | ||
* @param {String} filePath A file path to an image file | ||
* @returns {Promise<void>} Resolves when the file has been written | ||
*/ | ||
fillImageFromFile(keyIndex, filePath) { | ||
StreamDeck.checkValidKeyIndex(keyIndex); | ||
return sharp(filePath) | ||
.flatten() // Eliminate alpha channel, if any. | ||
.resize(this.ICON_SIZE, this.ICON_SIZE) | ||
.resize(this.ICON_SIZE) | ||
.raw() | ||
@@ -110,2 +188,34 @@ .toBuffer() | ||
/** | ||
* Clears the given key. | ||
* | ||
* @param {number} keyIndex The key to clear 0 - 14 | ||
* @returns {undefined} | ||
*/ | ||
clearKey(keyIndex) { | ||
StreamDeck.checkValidKeyIndex(keyIndex); | ||
return this.fillColor(keyIndex, 0, 0, 0); | ||
} | ||
/** | ||
* Sets the brightness of the keys on the Stream Deck | ||
* | ||
* @param {number} percentage The percentage brightness | ||
*/ | ||
setBrightness(percentage) { | ||
if (percentage < 0 || percentage > 100) { | ||
throw new RangeError('Expected brightness percentage to be between 0 and 100'); | ||
} | ||
this.sendFeatureReport(this._padToLength(Buffer.from([0x05, 0x55, 0xaa, 0xd1, 0x01, percentage]), 17)); | ||
} | ||
/** | ||
* Writes a Stream Deck's page 1 headers and image data to the Stream Deck. | ||
* | ||
* @private | ||
* @param {number} keyIndex The key to write to 0 - 14 | ||
* @param {Buffer} buffer Image data for page 1 | ||
* @returns {undefined} | ||
*/ | ||
_writePage1(keyIndex, buffer) { | ||
@@ -124,20 +234,48 @@ const header = Buffer.from([ | ||
const packetWithHeader = Buffer.concat([header, buffer]); | ||
const numZeroesToFill = PAGE_PACKET_SIZE - packetWithHeader.length; | ||
const packet = Buffer.concat([packetWithHeader, Buffer.alloc(numZeroesToFill)]); | ||
const packet = this._padToLength(Buffer.concat([header, buffer]), PAGE_PACKET_SIZE); | ||
return this.write(packet); | ||
} | ||
/** | ||
* Writes a Stream Deck's page 2 headers and image data to the Stream Deck. | ||
* | ||
* @private | ||
* @param {number} keyIndex The key to write to 0 - 14 | ||
* @param {Buffer} buffer Image data for page 2 | ||
* @returns {undefined} | ||
*/ | ||
_writePage2(keyIndex, buffer) { | ||
const header = Buffer.from([ | ||
0x02, 0x01, 0x02, 0x00, 0x01, keyIndex + 1, 0x00, 0x00, | ||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 | ||
]); | ||
const packetWithHeader = Buffer.concat([header, buffer]); | ||
const numZeroesToFill = PAGE_PACKET_SIZE - packetWithHeader.length; | ||
const packet = Buffer.concat([packetWithHeader, Buffer.alloc(numZeroesToFill)]); | ||
const header = Buffer.from([0x02, 0x01, 0x02, 0x00, 0x01, keyIndex + 1]); | ||
const packet = this._padToLength(Buffer.concat([header, this._pad(10), buffer]), PAGE_PACKET_SIZE); | ||
return this.write(packet); | ||
} | ||
/** | ||
* Pads a given buffer till padLength with 0s. | ||
* | ||
* @private | ||
* @param {Buffer} buffer Buffer to pad | ||
* @param {number} padLength The length to pad to | ||
* @returns {Buffer} The Buffer padded to the length requested | ||
*/ | ||
_padToLength(buffer, padLength) { | ||
return Buffer.concat([buffer, this._pad(padLength - buffer.length)]); | ||
} | ||
/** | ||
* Returns an empty buffer (filled with zeroes) of the given length | ||
* | ||
* @private | ||
* @param {number} padLength Length of the buffer | ||
* @returns {Buffer} | ||
*/ | ||
_pad(padLength) { | ||
return Buffer.alloc(padLength); | ||
} | ||
/** | ||
* The pixel size of an icon written to the Stream Deck key. | ||
* | ||
* @readonly | ||
*/ | ||
get ICON_SIZE() { | ||
@@ -147,2 +285,10 @@ return ICON_SIZE; | ||
/** | ||
* Converts a buffer into an number[]. Used to supply the underlying | ||
* node-hid device with the format it accepts. | ||
* | ||
* @static | ||
* @param {Buffer} buffer Buffer to convert | ||
* @returns {number[]} the converted buffer | ||
*/ | ||
static bufferToIntArray(buffer) { | ||
@@ -149,0 +295,0 @@ const array = []; |
@@ -0,0 +0,0 @@ MIT License |
{ | ||
"name": "elgato-stream-deck", | ||
"version": "1.1.0", | ||
"description": "A npm module for interfacing with the Elgato Stream Deck", | ||
"version": "1.2.0", | ||
"description": "An npm module for interfacing with the Elgato Stream Deck", | ||
"main": "index.js", | ||
"typings": "typings.d.ts", | ||
"dependencies": { | ||
@@ -10,5 +11,13 @@ "node-hid": "^0.5.4" | ||
"devDependencies": { | ||
"ava": "^0.19.1", | ||
"coveralls": "^2.13.1", | ||
"eslint": "^3.19.0", | ||
"eslint-config-xo": "^0.18.2", | ||
"eslint-plugin-ava": "^4.2.0", | ||
"mockery": "^2.0.0", | ||
"nyc": "^10.3.2", | ||
"pureimage": "0.0.21", | ||
"sharp": "^0.17.3", | ||
"sinon": "^2.2.0", | ||
"stream-buffers": "^3.0.1", | ||
"weallbehave": "^1.2.0", | ||
@@ -18,3 +27,6 @@ "weallcontribute": "^1.0.8" | ||
"scripts": { | ||
"test": "npm run static", | ||
"pretest": "npm run static", | ||
"test": "nyc ava", | ||
"report": "nyc report --reporter=html", | ||
"coverage": "nyc report --reporter=text-lcov | coveralls", | ||
"static": "eslint .", | ||
@@ -32,2 +44,3 @@ "update-coc": "weallbehave -o . && git add CODE_OF_CONDUCT.md && git commit -m 'docs(coc): updated CODE_OF_CONDUCT.md'", | ||
"deck", | ||
"streamdeck", | ||
"hid", | ||
@@ -68,3 +81,6 @@ "usb", | ||
"lib" | ||
] | ||
], | ||
"engines": { | ||
"node": ">=4" | ||
} | ||
} |
@@ -1,2 +0,2 @@ | ||
# elgato-stream-deck [![npm version](https://img.shields.io/npm/v/elgato-stream-deck.svg)](https://npm.im/elgato-stream-deck) [![license](https://img.shields.io/npm/l/elgato-stream-deck.svg)](https://npm.im/elgato-stream-deck) [![Travis](https://travis-ci.org/Lange/node-elgato-stream-deck.svg?branch=master)](https://travis-ci.org/Lange/node-elgato-stream-deck) [![Join the chat at https://gitter.im/node-elgato-stream-deck/Lobby](https://badges.gitter.im/node-elgato-stream-deck/Lobby.svg)](https://gitter.im/node-elgato-stream-deck/Lobby?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge&utm_content=badge) | ||
# elgato-stream-deck [![npm version](https://img.shields.io/npm/v/elgato-stream-deck.svg)](https://npm.im/elgato-stream-deck) [![license](https://img.shields.io/npm/l/elgato-stream-deck.svg)](https://npm.im/elgato-stream-deck) [![Travis](https://travis-ci.org/Lange/node-elgato-stream-deck.svg?branch=master)](https://travis-ci.org/Lange/node-elgato-stream-deck) [![Coverage Status](https://coveralls.io/repos/github/Lange/node-elgato-stream-deck/badge.svg?branch=master)](https://coveralls.io/github/Lange/node-elgato-stream-deck?branch=master) [![Join the chat at https://gitter.im/node-elgato-stream-deck/Lobby](https://badges.gitter.im/node-elgato-stream-deck/Lobby.svg)](https://gitter.im/node-elgato-stream-deck/Lobby?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge&utm_content=badge) | ||
@@ -12,2 +12,33 @@ ![alt text](media/streamdeck_ui.png "elgato-stream-deck") | ||
If that fails (**or if you are on a Raspberry Pi**), you will need to install a compiler toolchain to enable npm to build | ||
some of `node-elgato-stream-deck`'s dependencies from source. | ||
* Windows | ||
* Install [`windows-build-tools`](https://github.com/felixrieseberg/windows-build-tools): | ||
```bash | ||
npm install --global windows-build-tools | ||
``` | ||
* MacOS | ||
* Install [Xcode](https://developer.apple.com/xcode/download/), then: | ||
```bash | ||
xcode-select --install | ||
``` | ||
* Linux (**including Raspberry Pi**) | ||
* Follow the instructions for Linux in the ["Compiling from source"](https://github.com/node-hid/node-hid#compiling-from-source) steps for `node-hid`: | ||
```bash | ||
sudo apt-get install build-essential git | ||
sudo apt-get install gcc-4.8 g++-4.8 && export CXX=g++-4.8 | ||
sudo apt-get install sudo apt install libusb-1.0-0 libusb-1.0-0-dev | ||
``` | ||
* Install a recent version of Node.js. We've had success with v7: | ||
```bash | ||
curl -sL https://deb.nodesource.com/setup_7.x | sudo -E bash - | ||
sudo apt-get install -y nodejs | ||
``` | ||
* Try installing `node-elgato-stream-deck` | ||
* If you still have issues, ensure everything is updated and try again: | ||
```bash | ||
sudo apt-get update && sudo apt-get upgrade | ||
``` | ||
## Table of Contents | ||
@@ -24,2 +55,4 @@ | ||
* [`fillImage`](#fill-image) | ||
* [`clearKey`](#clear-key) | ||
* [`setBrightness`](#set-brightness) | ||
* [Events](#events) | ||
@@ -29,8 +62,11 @@ * [`down`](#down) | ||
* [`error`](#error) | ||
* [Protocol Notes](#protocol-notes) | ||
### Example | ||
#### JavaScript | ||
```javascript | ||
const path = require('path'); | ||
const streamDeck = require('elgato-stream-deck') | ||
const streamDeck = require('elgato-stream-deck'); | ||
@@ -60,11 +96,35 @@ streamDeck.on('down', keyIndex => { | ||
#### TypeScript | ||
```typescript | ||
import streamDeck = require('elgato-stream-deck'); | ||
streamDeck.on('down', keyIndex => { | ||
console.log('key %d down', keyIndex); | ||
}); | ||
streamDeck.on('up', keyIndex => { | ||
console.log('key %d up', keyIndex); | ||
}); | ||
streamDeck.on('error', error => { | ||
console.error(error); | ||
}); | ||
``` | ||
### Features | ||
* Miltiplatform support: Windows 7-10, MacOS, Linux, and even Raspberry Pi! | ||
* Key `down` and key `up` events | ||
* Fill keys with images or solid RGB colors | ||
* Typescript support | ||
* Set the Stream Deck brightness | ||
### Planned Features | ||
* Key combinations | ||
* [Hotplugging](https://github.com/Lange/node-elgato-stream-deck/issues/14) | ||
* [Key combinations](https://github.com/Lange/node-elgato-stream-deck/issues/9) | ||
* Support "pages" feature from the official Elgato Stream Deck software | ||
* [Text labels](https://github.com/Lange/node-elgato-stream-deck/issues/6) | ||
* [Changing the standby image](https://github.com/Lange/node-elgato-stream-deck/issues/11) | ||
@@ -135,3 +195,3 @@ ### Contributing | ||
.flatten() // Eliminate alpha channel, if any. | ||
.resize(streamDeck.ICON_SIZE, streamDeck.ICON_SIZE) // Scale down to the right size, cropping if necessary. | ||
.resize(streamDeck.ICON_SIZE) // Scale down to the right size, cropping if necessary. | ||
.raw() // Give us uncompressed RGB | ||
@@ -147,2 +207,24 @@ .toBuffer() | ||
#### <a name="clear-key"></a> `> streamDeck.clearKey(keyIndex) -> undefined` | ||
Synchronously clears the given `keyIndex`'s screen. | ||
##### Example | ||
```javascript | ||
// Clear the third button from the left in the first row. | ||
streamDeck.clearKey(2); | ||
``` | ||
#### <a name="set-brightness"></a> `> streamDeck.setBrightness(percentage) -> undefined` | ||
Synchronously set the brightness of the Stream Deck. | ||
##### Example | ||
```javascript | ||
// Set the Stream Deck to maximum brightness | ||
streamDeck.setBrightness(100); | ||
``` | ||
### Events | ||
@@ -182,5 +264,9 @@ | ||
```javascript | ||
streamDeck.on('error', keyIndex => { | ||
console.log('key %d error', keyIndex); | ||
streamDeck.on('error', error => { | ||
console.error(error); | ||
}); | ||
``` | ||
### Protocol Notes | ||
Raw protocol notes can be found in [NOTES.md](NOTES.md). These detail the protocol and method for interacting with the Stream Deck which this module implements. |
Major refactor
Supply chain riskPackage has recently undergone a major refactor. It may be unstable or indicate significant internal changes. Use caution when updating to versions that include significant changes.
Found 1 instance in 1 package
New author
Supply chain riskA new npm collaborator published a version of the package for the first time. New collaborators are usually benign additions to a project, but do indicate a change to the security surface area of a package.
Found 1 instance in 1 package
21393
6
269
266
13
2