Files
PackControl/node_modules/loupedeck/README.md
2026-02-27 22:46:14 +01:00

358 lines
14 KiB
Markdown

Loupedeck: Node.js Interface
============================
![tests](https://github.com/foxxyz/loupedeck/actions/workflows/nodejs.yml/badge.svg?branch=master)
Unofficial Node.js API for [Loupedeck Live](https://loupedeck.com/products/loupedeck-live/), [Loupedeck Live S](https://loupedeck.com/products/loupedeck-live-s/), [Loupedeck CT](https://loupedeck.com/us/products/loupedeck-ct/) and [Razer Stream](https://www.razer.com/streaming-accessories/razer-stream-controller) controllers.
![Loupedeck Live Interface](/docs/live-front-small.png?raw=true)
![Razer Stream Controller Interface](/docs/rsc-front-small.png?raw=true)
![Razer Stream Controller X Interface](/docs/rscx-front-small.png?raw=true)
![Loupedeck Live S Interface](/docs/live-s-front-small.png?raw=true)
![Loupedeck CT Interface](/docs/ct-front-small.png?raw=true)
Supports:
* Reading button presses
* Reading knob turns
* Reading touch events
* Setting button colors
* Setting screen brightness
* Vibrating device
* Writing screen graphics
Requirements
------------
* Node 20+
* Supported Devices:
* Loupedeck Live
* Loupedeck Live S
* Loupedeck CT
* Razer Stream Controller (_"RSC"_)
* Razer Stream Controller X (_"RSCX"_)
This library has been tested with firmware versions 0.1.3, 0.1.79, 0.2.5, 0.2.8 and 0.2.23. Other versions may work.
**Note: Firmware version 0.2.26 does not seem to work on Linux. Recommendation is to downgrade to 0.2.23 (see #30)**
Installation
------------
```shell
npm install loupedeck
```
By default, Loupdeck devices uses RGB565 (16-bit) buffers for drawing (with small exceptions, see below), so the [`canvas` module](https://www.npmjs.com/package/canvas) is used to enable a more pleasant API that allows for drawing using [Canvas API](https://developer.mozilla.org/en-US/docs/Web/API/Canvas_API) callbacks.
Usage Examples
--------------
_Note: Ensure Loupedeck software is not running as it may conflict with this library_
### Automatic Discovery
```javascript
import { discover } from 'loupedeck'
// Detects and opens first connected device
const device = await discover()
// Observe connect events
device.on('connect', () => {
console.info('Connection successful!')
})
// React to button presses
device.on('down', ({ id }) => {
console.info(`Button pressed: ${id}`)
})
// React to knob turns
device.on('rotate', ({ id, delta }) => {
console.info(`Knob ${id} rotated: ${delta}`)
})
```
### Manual Instantiation
```javascript
import { LoupedeckLiveS } from 'loupedeck'
const device = new LoupedeckLiveS({ path: '/dev/tty.usbmodem101', autoConnect: false })
await device.connect()
console.info('Connection successful!')
device.on('down', ({ id }) => {
console.info(`Button pressed: ${id}`)
})
```
For all examples, see the [`examples` folder](/examples/). Running some examples requires `canvas` to be installed (see above).
📝 API Docs
-----------
### `discover() : Promise<LoupedeckDevice>`
Find the first connected Loupedeck device and return it.
Returns an instance of `LoupedeckLive`, `LoupedeckLiveS`, `LoupedeckCT`, `RazerStreamController`, `RazerStreamControllerX`, or throws an `Error` in case none or unsupported devices are found.
### Class `LoupedeckLive`
Implements and supports all methods from the [`LoupedeckDevice` interface](#interface-loupedeckdevice).
#### `new LoupedeckLive({ path : String?, host : String?, autoConnect : Boolean? })`
Create a new Loupedeck Live device interface.
Most use-cases should omit the `host`/`path` parameter, unless you're using multiple devices or know specifically which IP or device path you want to connect to. Either use `path` OR `host`, never both.
- `path`: **(Firmware 0.2.X only)** Serial device path (example: `/dev/cu.ttymodem-1332` or `COM2`) (default: autodiscover)
- `host`: **(Firmware 0.1.X only)** Host or IP address to connect to (example: `127.100.1.1`) (default: autodiscover)
- `autoConnect`: Automatically connect during construction. (default: `true`) _Set to `false` if you'd prefer to call [`connect()`](#deviceconnect--promise). yourself._
- `reconnectInterval`: How many milliseconds to wait before attempting a reconnect after a failed connection (default: `3000`) _Set to `false` to turn off automatic reconnects._
### Class `LoupedeckCT`
Same interface as [`LoupedeckLive`](#class-loupedecklive).
### Class `LoupedeckLiveS`
Same interface as [`LoupedeckLive`](#class-loupedecklive).
### Class `RazerStreamController`
Same interface as [`LoupedeckLive`](#class-loupedecklive).
### Class `RazerStreamControllerX`
Same interface as [`LoupedeckLive`](#class-loupedecklive).
Does not implement vibration or button colors.
### Interface `LoupedeckDevice`
Shared device interface. Do not instantiate this manually, use one of the above classes instead or the [`discover()` function](#discover--promiseloupedeckdevice).
All incoming messages are emitted as action events and can be subscribed to via `device.on()`.
### `LoupedeckDevice.list({ ignoreSerial : Boolean?, ignoreWebsocket : Boolean?} = {}) : Promise<Array>`
Static method to scan for and return a list of all detected devices. This includes ones which are already opened.
- `ignoreSerial`: Ignore devices which operate over serial (Firmware 0.2.X) (default: false)
- `ignoreWebsocket`: Ignore devices which operate over websocket (Firmware 0.1.X) (default: false)
Device info can be directly passed on to the constructor below.
#### Event: `'connect'`
Emitted when connection to the device succeeds. Includes an info object containing:
- `address`: Connection address (E.G. serial path or websocket address)
#### Event: `'disconnect'`
Emitted when a device disconnects for any reason. First argument for the event is an `Error` object in case of an abnormal disconnect (otherwise `undefined`).
#### Event: `'down'`
Emitted when a button or knob is pressed down.
Arguments:
- `id`: Button ID ([see `constants.js` for possible IDs](/constants.js#L3))
#### Event: `'rotate'`
Emitted when a knob is rotated.
Arguments:
- `id`: Button ID ([see `constants.js` for possible IDs](/constants.js#L3))
- `delta`: Rotation direction, `-1` for counter-clockwise, `1` for clockwise.
#### Event: `'touchstart'`
Emitted when any part of the screen is touched for the first time.
Arguments:
- `changedTouches`: Array of new [touches](#touch-objects) created during this event
- `touches`: Array of all currently held [touches](#touch-objects) on screen
#### Event: `'touchmove'`
Emitted when a touch moves across the screen.
Arguments:
- `changedTouches`: Array of [touches](#touch-objects) changed during this event
- `touches`: Array of all currently held [touches](#touch-objects) on screen
#### Event: `'touchend'`
Emitted when a touch is no longer detected.
Arguments:
- `changedTouches`: Array of [touches](#touch-objects) removed during this event
- `touches`: Array of all currently held [touches](#touch-objects) on screen (if any)
#### Event: `'up'`
Emitted when a button or knob is released.
Arguments:
- `id`: Button ID ([see `constants.js` for possible IDs](/constants.js#L3))
#### `device.close() : Promise`
Close device connection.
Returns Promise which resolves once the device has been closed.
#### `device.connect() : Promise`
Manually connect. Resolves on success.
#### `device.drawBuffer({ id : String, width : Number, height : Number, x? : Number, y? : Number, autoRefresh? : Boolean }, buffer : Buffer) : Promise`
Draw graphics to a particular area using a RGB16-565 pixel buffer.
Lower-level method if [`drawKey()`](#devicedrawkeykey--number-buffercallback--bufferfunction--promise) or [`drawScreen()`](#devicedrawscreenscreenid--string-buffercallback--bufferfunction--promise) don't meet your needs.
- `id`: Screen to write to [`left`, `center`, `right`, `knob`] _(`left` and `right` available on Loupedeck Live / RSC only)_ _(`knob` available on Loupedeck CT only)_
- `width`: Width of area to draw
- `height`: Height of area to draw
- `x`: Starting X offset (default: `0`)
- `y`: Starting Y offset (default: `0`)
- `autoRefresh`: Whether to refresh the screen after drawing (default: `true`)
- `buffer`: RGB16-565 Buffer. Should be `width * height * 2` bytes long, with each pixel represented by 2 bytes (5 bits red, 6 bits green, 5 bits blue) in little-endian (LE). _Note: Loupedeck CT knob screen is the only exception, it uses big-endian (BE)_
Returns a Promise which resolves once the command has been acknowledged by the device.
#### `device.drawCanvas({ id : String, width : Number, height : Number, x? : Number, y? : Number, autoRefresh? : Boolean }, callback : Function) : Promise`
Draw graphics to a particular area using the [Canvas API](https://developer.mozilla.org/en-US/docs/Web/API/CanvasRenderingContext2D).
Lower-level method if [`drawKey()`](#devicedrawkeykey--number-buffercallback--bufferfunction--promise) or [`drawScreen()`](#devicedrawscreenscreenid--string-buffercallback--bufferfunction--promise) don't meet your needs.
- `id`: Screen to write to [`left`, `center`, `right`, `knob`] _(`left` and `right` available on Loupedeck Live / RSC only)_ _(`knob` available on Loupedeck CT only)_
- `width`: Width of area to draw
- `height`: Height of area to draw
- `x`: Starting X offset (default: `0`)
- `y`: Starting Y offset (default: `0`)
- `autoRefresh`: Whether to refresh the screen after drawing (default: `true`)
- `callback`: Function to handle draw calls. Receives the following arguments:
1. `context`: [2d canvas graphics context](https://developer.mozilla.org/en-US/docs/Web/API/CanvasRenderingContext2D)
2. `width`: Width of drawing area
3. `height`: Height of drawing area
Returns a Promise which resolves once the command has been acknowledged by the device.
#### `device.drawKey(key : Number, buffer/callback : Buffer/Function) : Promise`
Draw graphics to a specific key.
Second argument can be either a RGB16-565 buffer or a callback. Width and height of callback will typically be `90`, as keys are mostly 90x90px (_RSCX_ being the exception - those keys are 96x96px).
- `key`: Key index to write to ([0-11] on _Loupedeck Live/Loupedeck CT/RSC_, [0-14] on _Loupedeck Live S_ and _RSCX_)
- `buffer`: RGB16-565 Buffer
- `callback`: Function to handle draw calls. Receives the following arguments:
1. `context`: [2d canvas graphics context](https://developer.mozilla.org/en-US/docs/Web/API/CanvasRenderingContext2D)
2. `width`: Width of drawing area
3. `height`: Height of drawing area
Returns a Promise which resolves once the command has been acknowledged by the device.
#### `device.drawScreen(screenID : String, buffer/callback : Buffer/Function) : Promise`
Draw graphics to a specific screen. Screen sizes are as follows:
Loupedeck CT:
* `left`: 60x270px
* `center`: 360x270px
* `right`: 60x270px
* `knob`: 240x240px _(Note: uses big-endian byte order!)_
Loupedeck Live / Razer Stream Controller:
* `left`: 60x270px
* `center`: 360x270px
* `right`: 60x270px
Loupedeck Live S:
* `center`: 480x270px
Razer Stream Controller X:
* `center`: 480x288px
Second argument can be either a RGB16-565 buffer or a callback.
- `screenID`: Screen to write to [`left`, `center`, `right`, `knob`] _(`left` and `right` available on Loupedeck Live and Razer Stream Controller only)_ _(`knob` available on Loupedeck CT only)_
- `buffer`: RGB16-565 Buffer (BE for `knob`, LE otherwise)
- `callback`: Function to handle draw calls. Receives the following arguments:
1. `context`: [2d canvas graphics context](https://developer.mozilla.org/en-US/docs/Web/API/CanvasRenderingContext2D)
2. `width`: Width of drawing area
3. `height`: Height of drawing area
Returns a Promise which resolves once the command has been acknowledged by the device.
#### `device.getInfo() : Promise`
Request device information. Returns a promise resolving to object containing:
- `serial`: Device serial number
- `version`: Firmware version
If the device is not connected, the promise will reject.
#### `device.setBrightness(brightness : Number) : Promise`
Set screen brightness.
- `brightness`: Float between (0, 1) (`0` would turn the screen off, `1` for full brightness)
Returns a Promise which resolves once the command has been acknowledged by the device.
#### `device.setButtonColor({ id : String, color : String }) : Promise`
Set a button LED to a particular color. _(Unavailable on RSCX)_
- `id`: Button ID (possible choices: 0-7 on _Loupedeck Live/CT/RSC_, 0-4 on _Loupedeck Live S_, [see `BUTTONS` for _Loupedeck CT_ square buttons](/constants.js#L19))
- `color`: Any [valid CSS color string](https://github.com/colorjs/color-parse#parsed-strings)
Returns a Promise which resolves once the command has been acknowledged by the device.
#### `device.vibrate(pattern? : byte) : Promise`
Make device vibrate. _(Unavailable on RSCX)_
- `pattern`: A valid vibration pattern ([see `HAPTIC` for valid patterns](/constants.js#L57)) (default: `HAPTIC.SHORT`)
Returns a Promise which resolves once the command has been acknowledged by the device.
### Touch Objects
Touch objects are emitted in the [`touchstart`](#event-touchstart), [`touchmove`](#event-touchmove), and [`touchend`](#event-touchend) events and have the following properties:
+ `id`: Unique touch identifier
+ `x`: Screen X-coordinate ([0, 480])
+ `y`: Screen Y-coordinate ([0, 270])
+ `target`:
* `screen`: Identifier of screen this touch was detected on ([`left`, `center`, `right`, `knob`]) (`center` only on _Loupedeck Live S_, `knob` only on _Loupedeck CT_)
* `key`: Index of key touched ([0-11] on _Loupedeck Live/CT/RSC_, [0-14] on _Loupedeck Live S_/_RSCX_)
Contributing & Tests
--------------------
1. Install development dependencies: `npm install`
2. Run tests: `npm test`
Thanks
------
Big thanks go out to [Max Maischein's earlier work in Perl](https://github.com/Corion/HID-LoupedeckCT) on this topic.
License
-------
MIT