elgato-stream-deck
Advanced tools
Comparing version 1.2.0 to 2.0.0
@@ -0,1 +1,42 @@ | ||
# Change Log | ||
All notable changes to this project will be documented in this file. See [standard-version](https://github.com/conventional-changelog/standard-version) for commit guidelines. | ||
<a name="2.0.0"></a> | ||
# [2.0.0](https://github.com/Lange/node-elgato-stream-deck/compare/v1.2.0...v2.0.0) (2017-11-28) | ||
### Features | ||
* add `fillPanel` method | ||
* add `clearAllKeys` method | ||
* return the `StreamDeck` constructor instead of automatically instantiating it | ||
* allow providing a `devicePath` to the constructor | ||
* if no device path is provided, will attempt to use the first found Stream Deck. Errors if no Stream Decks are connected. | ||
* update `this.keyState` *before* emitting `down` and `up` events | ||
* this is technically a *breaking change*, but is very unlikely to affect any production code | ||
### Bug Fixes | ||
* fix center-cropping in `fillImageFromFile` | ||
* fix `sharp` only being a `devDependency`, and not a production `dependency` | ||
### Code Refactoring | ||
* refactor `StreamDeck` class to move as much as possible to static methods and static getters | ||
* refactor code to use `async`/`await` | ||
* this is a *breaking change*, because we now only support Node.js v7.6 or newer | ||
### Documentation | ||
* update all examples | ||
* add `fillPanel` example | ||
### BREAKING CHANGES | ||
* `this.keyState` is now updated **before** `down` and `up` events are emitted. | ||
* Support for versions of Node.js earlier than 7.6 has been dropped. | ||
* The `StreamDeck` constructor is now required when `require`ing this library, instead of an instance of the class. | ||
* See the docs for updated examples. | ||
<a name="1.2.0"></a> | ||
@@ -2,0 +43,0 @@ # [1.2.0](https://github.com/Lange/node-elgato-stream-deck/compare/v1.1.0...v1.2.0) (2017-06-23) |
255
index.js
@@ -16,27 +16,100 @@ 'use strict'; | ||
const NUM_TOTAL_PIXELS = NUM_FIRST_PAGE_PIXELS + NUM_SECOND_PAGE_PIXELS; | ||
const NUM_BUTTON_COLUMNS = 5; | ||
const NUM_BUTTON_ROWS = 3; | ||
const devices = HID.devices(); | ||
const connectedStreamDecks = devices.filter(device => { | ||
return device.vendorId === 0x0fd9 && device.productId === 0x0060; | ||
}); | ||
class StreamDeck extends EventEmitter { | ||
/** | ||
* The pixel size of an icon written to the Stream Deck key. | ||
* | ||
* @readonly | ||
*/ | ||
static get ICON_SIZE() { | ||
return ICON_SIZE; | ||
} | ||
/* istanbul ignore if */ | ||
if (connectedStreamDecks.length > 1) { | ||
throw new Error('More than one Stream Deck is connected. This is unsupported at this time.'); | ||
} | ||
/** | ||
* 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'); | ||
} | ||
} | ||
/* istanbul ignore if */ | ||
if (connectedStreamDecks.length < 1) { | ||
throw new Error('No Stream Decks are connected.'); | ||
} | ||
/** | ||
* 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'); | ||
} | ||
} | ||
class StreamDeck extends EventEmitter { | ||
constructor(device) { | ||
/** | ||
* 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 | ||
*/ | ||
static padBufferToLength(buffer, padLength) { | ||
return Buffer.concat([buffer, StreamDeck.createPadBuffer(padLength - buffer.length)]); | ||
} | ||
/** | ||
* Returns an empty buffer (filled with zeroes) of the given length | ||
* | ||
* @private | ||
* @param {number} padLength Length of the buffer | ||
* @returns {Buffer} | ||
*/ | ||
static createPadBuffer(padLength) { | ||
return Buffer.alloc(padLength); | ||
} | ||
/** | ||
* 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) { | ||
const array = []; | ||
for (const pair of buffer.entries()) { | ||
array.push(pair[1]); | ||
} | ||
return array; | ||
} | ||
constructor(devicePath) { | ||
super(); | ||
this.device = device; | ||
if (typeof devicePath === 'undefined') { | ||
// Device path not provided, will then select any connected device. | ||
const devices = HID.devices(); | ||
const connectedStreamDecks = devices.filter(device => { | ||
return device.vendorId === 0x0fd9 && device.productId === 0x0060; | ||
}); | ||
if (!connectedStreamDecks.length) { | ||
throw new Error('No Stream Decks are connected.'); | ||
} | ||
this.device = new HID.HID(connectedStreamDecks[0].path); | ||
} else { | ||
this.device = new HID.HID(devicePath); | ||
} | ||
this.keyState = new Array(NUM_KEYS).fill(false); | ||
this.device.on('data', data => { | ||
// The first byte is a report ID, the last byte appears to be padding | ||
// strip these out for now. | ||
// The first byte is a report ID, the last byte appears to be padding. | ||
// We strip these out for now. | ||
data = data.slice(1, data.length - 1); | ||
@@ -46,3 +119,5 @@ | ||
const keyPressed = Boolean(data[i]); | ||
if (keyPressed !== this.keyState[i]) { | ||
const stateChanged = keyPressed !== this.keyState[i]; | ||
if (stateChanged) { | ||
this.keyState[i] = keyPressed; | ||
if (keyPressed) { | ||
@@ -54,4 +129,2 @@ this.emit('down', i); | ||
} | ||
this.keyState[i] = keyPressed; | ||
} | ||
@@ -106,26 +179,2 @@ }); | ||
/** | ||
* 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. | ||
@@ -169,8 +218,7 @@ * | ||
*/ | ||
fillImageFromFile(keyIndex, filePath) { | ||
async fillImageFromFile(keyIndex, filePath) { | ||
StreamDeck.checkValidKeyIndex(keyIndex); | ||
return sharp(filePath) | ||
.flatten() // Eliminate alpha channel, if any. | ||
.resize(this.ICON_SIZE) | ||
.resize(StreamDeck.ICON_SIZE, StreamDeck.ICON_SIZE) | ||
.raw() | ||
@@ -184,2 +232,39 @@ .toBuffer() | ||
/** | ||
* Fills the whole panel with an image in a Buffer. | ||
* The image is scaled to fit, and then center-cropped (if necessary). | ||
* | ||
* @param {Buffer|String} imagePathOrBuffer | ||
* @param {Object} [sharpOptions] - Options to pass to sharp, necessary if supplying a buffer of raw pixels. | ||
* See http://sharp.dimens.io/en/latest/api-constructor/#sharpinput-options for more details. | ||
*/ | ||
async fillPanel(imagePathOrBuffer, sharpOptions) { | ||
const image = await sharp(imagePathOrBuffer, sharpOptions) | ||
.resize(NUM_BUTTON_COLUMNS * ICON_SIZE, NUM_BUTTON_ROWS * ICON_SIZE) | ||
.flatten(); // Eliminate alpha channel, if any. | ||
const buttons = []; | ||
for (let row = 0; row < NUM_BUTTON_ROWS; row++) { | ||
for (let column = 0; column < NUM_BUTTON_COLUMNS; column++) { | ||
buttons.push({ | ||
index: (row * NUM_BUTTON_COLUMNS) + NUM_BUTTON_COLUMNS - column - 1, | ||
x: column, | ||
y: row | ||
}); | ||
} | ||
} | ||
const buttonFillPromises = buttons.map(async button => { | ||
const imageBuffer = await image.extract({ | ||
left: button.x * ICON_SIZE, | ||
top: button.y * ICON_SIZE, | ||
width: ICON_SIZE, | ||
height: ICON_SIZE | ||
}).raw().toBuffer(); | ||
return this.fillImage(button.index, imageBuffer); | ||
}); | ||
return Promise.all(buttonFillPromises); | ||
} | ||
/** | ||
* Clears the given key. | ||
@@ -192,3 +277,2 @@ * | ||
StreamDeck.checkValidKeyIndex(keyIndex); | ||
return this.fillColor(keyIndex, 0, 0, 0); | ||
@@ -198,2 +282,13 @@ } | ||
/** | ||
* Clears all keys. | ||
* | ||
* returns {undefined} | ||
*/ | ||
clearAllKeys() { | ||
for (let keyIndex = 0; keyIndex < NUM_KEYS; keyIndex++) { | ||
this.clearKey(keyIndex); | ||
} | ||
} | ||
/** | ||
* Sets the brightness of the keys on the Stream Deck | ||
@@ -207,3 +302,5 @@ * | ||
} | ||
this.sendFeatureReport(this._padToLength(Buffer.from([0x05, 0x55, 0xaa, 0xd1, 0x01, percentage]), 17)); | ||
const brightnessCommandBuffer = Buffer.from([0x05, 0x55, 0xaa, 0xd1, 0x01, percentage]); | ||
this.sendFeatureReport(StreamDeck.padBufferToLength(brightnessCommandBuffer, 17)); | ||
} | ||
@@ -232,3 +329,3 @@ | ||
const packet = this._padToLength(Buffer.concat([header, buffer]), PAGE_PACKET_SIZE); | ||
const packet = StreamDeck.padBufferToLength(Buffer.concat([header, buffer]), PAGE_PACKET_SIZE); | ||
return this.write(packet); | ||
@@ -246,56 +343,12 @@ } | ||
_writePage2(keyIndex, buffer) { | ||
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); | ||
const header = Buffer.from([ | ||
0x02, 0x01, 0x02, 0x00, 0x01, keyIndex + 1, 0x00, 0x00, | ||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 | ||
]); | ||
const packet = StreamDeck.padBufferToLength(Buffer.concat([header, 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() { | ||
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) { | ||
const array = []; | ||
for (const pair of buffer.entries()) { | ||
array.push(pair[1]); | ||
} | ||
return array; | ||
} | ||
} | ||
module.exports = new StreamDeck(new HID.HID(connectedStreamDecks[0].path)); | ||
module.exports = StreamDeck; |
@@ -0,0 +0,0 @@ MIT License |
{ | ||
"name": "elgato-stream-deck", | ||
"version": "1.2.0", | ||
"version": "2.0.0", | ||
"description": "An npm module for interfacing with the Elgato Stream Deck", | ||
@@ -8,15 +8,16 @@ "main": "index.js", | ||
"dependencies": { | ||
"node-hid": "^0.5.4" | ||
"node-hid": "^0.5.7", | ||
"sharp": "^0.18.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", | ||
"ava": "^0.23.0", | ||
"coveralls": "^3.0.0", | ||
"eslint": "^4.11.0", | ||
"eslint-config-xo": "^0.19.0", | ||
"eslint-plugin-ava": "^4.2.2", | ||
"mockery": "^2.1.0", | ||
"nyc": "^11.3.0", | ||
"pureimage": "0.1.3", | ||
"sinon": "^4.1.2", | ||
"standard-version": "^4.2.0", | ||
"stream-buffers": "^3.0.1", | ||
@@ -27,2 +28,5 @@ "weallbehave": "^1.2.0", | ||
"scripts": { | ||
"prerelease": "npm t", | ||
"release": "standard-version", | ||
"postrelease": "npm publish && git push --follow-tags", | ||
"pretest": "npm run static", | ||
@@ -82,4 +86,4 @@ "test": "nyc ava", | ||
"engines": { | ||
"node": ">=4" | ||
"node": ">=7.6" | ||
} | ||
} |
104
README.md
@@ -21,3 +21,3 @@ # 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) | ||
* MacOS | ||
* Install [Xcode](https://developer.apple.com/xcode/download/), then: | ||
* Install the Xcode Command Line Tools: | ||
```bash | ||
@@ -28,12 +28,12 @@ xcode-select --install | ||
* 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 | ||
``` | ||
```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.: | ||
```bash | ||
curl -sL https://deb.nodesource.com/setup_8.x | sudo -E bash - | ||
sudo apt-get install -y nodejs | ||
``` | ||
* Try installing `node-elgato-stream-deck` | ||
@@ -56,3 +56,5 @@ * If you still have issues, ensure everything is updated and try again: | ||
* [`fillImage`](#fill-image) | ||
* [`fillPanel`](#fill-panel) | ||
* [`clearKey`](#clear-key) | ||
* [`clearAllKeys`](#clear-all-keys) | ||
* [`setBrightness`](#set-brightness) | ||
@@ -71,14 +73,21 @@ * [Events](#events) | ||
const path = require('path'); | ||
const streamDeck = require('elgato-stream-deck'); | ||
const StreamDeck = require('elgato-stream-deck'); | ||
streamDeck.on('down', keyIndex => { | ||
console.log('key %d down', keyIndex); | ||
// Automatically discovers connected Stream Decks, and attaches to the first one. | ||
// Throws if there are no connected stream decks. | ||
// You also have the option of providing the devicePath yourself as the first argument to the constructor. | ||
// For example: const myStreamDeck = new StreamDeck('\\\\?\\hid#vid_05f3&pid_0405&mi_00#7&56cf813&0&0000#{4d1e55b2-f16f-11cf-88cb-001111000030}') | ||
// Device paths can be obtained via node-hid: https://github.com/node-hid/node-hid | ||
const myStreamDeck = new StreamDeck(); | ||
myStreamDeck.on('down', keyIndex => { | ||
console.log('key %d down', keyIndex); | ||
}); | ||
streamDeck.on('up', keyIndex => { | ||
console.log('key %d up', keyIndex); | ||
myStreamDeck.on('up', keyIndex => { | ||
console.log('key %d up', keyIndex); | ||
}); | ||
streamDeck.on('error', error => { | ||
console.error(error); | ||
myStreamDeck.on('error', error => { | ||
console.error(error); | ||
}); | ||
@@ -88,3 +97,3 @@ | ||
// This is asynchronous and returns a promise. | ||
streamDeck.fillImageFromFile(3, path.resolve(__dirname, 'github_logo.png')).then(() => { | ||
myStreamDeck.fillImageFromFile(3, path.resolve(__dirname, 'github_logo.png')).then(() => { | ||
console.log('Successfully wrote a GitHub logo to key 3.'); | ||
@@ -94,3 +103,3 @@ }); | ||
// Fill the first button form the left in the first row with a solid red color. This is synchronous. | ||
streamDeck.fillColor(4, 255, 0, 0); | ||
myStreamDeck.fillColor(4, 255, 0, 0); | ||
console.log('Successfully wrote a red square to key 4.'); | ||
@@ -102,14 +111,15 @@ ``` | ||
```typescript | ||
import streamDeck = require('elgato-stream-deck'); | ||
import StreamDeck = require('elgato-stream-deck'); | ||
const myStreamDeck = new StreamDeck(); // Will throw an error if no Stream Decks are connected. | ||
streamDeck.on('down', keyIndex => { | ||
console.log('key %d down', keyIndex); | ||
myStreamDeck.on('down', keyIndex => { | ||
console.log('key %d down', keyIndex); | ||
}); | ||
streamDeck.on('up', keyIndex => { | ||
console.log('key %d up', keyIndex); | ||
myStreamDeck.on('up', keyIndex => { | ||
console.log('key %d up', keyIndex); | ||
}); | ||
streamDeck.on('error', error => { | ||
console.error(error); | ||
myStreamDeck.on('error', error => { | ||
console.error(error); | ||
}); | ||
@@ -120,7 +130,8 @@ ``` | ||
* Miltiplatform support: Windows 7-10, MacOS, Linux, and even Raspberry Pi! | ||
* Multiplatform 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 | ||
* Fill the entire panel with a single image, spread across all keys | ||
* Set the Stream Deck brightness | ||
* TypeScript support | ||
@@ -199,4 +210,4 @@ ### Planned Features | ||
.flatten() // Eliminate alpha channel, if any. | ||
.resize(streamDeck.ICON_SIZE) // Scale down to the right size, cropping if necessary. | ||
.raw() // Give us uncompressed RGB | ||
.resize(streamDeck.ICON_SIZE, streamDeck.ICON_SIZE) // Scale up/down to the right size, cropping if necessary. | ||
.raw() // Give us uncompressed RGB. | ||
.toBuffer() | ||
@@ -211,2 +222,21 @@ .then(buffer => { | ||
#### <a name="fill-panel"></a> `> streamDeck.fillPanel(imagePathOrBuffer[, sharpOptions]) -> Promise` | ||
Asynchronously applies an image to the entire panel, spreading it over all keys. The image is scaled down and center-cropped to fit. This method does not currently account for the gaps between keys, and behaves as if each key was directly connected to its neighbors. If you wish to account for the gaps between keys, you'll need to do so via other means, and bake that into the image you provide to `fillPanel`. | ||
This method accepts either a path to an image on the disk, or a buffer. The image or path or buffer is passed directly to [`sharp`](https://github.com/lovell/sharp). Therefore, this method accepts all images and buffers which `sharp` can accept. | ||
##### Example | ||
```javascript | ||
// Fill the second button from the left in the first row with an image of the GitHub logo. | ||
streamDeck.fillImageFromFile(3, path.resolve(__dirname, 'github_logo.png')) | ||
.then(() => { | ||
console.log('Successfully wrote a GitHub logo to key 3.'); | ||
}) | ||
.catch(err => { | ||
console.error(err); | ||
}); | ||
``` | ||
#### <a name="clear-key"></a> `> streamDeck.clearKey(keyIndex) -> undefined` | ||
@@ -216,2 +246,6 @@ | ||
#### <a name="clear-all-keys"></a> `> streamDeck.clearAllKeys() -> undefined` | ||
Synchronously clears all keys on the device. | ||
##### Example | ||
@@ -226,3 +260,3 @@ | ||
Synchronously set the brightness of the Stream Deck. | ||
Synchronously set the brightness of the Stream Deck. This affects all keys at once. The brightness of individual keys cannot be controlled. | ||
@@ -246,3 +280,3 @@ ##### Example | ||
streamDeck.on('down', keyIndex => { | ||
console.log('key %d down', keyIndex); | ||
console.log('key %d down', keyIndex); | ||
}); | ||
@@ -259,3 +293,3 @@ ``` | ||
streamDeck.on('up', keyIndex => { | ||
console.log('key %d up', keyIndex); | ||
console.log('key %d up', keyIndex); | ||
}); | ||
@@ -273,3 +307,3 @@ ``` | ||
streamDeck.on('error', error => { | ||
console.error(error); | ||
console.error(error); | ||
}); | ||
@@ -276,0 +310,0 @@ ``` |
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
26859
319
300
0
2
+ Addedsharp@^0.18.4
+ Addedcaw@2.0.1(transitive)
+ Addedcolor@2.0.1(transitive)
+ Addedcolor-convert@1.9.3(transitive)
+ Addedcolor-name@1.1.3(transitive)
+ Addedcolor-string@1.9.1(transitive)
+ Addedconfig-chain@1.1.13(transitive)
+ Addeddetect-libc@0.2.0(transitive)
+ Addedget-proxy@2.1.0(transitive)
+ Addedhas-symbol-support-x@1.4.2(transitive)
+ Addedhas-to-string-tag-x@1.4.1(transitive)
+ Addedis-arrayish@0.3.2(transitive)
+ Addedis-object@1.0.2(transitive)
+ Addedisurl@1.0.0(transitive)
+ Addedminipass@2.9.0(transitive)
+ Addedminizlib@1.3.3(transitive)
+ Addednpm-conf@1.1.3(transitive)
+ Addedpify@3.0.0(transitive)
+ Addedproto-list@1.2.4(transitive)
+ Addedsharp@0.18.4(transitive)
+ Addedsimple-swizzle@0.2.2(transitive)
+ Addedtar@3.2.3(transitive)
+ Addedurl-to-options@1.0.1(transitive)
+ Addedyallist@3.1.1(transitive)
Updatednode-hid@^0.5.7