stream-deck-ts
v1.0.0
Published
An npm module for interfacing with a stream deck
Downloads
2
Maintainers
Readme
elgato-stream-deck
This is a library for developers to use. It is not a program for end users. It cannot and will not replace the official Stream Deck program. That is not its goal. However, it does enable someone to more easily write a program which does do that.
Supported devices
Any additional devices would be appriciated. If you are able to control another device than those listed - please send a PR.
Elgato
Install
$ npm install --save stream-deck-ts
All of this library's native dependencies ship with prebuilt binaries, so having a full compiler toolchain should not be necessary to install stream-deck-ts
.
However, in the event that installation does fail (or if you are on a platform that our dependencies don't provide prebuilt binaries for, such as a Raspberry Pi), you will need to install a compiler toolchain to enable npm to build some of stream-deck-ts
's dependencies from source. Expand the details block below for full instructions on how to do so.
- Windows
- Install
windows-build-tools
:
npm install --global windows-build-tools
- Install
- MacOS
- Install the Xcode Command Line Tools:
xcode-select --install
- Linux (including Raspberry Pi)
- Follow the instructions for Linux in the "Compiling from source" steps for
node-hid
: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.:
curl -sL https://deb.nodesource.com/setup_8.x | sudo -E bash - sudo apt-get install -y nodejs
- Try installing
stream-deck-ts
- If you still have issues, ensure everything is updated and try again:
sudo apt-get update && sudo apt-get upgrade
- Follow the instructions for Linux in the "Compiling from source" steps for
Table of Contents
- Example
- Features
- Planned Features
- Contributing
- API
- Constants
- devices()
- getStreamDeckProduct(vendor, product)
- registerStreamDeckProduct(vendor, product, config)
- selectDevice([vendor[, product]])
- selectAllDevices([vendor[, product]])
- setHidAsyncType(asyncMode)
- Class: StreamDeck
- Event: 'down'
- Event: 'up'
- Event: 'error'
- streamDeck.buttonColumns
- streamDeck.buttonLength
- streamDeck.buttonRows
- streamDeck.iconSize
- streamDeck.pressedKeys
- streamDeck.buttonIndexFromPosition(x, y)
- streamDeck.checkValidKeyIndex(keyIndex)
- streamDeck.clearAllKeys()
- streamDeck.clearKey(keyIndex)
- streamDeck.fillColor(keyIndex, rgb)
- streamDeck.fillColor(keyIndex, r, g, b)
- streamDeck.fillImage(keyIndex, buffer)
- streamDeck.fillImageFromFile(keyIndex, filePath)
- streamDeck.fillPanel(imagePathOrBuffer[, rawOptions])
- streamDeck.forEachKey(callback)
- streamDeck.sendFeatureReport(buffer)
- streamDeck.setBrightness(percentage)
- streamDeck.setImageLibrary(library)
- streamDeck.write(buffer)
- streamDeck.writeMulti(buffers)
- Abstract class: StreamDeckBase
- Interface: IImageLibrary
- Interface: IImageLibraryCreator
Example
import { resolve } from "path";
import { selectDevice } from "stream-deck-ts";
(async function asyncMain() {
// Automatically discovers connected Stream Decks, and attaches to the first one.
// Returns `null` if there are no connected stream decks.
// You also have the option of providing the numeric vendor identifier and product identifier.
// For example: `const myStreamDeck = await selectDevice(VENDOR_ELGATO, PRODUCT_ELGATO_STREAMDECK_MINI);`
const myStreamDeck = await selectDevice();
if (!myStreamDeck) {
throw new Error("No StreamDeck found.");
}
myStreamDeck.on('down', (keyIndex) => {
console.log('key %d down', keyIndex);
});
myStreamDeck.on('up', (keyIndex) => {
console.log('key %d up', keyIndex);
});
// Fired whenever an error is detected by the `node-hid` library.
// Always add a listener for this event!
myStreamDeck.on('error', (error) => {
console.error(error);
});
// Fill button 3 with an image of the GitHub logo.
// This is asynchronous and returns a promise.
myStreamDeck.fillImageFromFile(3, resolve(__dirname, 'github_logo.png')).then(() => {
console.log('Successfully wrote a GitHub logo to key 3.');
});
// Fill the first button form the left in the first row with a solid red color. This is synchronous.
myStreamDeck.fillColor(4, 255, 0, 0);
console.log('Successfully wrote a red square to key 4.');
}());
Features
- Multiplatform support: Windows 7-10, MacOS, Linux, and even Raspberry Pi!
- Key
down
and keyup
events - Fill keys with images or solid RGB colors
- Fill the entire panel with a single image, spread across all keys
- Set the Stream Deck brightness
- TypeScript support
- Multi device support
- Optional user supplier image processing library
API
Constants
Constant: DEVICE_MODELS
- <Object<number, Object<number, Object>>>
An object containing devices which are supported.
DEVICE_MODELS[vendor][device].import
<string> A path to the device implementation.DEVICE_MODELS[vendor][device].productName
<string> A name representing the product of the device.DEVICE_MODELS[vendor][device].vendorName
<string> A name representing the vendor of the device.
To get specific product information getStreamDeckProduct
is the preferred solution.
Constant: PRODUCT_ELGATO_STREAMDECK
export const PRODUCT_ELGATO_STREAMDECK = 96;
Exported constant to use as the product
filter while selecting a device.
Constant: PRODUCT_ELGATO_STREAMDECK_MINI
export const PRODUCT_ELGATO_STREAMDECK_MINI = 99;
Exported constant to use as the product
filter while selecting a device.
Constant: VENDOR_ELGATO
export const VENDOR_ELGATO = 4057;
Exported constant to use as the vendor
filter while selecting a device.
devices ()
- Returns: <Promise<Object[]>>
object.vendorId
<number> Numeric vendor id.object.productId
<number> Numeric product id.object.release
<number> Numeric release value.object.interface
<number> Interface number.object.path
<string> Path to the specific device.
Get a list of connected HID devices.
getStreamDeckProduct (vendor, product)
vendor
<number> A vendor identity number to register the product to.product
<number> A product identity number to register the product to.- Returns: <Object | undefined> Configuration if one exists.
object.import
<string> Module name of the device implementation.object.productName
<string> Name of the product.object.vendorName
<string> Name of the vendor.
Register a StreamDeck
compatable product. When a device matching the product is selected the module path in config.import
is loaded and instantiated.
The module referenced by config.import
must export a class named default
. The exported class SHOULD extend StreamDeckBase
.
registerStreamDeckProduct (vendor, product, config)
vendor
<number> A vendor identity number to register the product to.product
<number> A product identity number to register the product to.config
<Object> The configuration object describing the product.- Returns: <Object | undefined> Previous configuration if one existed.
config.import
<string> Module name of the device implementation.config.productName
<string> Name of the product.config.vendorName
<string> Name of the vendor.
Register a StreamDeck
compatable product. When a device matching the product is selected the module path in config.import
is loaded and instantiated.
The module referenced by config.import
must export a class named default
. The exported class SHOULD extend StreamDeckBase
.
selectDevice ([vendor[, product]])
vendor
<number> An optional vendor identity number to limit which device will be selected.product
<number> An optional product identity number to limit which device will be selected.- Returns: <Promise<StreamDeck | null>>
Select the first supported device. If no supported device is found null
is returned.
selectAllDevices ([vendor[, product]])
vendor
<number> An optional vendor identity number to limit which devices will be selected.product
<number> An optional product identity number to limit which devices will be selected.- Returns: <Promise<Array<Promise<StreamDeck>>>>
Get a list of all supported devices while they're being opened.
setHidAsyncType (type)
type
<string> Sets which type of async provider should be used.
The async provider type handles how async calls are made.
auto
selects one of the other modes available to the system, based on priority.emulated
will emulate an async environment with microtasks while still running synchronously inside the main thread. This might be good for debugging or as a last resort fallback but should be avoided if possible.process
creates aforked
process to handle the USB communication which is first sent serialized over an IPC channel.worker
spins up aWorker
to handle the USB communication, which should be the most performant mode by using transferrableByteArray
s. But workers are not currently supported bynode-hid
.
Class: StreamDeck
Instances of the StreamDeck
class have an active connection to a device.
Event: 'down'
keyIndex
<number> The index of the key that got pressed.
The down
event is triggered when a button on the Stream Deck has been pressed down.
Event: 'up'
keyIndex
<number> The index of the key that got released.
The up
event is triggered when a button on the Stream Deck has been released which previously had been pressed down.
Event: 'error'
error
<Error> The index of the key that got released.
Fired whenever an error is detected by the node-hid
library.
Always add a listener for this event! If you don't, errors will be silently dropped.
streamDeck .buttonColumns
- <number>
Returns the number of button columns available to this StreamDeck
.
streamDeck. buttonLength
- <number>
Returns the number of buttons available to this StreamDeck
.
streamDeck .buttonRows
- <number>
Returns the number of button rows available to this StreamDeck
.
streamDeck .hasPressedKeys
- <boolean>
A boolean showing if there currently are any pressed keys.
streamDeck .iconSize
- <number>
Returns the size in pixels used for icons.
streamDeck .pressedKeys
- <Array<number>>
A sorted list of all buttons currently pressed.
streamDeck .buttonIndexFromPosition (x, y)
x
<number> Cloumn number counted from from the left.y
<number> Row number counted from from the top.- Returns: <number> The
keyIndex
at the given position orundefined
if out of bounds.
Get the keyIndex
at a specific column and row.
streamDeck .checkValidKeyIndex (keyIndex)
keyIndex
<number> Cloumn number counted from from the left.y
<number> Row number counted from from the top.- Returns: <number> The
keyIndex
at the given position orundefined
if out of bounds.
Validate a keyIndex
. If the number is not valid the function will throw a TypeError
, otherwise the same value will be returned.
streamDeck .clearAllKeys ()
- Returns: <StreamDeck>
Synchronously clears all keys on the device.
Example: clear all keys
// Clear all keys.
streamDeck.clearAllKeys();
streamDeck .clearKey (keyIndex)
keyIndex
<number> Key to affect.- Returns: <StreamDeck>
Synchronously clears the given keyIndex
's screen.
Example: clear button 2
// Clear button 2.
streamDeck.clearKey(2);
streamDeck .close ()
Closes this reference to the device.
streamDeck .fillColor (keyIndex, rgb)
keyIndex
<number> Key to affect.rgb
<number> Fill color.
Synchronously sets the given keyIndex
's screen to a solid RGB color.
Example: set button 4 to solid red
// Turn key 4 solid red.
streamDeck.fillColor(4, 0xFF0000);
streamDeck .fillColor (keyIndex, r, g, b)
keyIndex
<number> Key to affect.r
<number> Red component between0
-255
.g
<number> Green component between0
-255
.b
<number> Blue component between0
-255
.
Synchronously sets the given keyIndex
's screen to a solid RGB color.
Example: set button 5 to solid blue
// Turn key 5 solid blue.
streamDeck.fillColor(5, 0, 0, 0xFF);
streamDeck .fillImage (keyIndex, buffer)
keyIndex
<number> Key to affect.buffer
<Buffer> Image bytes.- Returns: <Promise<StreamDeck>>
Synchronously writes a buffer of streamDeck.iconSize
* streamDeck.iconSize
RGB image data to the given keyIndex
's screen.
The buffer must be exactly the expected length of bytes. Any other length will result in an error being thrown.
Example: fill button 2 with an image of the GitHub logo
// Fill button 2 with an image of the GitHub logo.
import * as sharp from "sharp"; // See http://sharp.dimens.io/en/stable/ for full docs on this great library!
import { resolve } from "path";
const filepath = resolve(__dirname, 'github_logo.png');
const buffer = await sharp(filepath)
.flatten() // Eliminate alpha channel, if any.
.resize(streamDeck.iconSize, streamDeck.iconSize) // Scale up/down to the right size, cropping if necessary.
.raw() // Give us uncompressed RGB.
.toBuffer();
streamDeck.fillImage(2, buffer);
streamDeck .fillImageFromFile (keyIndex, filePath)
keyIndex
<number> Key to affect.filePath
<string> File system path to an image file.- Returns: <Promise<StreamDeck>>
Asynchronously reads an image from filePath
and sets the given keyIndex
's screen to that image.
Automatically scales the image to the expected height and width and strips out the alpha channel.
If necessary, the image will be center-cropped to fit into a square.
Example: fill the button 3 with an image of the GitHub logo
// Fill the button 3 with an image of the GitHub logo.
await streamDeck.fillImageFromFile(3, path.resolve(__dirname, 'github_logo.png'));
console.log('Successfully wrote a GitHub logo to key 3.');
streamDeck .fillPanel (imagePathOrBuffer[, rawOptions])
imagePathOrBuffer
<string | Uint8Array | Buffer> Image data or path to a file.rawOptions
<object> Optional options object used if the image buffer contained raw pixel data.rawOptions.channels
<number> Number of channels per pixel.RGBA = 4
.rawOptions.height
<number> Height in pixels.rawOptions.width
<number> Width in pixels.
- Returns: <Promise<StreamDeck>>
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 path or buffer is passed directly to sharp
. Therefore, this method accepts all images and buffers which sharp
can accept.
Example: fill the entire panel with a photo of a sunny field
// Fill the entire panel with a photo of a sunny field.
import { resolve } from "path";
const filepath = resolve(__dirname, 'examples/fixtures/sunny_field.png');
await streamDeck.fillPanel(filepath);
console.log('Successfully filled the panel with an image.');
streamDeck .forEachKey (callback)
callback
<Function> The function to call for each button in the stream deck.- Returns: <StreamDeck>
Execute a function for each button available to the StreamDeck
.
streamDeck .sendFeatureReport (buffer)
buffer
<Buffer | Uint8Array> The buffer send to the Stream Deck.- Returns: <StreamDeck>
Sends a HID feature report to the Stream Deck.
streamDeck .setBrightness (percentage)
percentage
<number> Percentage of brightness.- Returns: <StreamDeck>
Synchronously set the brightness of the Stream Deck. This affects all keys at once. The brightness of individual keys cannot be controlled.
Example: set the Stream Deck to maximum brightness
// Set the Stream Deck to maximum brightness
streamDeck.setBrightness(100);
streamDeck .setImageLibrary (library)
library
<string | IImageLibraryCreator | Promise<IImageLibraryCreator>> An object capable to create image contexts or a string representing a module exporting such an object asdefault
.- Returns: <StreamDeck>
Allows users to switch image processing library to a non standard version. This might be because of the library providing better performance or image quality, or simply to minimize bloat when another image library is used somewhere else.
streamDeck .write (buffer)
buffer
<Buffer | Uint8Array> Data to write.- Returns: <Promise<number>> When written this resolves to the number of bytes
Asynchronously writes an arbitrary Uint8Array
instance to the Stream Deck.
The promise is rejected if an error is encountered during the write operation.
streamDeck .writeMulti (buffers)
buffers
<Array<Buffer | Uint8Array>> Data buffers to write.- Returns: <Promise<number>> When written this resolves to the number of bytes
Asynchronously writes a bunch of arbitrary Uint8Array
instances to the Stream Deck.
The promise is rejected if an error is encountered during the write operation.
Example: write a number of zeros to the stream deck
// Writes a total of 32 bytes of zero to the Stream Deck in two pages and waits for the last one to finish.
await streamDeck.writeMulti([
Buffer.alloc(16), Buffer.alloc(16)
]);
Abstract class: StreamDeckBase
Abstract base class which is a partial implementation and must be extended.
To see which abstract properties and methods are required for a subclass check out the source code for StreamDeck
and look for the keyword abstract
.
Interface: IImageLibrary
An image instance controlled by an image library.
Any library implementing, or wrapped by an object implementing, this interface could be used for image operations.
IImageLibrary .extract (options)
options
<Object> Data to write.- Returns: <IImageLibrary | Promise<IImageLibrary>>
Extract a section of the image.
If this is a immutable data structure a clone of the original should be returned with the new 2d slice. A mutable structure should return the original but with a view which may be moved with another call to extract.
options.left
<number> the distance from the left edge of the originaloptions.top
<number> the distance from the top edge of the originaloptions.width
<number> the width of this subsectionoptions.height
<number> the height of this subsection
IImageLibrary .flatten ()
- Returns: <IImageLibrary | Promise<IImageLibrary>>
Remove alpha channel if one exists.
IImageLibrary .resize (width, height)
width
<number> Width of the resulting image.height
<number> Height of the resulting image.- Returns: <IImageLibrary | Promise<IImageLibrary>>
Resizes an image to the specified size.
Interpolation and ratio perservation mode is up to the library or wrapper.
However it is recommended that the image be applied in a cover
fashion.
IImageLibrary .toUint8Array ()
- Returns: <Uint8Array | Promise<Uint8Array>>
Get the RGB raw pixel bytes representing the image.
Interface: IImageLibraryCreator
An object implementing this interface is tasked with instantiating image contexts using a library or a custom implementation.
IImageLibraryCreator .createRaw (data, rawOptions)
data
<Uint8Array> Raw RGB pixel data.rawOptions
<Object> Parameters describing the raw image.- Returns: <IImageLibrary | Promise<IImageLibrary>>
Creates an image context containing the pixels given in the data
argument.
The image size and number of channels are specified in the rawOptions
argument.
rawOptions.channels
<number> The number of channels used.4
is RGBA,3
is RGB.rawOptions.height
<number> The height of the image.rawOptions.width
<number> The width of the image.
The total number of bytes should be equal to height * width * channels
.
IImageLibraryCreator .loadFile (filepath)
filepath
<string> Path to an image file.- Returns: <IImageLibrary | Promise<IImageLibrary>>
If using a library exposing a way to read a file or support stream decoding such a function sould be used here.
If no streaming optimization is available this could be implemented by doing an async readFile
and passing the resulting data to loadFileData
.
IImageLibraryCreator .loadFileData (filedata)
filedata
<Uint8Array> Bytes of some image format hoping to be decoded.- Returns: <IImageLibrary | Promise<IImageLibrary>>
Creates an image context based on an existing image.
Which image encodings is supported is determined by the library or implementor.
Recommended formats are PNG
, JPEG
, and SVG
.