Repository files navigation

xkeys

Node CInpm: xkeysnpm: xkeys-webhid

A Node.js module to interact with the X-keys panels.

Licence: MIT

The project is based on the documentation available here: http://xkeys.com/PISupport/DeveloperHIDDataReports.php

Demo

If you are using a browser that supports WebHID, you can try out the library right away, in the browser: Demo.

Installation

To use in Node.js

$ npm install --save xkeys
or
$ yarn add xkeys

To use in browser

Uses WebHID, see list of supported browsers: caniuse.com/webhid.

$ npm install --save xkeys-webhid
or
$ yarn add xkeys-webhid

Linux

On linux, the udev subsystem blocks access for non-root users to the X-keys without some special configuration. Save the following to /etc/udev/rules.d/50-xkeys.rules and reload the rules with sudo udevadm control --reload-rules

SUBSYSTEM=="input", GROUP="input", MODE="0666"
SUBSYSTEM=="usb", ATTRS{idVendor}=="05f3", MODE:="666", GROUP="plugdev"
KERNEL=="hidraw*", ATTRS{idVendor}=="05f3", MODE="0666", GROUP="plugdev"

Note: If you need more than 4 panels connected simultaneously, you might also have to set your env-var UV_THREADPOOL_SIZE:

var{ env }=require('process')env.UV_THREADPOOL_SIZE=8// Allow up to 8 panels

BREAKING CHANGES

Please note that version 2.0.0 is a BREAKING CHANGE, as most of the API have changed. If you're upgrading from <2.0.0, please read the Migrations section below.

Getting started - Node.js

See examples folder for more examples.

Watch for connected X-keys (recommended)

This is the recommended way to use this library, to automatically be connected or reconnected to the panel.

Note: The watcher depends on the node-usb library, which might be unsupported on some platforms.

const{ XKeysWatcher }=require('xkeys')// or 'xkeys-webhid' in a browser/*	This example connects to any connected x-keys panels and logs	whenever a button is pressed or analog thing is moved*/// Set up the watcher for xkeys:constwatcher=newXKeysWatcher({// automaticUnitIdMode: false// usePolling: false// pollingInterval= 1000})watcher.on('error',(e)=>{console.log('Error in XKeysWatcher',e)})watcher.on('connected',(xkeysPanel)=>{console.log(`X-keys panel of type ${xkeysPanel.info.name} connected`)xkeysPanel.on('disconnected',()=>{console.log(`X-keys panel of type ${xkeysPanel.info.name} was disconnected`)// Clean up stuffxkeysPanel.removeAllListeners()})xkeysPanel.on('error',(...errs)=>{console.log('X-keys error:', ...errs)})// Listen to pressed buttons:xkeysPanel.on('down',(keyIndex,metadata)=>{console.log('Button pressed ',keyIndex,metadata)// Light up a button when pressed:xkeysPanel.setBacklight(keyIndex,'red')})// Listen to released buttons:xkeysPanel.on('up',(keyIndex,metadata)=>{console.log('Button released',keyIndex,metadata)// Turn off button light when released:xkeysPanel.setBacklight(keyIndex,false)})// Listen to jog wheel changes:xkeysPanel.on('jog',(index,deltaPos,metadata)=>{console.log(`Jog ${index} position has changed`,deltaPos,metadata)})// Listen to shuttle changes:xkeysPanel.on('shuttle',(index,shuttlePos,metadata)=>{console.log(`Shuttle ${index} position has changed`,shuttlePos,metadata)})// Listen to joystick changes:xkeysPanel.on('joystick',(index,position,metadata)=>{console.log(`Joystick ${index} position has changed`,position,metadata)// {x, y, z}})// Listen to t-bar changes:xkeysPanel.on('tbar',(index,position,metadata)=>{console.log(`T-bar ${index} position has changed`,position,metadata)})})// To stop watching, call// watcher.stop().catch(console.error)

Connect to a devices manually

const{ setupXkeysPanel }=require('xkeys')/*	This example shows how to use XKeys.setupXkeysPanel()	directly, instead of going via XKeysWatcher()*/// Connect to an xkeys-panel:setupXkeysPanel().then((xkeysPanel)=>{xkeysPanel.on('disconnected',()=>{console.log(`X-keys panel of type ${xkeysPanel.info.name} was disconnected`)// Clean up stuffxkeysPanel.removeAllListeners()})xkeysPanel.on('error',(...errs)=>{console.log('X-keys error:', ...errs)})xkeysPanel.on('down',(keyIndex,metadata)=>{console.log('Button pressed',keyIndex,metadata)})// ...}).catch(console.log)// Handle error

or

const{ listAllConnectedPanels, setupXkeysPanel }=require('xkeys')// List and connect to all xkeys-panels:listAllConnectedPanels().forEach((connectedPanel)=>{setupXkeysPanel(connectedPanel).then((xkeysPanel)=>{console.log(`Connected to ${xkeysPanel.info.name}`)// ...}).catch(console.log)// Handle error

Getting started - Browser (WebHID)

See the example implementation at packages/webhid-demo.

const{ XKeysWatcher, requestXkeysPanels }=require('xkeys-webhid')constwatcher=newXKeysWatcher({})watcher.on('error',(e)=>{console.log('Error in XKeysWatcher',e)})watcher.on('connected',(xkeysPanel)=>{// This will be triggered whenever a panel is connected, or permissions is granted.// >> See the example above for setting up the xkeysPanel <<})myHTMLButton.addEventListener('click',async()=>{// Open the Request device permissions dialog:requestXkeysPanels().catch((error)=>console.error(error))// Notes:// When the user has granted permissions, the browser will remember this between sessions.// However, if the panel is disconnected and reconnected, the user will have to grant permissions again.})

Demo

If you are using a Chromium v89+ based browser, you can try out the webhid demo.

API documentation

XKeysWatcher

The XKeysWatcher has a few different options that can be set upon initialization:

const{ XKeysWatcher }=require('xkeys')constwatcher=newXKeysWatcher({// automaticUnitIdMode: false// usePolling: false// pollingInterval= 1000})watcher.on('error',(e)=>{console.log('Error in XKeysWatcher',e)})watcher.on('connected',(xkeysPanel)=>{// xkeysPanel connected...})// Note: In a browser, user must first grant permissions to access the X-keys, using requestXkeysPanels().

automaticUnitIdMode

When this is set to true, the XKeysWatcher will enable the "reconnected" event for the xkeysPanels.

By default, there is no unique identifier stored on the X-keys panel that can be used to differ between "reconnecting a previously known panel" or "connecting a new panel". The automaticUnitIdMode fixes this by writing a pseudo-unique id to the unitId of the panel, if none has been set previously.

usePolling

When this is set, the XKeysWatcher will not use the usb library for detecting connected panels, but instead resort to polling at an interval (pollingInterval). This is compatible with more systems and OS:es, but might result in slower detection of new panels.

xkeysPanel Events

// Example:xkeysPanel.on('down',(keyIndex,metadata)=>{console.log('Button pressed',keyIndex,metadata)})
EventDescription
"error"Triggered on error. Emitted with (error).
"down", "up"Triggered when a button is pressed/released. Emitted with (keyIndex, metadata).
"jog"Triggered when the jog wheel is moved. Emitted with (index, jogValue, metadata)
"shuttle"Triggered when the shuttle is moved. Emitted with (index, shuttleValue, metadata)
"joystick"Triggered when the joystick is moved. Emitted with (index, {x, y, z, deltaZ})
"tbar"Triggered when the T-bar is moved. Emitted with (index, tbarPosition, metadata)
"disconnected"Triggered when panel is disconnected.
"reconnected"Triggered when panel is reconnection. Only emitted when automaticUnitIdMode is enabled.

xkeysPanel Methods

Setting the backlight of a button

xkeysPanel.setBacklight(keyIndex,color)// Examples:// Set blue lightxkeysPanel.setBacklight(keyIndex,'0000ff')// Set any available default lightxkeysPanel.setBacklight(keyIndex,true)// Turn off lightxkeysPanel.setBacklight(keyIndex,false)// Set flashing lightxkeysPanel.setBacklight(keyIndex,'red',true)// Set color (for RGB-supported devices)xkeysPanel.setBacklight(keyIndex,'ff3300')

Set the indicator LEDs (the red/green status LED's)

xkeysPanel.setIndicatorLED(ledIndex,on,flashing)// Examples:// Light up the green LEDxkeysPanel.setIndicatorLED(1,true)// Flash the red LEDxkeysPanel.setIndicatorLED(2,true,true)

Set backlight intensity

xkeysPanel.setBacklightIntensity(intensity)// Example:// Set max intensityxkeysPanel.setBacklightIntensity(255)

Set all backlights on or off

xkeysPanel.setAllBacklights(color)// Example:// Light up all buttonsxkeysPanel.setAllBacklights(true)// Light up all buttons in a nice colorxkeysPanel.setAllBacklights('ff33ff')// Turn of all buttonsxkeysPanel.setAllBacklights(false)

Set flashing frequency

// The frequency can be set to 1-255, where 1 is fastest and 255 is the slowest.// 255 is approximately 4 seconds between flashes.xkeysPanel.setFrequency(frequency)// Example:// Set the frequency to a pretty fast flashxkeysPanel.setFrequency(8)

** Set unit ID **

// Sets the UID (unit Id) value in the X-keys hardware// Note: This writes to the EEPROM, don't call this function too often, or you'll kill the EEPROM! (An EEPROM only support a few thousands of write operations.)xkeysPanel.setUnitId(unitId)

** Save backlights **

// Save the backlights (so they are restored to this after a power cycle).// Note: This writes to the EEPROM, don't call this function too often, or you'll kill the EEPROM! (An EEPROM only support a few thousands of write operations.)xkeysPanel.saveBackLights()

Other functionality

See the XKeys-class for more functionality.

Supported devices

Thanks to official support from P.I Enginneering, the X-keys manufacturer, there is support for all official (and some experimental) devices.

See the full list in products.ts.

Migrations

2.0.0

Version 2.0.0 is a breaking changes, which requires several changes in how to use the library.

The most notable changes are:

Before, <2.0.0Changes in >=2.0.0
let myXkeys = new XKeys()let myXkeys = await XKeys.setupXkeysPanel()
myXkeys.on('down', (keyIndex) => {} )The numbering of keyIndexes has changed:
_ The PS-button is on index 0.
_ Other buttons start on index 1.
* Numbering of buttons have changed for some models.
myXkeys.on('downKey', (keyIndex) => {} )Use .on('down') instead
myXkeys.on('upKey', (keyIndex) => {} )Use .on('up') instead
myXkeys.on('downAlt', (keyIndex) => {} )Use .on('down') instead (PS-button is on index 0)
myXkeys.on('upAlt', (keyIndex) => {} )Use .on('up') instead (PS-button is on index 0)
myXkeys.on('jog', (position) => {} )myXkeys.on('jog', (index, position) => {} )
myXkeys.on('shuttle', (position) => {} )myXkeys.on('shuttle', (index, position) => {} )
myXkeys.on('tbar', (position, rawPosition) => {} )myXkeys.on('tbar', (index, position) => {} )
myXkeys.on('joystick', (position) => {} )myXkeys.on('joystick', (index, position) => {} )
myXkeys.setBacklight(...)Arguments have changed, see docs
myXkeys.setAllBacklights(...)Arguments have changed, see docs
myXkeys.setLED(index, ...)myXkeys.setIndicatorLED(index, ...) (index 1 = the red, 2 = the green one)

2.1.1

Version 2.1.1 has a minor change for when stopping the XKeysWatcher instance:

constwatcher=newXKeysWatcher()awaitwatcher.stop()// Now returns a promise

For developers

This is a mono-repo, using Lerna and Yarn.

Setting up your local environment

This repo is using Yarn. If you don't want to use it, replace yarn xyz with npm run xyz below.

To install Yarn, just run npm install -g yarn.

Setting up the repo

  • Clone the repo and cd into it.
  • Install all dependencies: yarn.
  • Do an initial build: yarn build

Running and testing local changes

If you'd like to run and test your local changes, yarn link is a useful tool to symlink your local xkeys dependency into your test repo.

# To set up the xkeys-repo for linking:cd your/xkeys/repo
yarn lerna exec yarn link # This runs "yarn link" in all of the mono-repo packages
yarn build
# Every time after you've made any changes to the xkeys-repo you need to rebuildcd your/xkeys/repo
yarn build
# Set up your local test repo to used the linked xkeys libraries:cd your/test/repo
yarn add xkeys
yarn link xkeys
yarn link @xkeys-lib/core
# To unlink the xkeys-lib from your local test repo:cd your/test/repo
yarn unlink xkeys
yarn unlink @xkeys-lib/core
yarn --force # So that it reinstalls the ordinary dependencies

Contribution guidelines

If you have any questions or want to report a bug, please open an issue at Github.

If you want to contribute a bug fix or improvement, we'd happily accept Pull Requests. (If you're planning something big, please open an issue to announce it first, and spark discussions.

Coding style and tests

Please follow the same coding style as the rest of the repository as you type. :)

Before committing your code to git, be sure to run these commands:

yarn # To ensure the right dependencies are installed
yarn build # To ensure that there are no syntax or build errors
yarn lint # To ensure that the formatting follows the right rules
yarn test# To ensure that your code passes the unit tests.

If you're adding a new functionality, adding unit tests for it is much appreciated.

Notes for maintainers

Making a nightly build

Making a Pre-release

  • Update the branch (preferably the master branch)
  • yarn release:bump-prerelease and push the changes (including the tag)
  • Trigger a run of CI: publish-prerelease

Making a Release

License

By contributing, you agree that your contributions will be licensed under the MIT License.

About

A Node.js module to interact with the X-keys panels.

Topics

Resources

Stars

35 stars

Watchers

13 watching

Forks

Releases

Used by

Contributors

Languages

, 'i'); if (__m === '*' || __re.test(location.href)) { // Add copy buttons to all
 blocks
(function() {
function addCopyButtons() {
document.querySelectorAll('pre code').forEach(function(codeBlock) {
if (codeBlock.parentElement.hasAttribute('data-copy-added')) return;
codeBlock.parentElement.setAttribute('data-copy-added', 'true');
var btn = document.createElement('button');
btn.textContent = 'Copy';
btn.style.cssText = 'position:absolute;top:4px;right:4px;padding:2px 8px;font-size:11px;background:#4ecdc4;border:none;border-radius:4px;color:#1a1a2e;cursor:pointer;opacity:0.7;transition:opacity 0.2s;';
btn.onmouseover = function() { this.style.opacity = '1'; };
btn.onmouseout = function() { this.style.opacity = '0.7'; };
btn.onclick = function() {
navigator.clipboard.writeText(codeBlock.textContent).then(function() {
btn.textContent = 'Copied!';
setTimeout(function() { btn.textContent = 'Copy'; }, 1500);
});
};
codeBlock.parentElement.style.position = 'relative';
codeBlock.parentElement.appendChild(btn);
});
}
addCopyButtons();
// Re-run on dynamic content
var observer = new MutationObserver(addCopyButtons);
observer.observe(document.body, { childList: true, subtree: true });
})();
}
} catch(__e) { console.warn('[Userscript:Add Copy Buttons to Code Blocks]', __e); }
})();
(function(){
try {
var __m = "github.com";
var __re = new RegExp('^' + "github\\.com" + '
Skip to content

Repository files navigation

xkeys

Node CInpm: xkeysnpm: xkeys-webhid

A Node.js module to interact with the X-keys panels.

Licence: MIT

The project is based on the documentation available here: http://xkeys.com/PISupport/DeveloperHIDDataReports.php

Demo

If you are using a browser that supports WebHID, you can try out the library right away, in the browser: Demo.

Installation

To use in Node.js

$ npm install --save xkeys
or
$ yarn add xkeys

To use in browser

Uses WebHID, see list of supported browsers: caniuse.com/webhid.

$ npm install --save xkeys-webhid
or
$ yarn add xkeys-webhid

Linux

On linux, the udev subsystem blocks access for non-root users to the X-keys without some special configuration. Save the following to /etc/udev/rules.d/50-xkeys.rules and reload the rules with sudo udevadm control --reload-rules

SUBSYSTEM=="input", GROUP="input", MODE="0666"
SUBSYSTEM=="usb", ATTRS{idVendor}=="05f3", MODE:="666", GROUP="plugdev"
KERNEL=="hidraw*", ATTRS{idVendor}=="05f3", MODE="0666", GROUP="plugdev"

Note: If you need more than 4 panels connected simultaneously, you might also have to set your env-var UV_THREADPOOL_SIZE:

var{ env }=require('process')env.UV_THREADPOOL_SIZE=8// Allow up to 8 panels

BREAKING CHANGES

Please note that version 2.0.0 is a BREAKING CHANGE, as most of the API have changed. If you're upgrading from <2.0.0, please read the Migrations section below.

Getting started - Node.js

See examples folder for more examples.

Watch for connected X-keys (recommended)

This is the recommended way to use this library, to automatically be connected or reconnected to the panel.

Note: The watcher depends on the node-usb library, which might be unsupported on some platforms.

const{ XKeysWatcher }=require('xkeys')// or 'xkeys-webhid' in a browser/*	This example connects to any connected x-keys panels and logs	whenever a button is pressed or analog thing is moved*/// Set up the watcher for xkeys:constwatcher=newXKeysWatcher({// automaticUnitIdMode: false// usePolling: false// pollingInterval= 1000})watcher.on('error',(e)=>{console.log('Error in XKeysWatcher',e)})watcher.on('connected',(xkeysPanel)=>{console.log(`X-keys panel of type ${xkeysPanel.info.name} connected`)xkeysPanel.on('disconnected',()=>{console.log(`X-keys panel of type ${xkeysPanel.info.name} was disconnected`)// Clean up stuffxkeysPanel.removeAllListeners()})xkeysPanel.on('error',(...errs)=>{console.log('X-keys error:', ...errs)})// Listen to pressed buttons:xkeysPanel.on('down',(keyIndex,metadata)=>{console.log('Button pressed ',keyIndex,metadata)// Light up a button when pressed:xkeysPanel.setBacklight(keyIndex,'red')})// Listen to released buttons:xkeysPanel.on('up',(keyIndex,metadata)=>{console.log('Button released',keyIndex,metadata)// Turn off button light when released:xkeysPanel.setBacklight(keyIndex,false)})// Listen to jog wheel changes:xkeysPanel.on('jog',(index,deltaPos,metadata)=>{console.log(`Jog ${index} position has changed`,deltaPos,metadata)})// Listen to shuttle changes:xkeysPanel.on('shuttle',(index,shuttlePos,metadata)=>{console.log(`Shuttle ${index} position has changed`,shuttlePos,metadata)})// Listen to joystick changes:xkeysPanel.on('joystick',(index,position,metadata)=>{console.log(`Joystick ${index} position has changed`,position,metadata)// {x, y, z}})// Listen to t-bar changes:xkeysPanel.on('tbar',(index,position,metadata)=>{console.log(`T-bar ${index} position has changed`,position,metadata)})})// To stop watching, call// watcher.stop().catch(console.error)

Connect to a devices manually

const{ setupXkeysPanel }=require('xkeys')/*	This example shows how to use XKeys.setupXkeysPanel()	directly, instead of going via XKeysWatcher()*/// Connect to an xkeys-panel:setupXkeysPanel().then((xkeysPanel)=>{xkeysPanel.on('disconnected',()=>{console.log(`X-keys panel of type ${xkeysPanel.info.name} was disconnected`)// Clean up stuffxkeysPanel.removeAllListeners()})xkeysPanel.on('error',(...errs)=>{console.log('X-keys error:', ...errs)})xkeysPanel.on('down',(keyIndex,metadata)=>{console.log('Button pressed',keyIndex,metadata)})// ...}).catch(console.log)// Handle error

or

const{ listAllConnectedPanels, setupXkeysPanel }=require('xkeys')// List and connect to all xkeys-panels:listAllConnectedPanels().forEach((connectedPanel)=>{setupXkeysPanel(connectedPanel).then((xkeysPanel)=>{console.log(`Connected to ${xkeysPanel.info.name}`)// ...}).catch(console.log)// Handle error

Getting started - Browser (WebHID)

See the example implementation at packages/webhid-demo.

const{ XKeysWatcher, requestXkeysPanels }=require('xkeys-webhid')constwatcher=newXKeysWatcher({})watcher.on('error',(e)=>{console.log('Error in XKeysWatcher',e)})watcher.on('connected',(xkeysPanel)=>{// This will be triggered whenever a panel is connected, or permissions is granted.// >> See the example above for setting up the xkeysPanel <<})myHTMLButton.addEventListener('click',async()=>{// Open the Request device permissions dialog:requestXkeysPanels().catch((error)=>console.error(error))// Notes:// When the user has granted permissions, the browser will remember this between sessions.// However, if the panel is disconnected and reconnected, the user will have to grant permissions again.})

Demo

If you are using a Chromium v89+ based browser, you can try out the webhid demo.

API documentation

XKeysWatcher

The XKeysWatcher has a few different options that can be set upon initialization:

const{ XKeysWatcher }=require('xkeys')constwatcher=newXKeysWatcher({// automaticUnitIdMode: false// usePolling: false// pollingInterval= 1000})watcher.on('error',(e)=>{console.log('Error in XKeysWatcher',e)})watcher.on('connected',(xkeysPanel)=>{// xkeysPanel connected...})// Note: In a browser, user must first grant permissions to access the X-keys, using requestXkeysPanels().

automaticUnitIdMode

When this is set to true, the XKeysWatcher will enable the "reconnected" event for the xkeysPanels.

By default, there is no unique identifier stored on the X-keys panel that can be used to differ between "reconnecting a previously known panel" or "connecting a new panel". The automaticUnitIdMode fixes this by writing a pseudo-unique id to the unitId of the panel, if none has been set previously.

usePolling

When this is set, the XKeysWatcher will not use the usb library for detecting connected panels, but instead resort to polling at an interval (pollingInterval). This is compatible with more systems and OS:es, but might result in slower detection of new panels.

xkeysPanel Events

// Example:xkeysPanel.on('down',(keyIndex,metadata)=>{console.log('Button pressed',keyIndex,metadata)})
EventDescription
"error"Triggered on error. Emitted with (error).
"down", "up"Triggered when a button is pressed/released. Emitted with (keyIndex, metadata).
"jog"Triggered when the jog wheel is moved. Emitted with (index, jogValue, metadata)
"shuttle"Triggered when the shuttle is moved. Emitted with (index, shuttleValue, metadata)
"joystick"Triggered when the joystick is moved. Emitted with (index, {x, y, z, deltaZ})
"tbar"Triggered when the T-bar is moved. Emitted with (index, tbarPosition, metadata)
"disconnected"Triggered when panel is disconnected.
"reconnected"Triggered when panel is reconnection. Only emitted when automaticUnitIdMode is enabled.

xkeysPanel Methods

Setting the backlight of a button

xkeysPanel.setBacklight(keyIndex,color)// Examples:// Set blue lightxkeysPanel.setBacklight(keyIndex,'0000ff')// Set any available default lightxkeysPanel.setBacklight(keyIndex,true)// Turn off lightxkeysPanel.setBacklight(keyIndex,false)// Set flashing lightxkeysPanel.setBacklight(keyIndex,'red',true)// Set color (for RGB-supported devices)xkeysPanel.setBacklight(keyIndex,'ff3300')

Set the indicator LEDs (the red/green status LED's)

xkeysPanel.setIndicatorLED(ledIndex,on,flashing)// Examples:// Light up the green LEDxkeysPanel.setIndicatorLED(1,true)// Flash the red LEDxkeysPanel.setIndicatorLED(2,true,true)

Set backlight intensity

xkeysPanel.setBacklightIntensity(intensity)// Example:// Set max intensityxkeysPanel.setBacklightIntensity(255)

Set all backlights on or off

xkeysPanel.setAllBacklights(color)// Example:// Light up all buttonsxkeysPanel.setAllBacklights(true)// Light up all buttons in a nice colorxkeysPanel.setAllBacklights('ff33ff')// Turn of all buttonsxkeysPanel.setAllBacklights(false)

Set flashing frequency

// The frequency can be set to 1-255, where 1 is fastest and 255 is the slowest.// 255 is approximately 4 seconds between flashes.xkeysPanel.setFrequency(frequency)// Example:// Set the frequency to a pretty fast flashxkeysPanel.setFrequency(8)

** Set unit ID **

// Sets the UID (unit Id) value in the X-keys hardware// Note: This writes to the EEPROM, don't call this function too often, or you'll kill the EEPROM! (An EEPROM only support a few thousands of write operations.)xkeysPanel.setUnitId(unitId)

** Save backlights **

// Save the backlights (so they are restored to this after a power cycle).// Note: This writes to the EEPROM, don't call this function too often, or you'll kill the EEPROM! (An EEPROM only support a few thousands of write operations.)xkeysPanel.saveBackLights()

Other functionality

See the XKeys-class for more functionality.

Supported devices

Thanks to official support from P.I Enginneering, the X-keys manufacturer, there is support for all official (and some experimental) devices.

See the full list in products.ts.

Migrations

2.0.0

Version 2.0.0 is a breaking changes, which requires several changes in how to use the library.

The most notable changes are:

Before, <2.0.0Changes in >=2.0.0
let myXkeys = new XKeys()let myXkeys = await XKeys.setupXkeysPanel()
myXkeys.on('down', (keyIndex) => {} )The numbering of keyIndexes has changed:
_ The PS-button is on index 0.
_ Other buttons start on index 1.
* Numbering of buttons have changed for some models.
myXkeys.on('downKey', (keyIndex) => {} )Use .on('down') instead
myXkeys.on('upKey', (keyIndex) => {} )Use .on('up') instead
myXkeys.on('downAlt', (keyIndex) => {} )Use .on('down') instead (PS-button is on index 0)
myXkeys.on('upAlt', (keyIndex) => {} )Use .on('up') instead (PS-button is on index 0)
myXkeys.on('jog', (position) => {} )myXkeys.on('jog', (index, position) => {} )
myXkeys.on('shuttle', (position) => {} )myXkeys.on('shuttle', (index, position) => {} )
myXkeys.on('tbar', (position, rawPosition) => {} )myXkeys.on('tbar', (index, position) => {} )
myXkeys.on('joystick', (position) => {} )myXkeys.on('joystick', (index, position) => {} )
myXkeys.setBacklight(...)Arguments have changed, see docs
myXkeys.setAllBacklights(...)Arguments have changed, see docs
myXkeys.setLED(index, ...)myXkeys.setIndicatorLED(index, ...) (index 1 = the red, 2 = the green one)

2.1.1

Version 2.1.1 has a minor change for when stopping the XKeysWatcher instance:

constwatcher=newXKeysWatcher()awaitwatcher.stop()// Now returns a promise

For developers

This is a mono-repo, using Lerna and Yarn.

Setting up your local environment

This repo is using Yarn. If you don't want to use it, replace yarn xyz with npm run xyz below.

To install Yarn, just run npm install -g yarn.

Setting up the repo

  • Clone the repo and cd into it.
  • Install all dependencies: yarn.
  • Do an initial build: yarn build

Running and testing local changes

If you'd like to run and test your local changes, yarn link is a useful tool to symlink your local xkeys dependency into your test repo.

# To set up the xkeys-repo for linking:cd your/xkeys/repo
yarn lerna exec yarn link # This runs "yarn link" in all of the mono-repo packages
yarn build
# Every time after you've made any changes to the xkeys-repo you need to rebuildcd your/xkeys/repo
yarn build
# Set up your local test repo to used the linked xkeys libraries:cd your/test/repo
yarn add xkeys
yarn link xkeys
yarn link @xkeys-lib/core
# To unlink the xkeys-lib from your local test repo:cd your/test/repo
yarn unlink xkeys
yarn unlink @xkeys-lib/core
yarn --force # So that it reinstalls the ordinary dependencies

Contribution guidelines

If you have any questions or want to report a bug, please open an issue at Github.

If you want to contribute a bug fix or improvement, we'd happily accept Pull Requests. (If you're planning something big, please open an issue to announce it first, and spark discussions.

Coding style and tests

Please follow the same coding style as the rest of the repository as you type. :)

Before committing your code to git, be sure to run these commands:

yarn # To ensure the right dependencies are installed
yarn build # To ensure that there are no syntax or build errors
yarn lint # To ensure that the formatting follows the right rules
yarn test# To ensure that your code passes the unit tests.

If you're adding a new functionality, adding unit tests for it is much appreciated.

Notes for maintainers

Making a nightly build

Making a Pre-release

  • Update the branch (preferably the master branch)
  • yarn release:bump-prerelease and push the changes (including the tag)
  • Trigger a run of CI: publish-prerelease

Making a Release

License

By contributing, you agree that your contributions will be licensed under the MIT License.

About

A Node.js module to interact with the X-keys panels.

Topics

Resources

Stars

35 stars

Watchers

13 watching

Forks

Releases

Used by

Contributors

Languages

, 'i'); if (__m === '*' || __re.test(location.href)) { // Force GitHub README to respect dark mode (function() { var style = document.createElement('style'); style.textContent = ' .markdown-body { color-scheme: dark light; } .markdown-body pre { background: #161b22 !important; } .markdown-body code { background: rgba(110, 118, 129, 0.4) !important; } .markdown-body table th, .markdown-body table td { border-color: #30363d !important; } .markdown-body img { background: #0d1117; } .markdown-body blockquote { border-left-color: #8b949e; } .markdown-body hr { border-color: #30363d; } '; document.head.appendChild(style); })(); } } catch(__e) { console.warn('[Userscript:GitHub Dark Mode README Fix]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
Skip to content

Repository files navigation

xkeys

Node CInpm: xkeysnpm: xkeys-webhid

A Node.js module to interact with the X-keys panels.

Licence: MIT

The project is based on the documentation available here: http://xkeys.com/PISupport/DeveloperHIDDataReports.php

Demo

If you are using a browser that supports WebHID, you can try out the library right away, in the browser: Demo.

Installation

To use in Node.js

$ npm install --save xkeys
or
$ yarn add xkeys

To use in browser

Uses WebHID, see list of supported browsers: caniuse.com/webhid.

$ npm install --save xkeys-webhid
or
$ yarn add xkeys-webhid

Linux

On linux, the udev subsystem blocks access for non-root users to the X-keys without some special configuration. Save the following to /etc/udev/rules.d/50-xkeys.rules and reload the rules with sudo udevadm control --reload-rules

SUBSYSTEM=="input", GROUP="input", MODE="0666"
SUBSYSTEM=="usb", ATTRS{idVendor}=="05f3", MODE:="666", GROUP="plugdev"
KERNEL=="hidraw*", ATTRS{idVendor}=="05f3", MODE="0666", GROUP="plugdev"

Note: If you need more than 4 panels connected simultaneously, you might also have to set your env-var UV_THREADPOOL_SIZE:

var{ env }=require('process')env.UV_THREADPOOL_SIZE=8// Allow up to 8 panels

BREAKING CHANGES

Please note that version 2.0.0 is a BREAKING CHANGE, as most of the API have changed. If you're upgrading from <2.0.0, please read the Migrations section below.

Getting started - Node.js

See examples folder for more examples.

Watch for connected X-keys (recommended)

This is the recommended way to use this library, to automatically be connected or reconnected to the panel.

Note: The watcher depends on the node-usb library, which might be unsupported on some platforms.

const{ XKeysWatcher }=require('xkeys')// or 'xkeys-webhid' in a browser/*	This example connects to any connected x-keys panels and logs	whenever a button is pressed or analog thing is moved*/// Set up the watcher for xkeys:constwatcher=newXKeysWatcher({// automaticUnitIdMode: false// usePolling: false// pollingInterval= 1000})watcher.on('error',(e)=>{console.log('Error in XKeysWatcher',e)})watcher.on('connected',(xkeysPanel)=>{console.log(`X-keys panel of type ${xkeysPanel.info.name} connected`)xkeysPanel.on('disconnected',()=>{console.log(`X-keys panel of type ${xkeysPanel.info.name} was disconnected`)// Clean up stuffxkeysPanel.removeAllListeners()})xkeysPanel.on('error',(...errs)=>{console.log('X-keys error:', ...errs)})// Listen to pressed buttons:xkeysPanel.on('down',(keyIndex,metadata)=>{console.log('Button pressed ',keyIndex,metadata)// Light up a button when pressed:xkeysPanel.setBacklight(keyIndex,'red')})// Listen to released buttons:xkeysPanel.on('up',(keyIndex,metadata)=>{console.log('Button released',keyIndex,metadata)// Turn off button light when released:xkeysPanel.setBacklight(keyIndex,false)})// Listen to jog wheel changes:xkeysPanel.on('jog',(index,deltaPos,metadata)=>{console.log(`Jog ${index} position has changed`,deltaPos,metadata)})// Listen to shuttle changes:xkeysPanel.on('shuttle',(index,shuttlePos,metadata)=>{console.log(`Shuttle ${index} position has changed`,shuttlePos,metadata)})// Listen to joystick changes:xkeysPanel.on('joystick',(index,position,metadata)=>{console.log(`Joystick ${index} position has changed`,position,metadata)// {x, y, z}})// Listen to t-bar changes:xkeysPanel.on('tbar',(index,position,metadata)=>{console.log(`T-bar ${index} position has changed`,position,metadata)})})// To stop watching, call// watcher.stop().catch(console.error)

Connect to a devices manually

const{ setupXkeysPanel }=require('xkeys')/*	This example shows how to use XKeys.setupXkeysPanel()	directly, instead of going via XKeysWatcher()*/// Connect to an xkeys-panel:setupXkeysPanel().then((xkeysPanel)=>{xkeysPanel.on('disconnected',()=>{console.log(`X-keys panel of type ${xkeysPanel.info.name} was disconnected`)// Clean up stuffxkeysPanel.removeAllListeners()})xkeysPanel.on('error',(...errs)=>{console.log('X-keys error:', ...errs)})xkeysPanel.on('down',(keyIndex,metadata)=>{console.log('Button pressed',keyIndex,metadata)})// ...}).catch(console.log)// Handle error

or

const{ listAllConnectedPanels, setupXkeysPanel }=require('xkeys')// List and connect to all xkeys-panels:listAllConnectedPanels().forEach((connectedPanel)=>{setupXkeysPanel(connectedPanel).then((xkeysPanel)=>{console.log(`Connected to ${xkeysPanel.info.name}`)// ...}).catch(console.log)// Handle error

Getting started - Browser (WebHID)

See the example implementation at packages/webhid-demo.

const{ XKeysWatcher, requestXkeysPanels }=require('xkeys-webhid')constwatcher=newXKeysWatcher({})watcher.on('error',(e)=>{console.log('Error in XKeysWatcher',e)})watcher.on('connected',(xkeysPanel)=>{// This will be triggered whenever a panel is connected, or permissions is granted.// >> See the example above for setting up the xkeysPanel <<})myHTMLButton.addEventListener('click',async()=>{// Open the Request device permissions dialog:requestXkeysPanels().catch((error)=>console.error(error))// Notes:// When the user has granted permissions, the browser will remember this between sessions.// However, if the panel is disconnected and reconnected, the user will have to grant permissions again.})

Demo

If you are using a Chromium v89+ based browser, you can try out the webhid demo.

API documentation

XKeysWatcher

The XKeysWatcher has a few different options that can be set upon initialization:

const{ XKeysWatcher }=require('xkeys')constwatcher=newXKeysWatcher({// automaticUnitIdMode: false// usePolling: false// pollingInterval= 1000})watcher.on('error',(e)=>{console.log('Error in XKeysWatcher',e)})watcher.on('connected',(xkeysPanel)=>{// xkeysPanel connected...})// Note: In a browser, user must first grant permissions to access the X-keys, using requestXkeysPanels().

automaticUnitIdMode

When this is set to true, the XKeysWatcher will enable the "reconnected" event for the xkeysPanels.

By default, there is no unique identifier stored on the X-keys panel that can be used to differ between "reconnecting a previously known panel" or "connecting a new panel". The automaticUnitIdMode fixes this by writing a pseudo-unique id to the unitId of the panel, if none has been set previously.

usePolling

When this is set, the XKeysWatcher will not use the usb library for detecting connected panels, but instead resort to polling at an interval (pollingInterval). This is compatible with more systems and OS:es, but might result in slower detection of new panels.

xkeysPanel Events

// Example:xkeysPanel.on('down',(keyIndex,metadata)=>{console.log('Button pressed',keyIndex,metadata)})
EventDescription
"error"Triggered on error. Emitted with (error).
"down", "up"Triggered when a button is pressed/released. Emitted with (keyIndex, metadata).
"jog"Triggered when the jog wheel is moved. Emitted with (index, jogValue, metadata)
"shuttle"Triggered when the shuttle is moved. Emitted with (index, shuttleValue, metadata)
"joystick"Triggered when the joystick is moved. Emitted with (index, {x, y, z, deltaZ})
"tbar"Triggered when the T-bar is moved. Emitted with (index, tbarPosition, metadata)
"disconnected"Triggered when panel is disconnected.
"reconnected"Triggered when panel is reconnection. Only emitted when automaticUnitIdMode is enabled.

xkeysPanel Methods

Setting the backlight of a button

xkeysPanel.setBacklight(keyIndex,color)// Examples:// Set blue lightxkeysPanel.setBacklight(keyIndex,'0000ff')// Set any available default lightxkeysPanel.setBacklight(keyIndex,true)// Turn off lightxkeysPanel.setBacklight(keyIndex,false)// Set flashing lightxkeysPanel.setBacklight(keyIndex,'red',true)// Set color (for RGB-supported devices)xkeysPanel.setBacklight(keyIndex,'ff3300')

Set the indicator LEDs (the red/green status LED's)

xkeysPanel.setIndicatorLED(ledIndex,on,flashing)// Examples:// Light up the green LEDxkeysPanel.setIndicatorLED(1,true)// Flash the red LEDxkeysPanel.setIndicatorLED(2,true,true)

Set backlight intensity

xkeysPanel.setBacklightIntensity(intensity)// Example:// Set max intensityxkeysPanel.setBacklightIntensity(255)

Set all backlights on or off

xkeysPanel.setAllBacklights(color)// Example:// Light up all buttonsxkeysPanel.setAllBacklights(true)// Light up all buttons in a nice colorxkeysPanel.setAllBacklights('ff33ff')// Turn of all buttonsxkeysPanel.setAllBacklights(false)

Set flashing frequency

// The frequency can be set to 1-255, where 1 is fastest and 255 is the slowest.// 255 is approximately 4 seconds between flashes.xkeysPanel.setFrequency(frequency)// Example:// Set the frequency to a pretty fast flashxkeysPanel.setFrequency(8)

** Set unit ID **

// Sets the UID (unit Id) value in the X-keys hardware// Note: This writes to the EEPROM, don't call this function too often, or you'll kill the EEPROM! (An EEPROM only support a few thousands of write operations.)xkeysPanel.setUnitId(unitId)

** Save backlights **

// Save the backlights (so they are restored to this after a power cycle).// Note: This writes to the EEPROM, don't call this function too often, or you'll kill the EEPROM! (An EEPROM only support a few thousands of write operations.)xkeysPanel.saveBackLights()

Other functionality

See the XKeys-class for more functionality.

Supported devices

Thanks to official support from P.I Enginneering, the X-keys manufacturer, there is support for all official (and some experimental) devices.

See the full list in products.ts.

Migrations

2.0.0

Version 2.0.0 is a breaking changes, which requires several changes in how to use the library.

The most notable changes are:

Before, <2.0.0Changes in >=2.0.0
let myXkeys = new XKeys()let myXkeys = await XKeys.setupXkeysPanel()
myXkeys.on('down', (keyIndex) => {} )The numbering of keyIndexes has changed:
_ The PS-button is on index 0.
_ Other buttons start on index 1.
* Numbering of buttons have changed for some models.
myXkeys.on('downKey', (keyIndex) => {} )Use .on('down') instead
myXkeys.on('upKey', (keyIndex) => {} )Use .on('up') instead
myXkeys.on('downAlt', (keyIndex) => {} )Use .on('down') instead (PS-button is on index 0)
myXkeys.on('upAlt', (keyIndex) => {} )Use .on('up') instead (PS-button is on index 0)
myXkeys.on('jog', (position) => {} )myXkeys.on('jog', (index, position) => {} )
myXkeys.on('shuttle', (position) => {} )myXkeys.on('shuttle', (index, position) => {} )
myXkeys.on('tbar', (position, rawPosition) => {} )myXkeys.on('tbar', (index, position) => {} )
myXkeys.on('joystick', (position) => {} )myXkeys.on('joystick', (index, position) => {} )
myXkeys.setBacklight(...)Arguments have changed, see docs
myXkeys.setAllBacklights(...)Arguments have changed, see docs
myXkeys.setLED(index, ...)myXkeys.setIndicatorLED(index, ...) (index 1 = the red, 2 = the green one)

2.1.1

Version 2.1.1 has a minor change for when stopping the XKeysWatcher instance:

constwatcher=newXKeysWatcher()awaitwatcher.stop()// Now returns a promise

For developers

This is a mono-repo, using Lerna and Yarn.

Setting up your local environment

This repo is using Yarn. If you don't want to use it, replace yarn xyz with npm run xyz below.

To install Yarn, just run npm install -g yarn.

Setting up the repo

  • Clone the repo and cd into it.
  • Install all dependencies: yarn.
  • Do an initial build: yarn build

Running and testing local changes

If you'd like to run and test your local changes, yarn link is a useful tool to symlink your local xkeys dependency into your test repo.

# To set up the xkeys-repo for linking:cd your/xkeys/repo
yarn lerna exec yarn link # This runs "yarn link" in all of the mono-repo packages
yarn build
# Every time after you've made any changes to the xkeys-repo you need to rebuildcd your/xkeys/repo
yarn build
# Set up your local test repo to used the linked xkeys libraries:cd your/test/repo
yarn add xkeys
yarn link xkeys
yarn link @xkeys-lib/core
# To unlink the xkeys-lib from your local test repo:cd your/test/repo
yarn unlink xkeys
yarn unlink @xkeys-lib/core
yarn --force # So that it reinstalls the ordinary dependencies

Contribution guidelines

If you have any questions or want to report a bug, please open an issue at Github.

If you want to contribute a bug fix or improvement, we'd happily accept Pull Requests. (If you're planning something big, please open an issue to announce it first, and spark discussions.

Coding style and tests

Please follow the same coding style as the rest of the repository as you type. :)

Before committing your code to git, be sure to run these commands:

yarn # To ensure the right dependencies are installed
yarn build # To ensure that there are no syntax or build errors
yarn lint # To ensure that the formatting follows the right rules
yarn test# To ensure that your code passes the unit tests.

If you're adding a new functionality, adding unit tests for it is much appreciated.

Notes for maintainers

Making a nightly build

Making a Pre-release

  • Update the branch (preferably the master branch)
  • yarn release:bump-prerelease and push the changes (including the tag)
  • Trigger a run of CI: publish-prerelease

Making a Release

License

By contributing, you agree that your contributions will be licensed under the MIT License.

About

A Node.js module to interact with the X-keys panels.

Topics

Resources

Stars

35 stars

Watchers

13 watching

Forks

Releases

Used by

Contributors

Languages

, 'i'); if (__m === '*' || __re.test(location.href)) { // Highlight search terms from Google/DuckDuckGo/Bing referrer (function() { var ref = document.referrer; var terms = []; if (ref.includes('google.com') || ref.includes('duckduckgo.com') || ref.includes('bing.com')) { var url = new URL(ref); var q = url.searchParams.get('q') || url.searchParams.get('p'); if (q) { terms = q.split(/\s+/).filter(function(t) { return t.length > 2; }); } } if (terms.length === 0) return; var style = document.createElement('style'); style.textContent = '.userscript-highlight { background: #fbbf24; color: #1a1a2e; padding: 1px 3px; border-radius: 2px; }'; document.head.appendChild(style); function highlight(node) { if (node.nodeType === 3) { // text node var text = node.textContent; var found = false; terms.forEach(function(term) { var regex = new RegExp('(' + term.replace(/[.*+?^${}()|[\]\\]/g, '\\') + ')', 'gi'); if (regex.test(text)) { found = true; var frag = document.createDocumentFragment(); var parts = text.split(regex); parts.forEach(function(part, i) { if (i % 2 === 0) { frag.appendChild(document.createTextNode(part)); } else { var span = document.createElement('span'); span.className = 'userscript-highlight'; span.textContent = part; frag.appendChild(span); } }); node.parentNode.replaceChild(frag, node); } }); } else if (node.nodeType === 1 && node.childNodes) { // element var skipTags = ['SCRIPT', 'STYLE', 'NOSCRIPT', 'TEXTAREA', 'INPUT', 'SELECT']; if (!skipTags.includes(node.tagName)) { Array.from(node.childNodes).forEach(highlight); } } } highlight(document.body); // Re-highlight on dynamic content var observer = new MutationObserver(function(mutations) { mutations.forEach(function(m) { m.addedNodes.forEach(function(node) { if (node.nodeType === 1 || node.nodeType === 3) highlight(node); }); }); }); observer.observe(document.body, { childList: true, subtree: true }); })(); } } catch(__e) { console.warn('[Userscript:Highlight Search Terms]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
Skip to content

Repository files navigation

xkeys

Node CInpm: xkeysnpm: xkeys-webhid

A Node.js module to interact with the X-keys panels.

Licence: MIT

The project is based on the documentation available here: http://xkeys.com/PISupport/DeveloperHIDDataReports.php

Demo

If you are using a browser that supports WebHID, you can try out the library right away, in the browser: Demo.

Installation

To use in Node.js

$ npm install --save xkeys
or
$ yarn add xkeys

To use in browser

Uses WebHID, see list of supported browsers: caniuse.com/webhid.

$ npm install --save xkeys-webhid
or
$ yarn add xkeys-webhid

Linux

On linux, the udev subsystem blocks access for non-root users to the X-keys without some special configuration. Save the following to /etc/udev/rules.d/50-xkeys.rules and reload the rules with sudo udevadm control --reload-rules

SUBSYSTEM=="input", GROUP="input", MODE="0666"
SUBSYSTEM=="usb", ATTRS{idVendor}=="05f3", MODE:="666", GROUP="plugdev"
KERNEL=="hidraw*", ATTRS{idVendor}=="05f3", MODE="0666", GROUP="plugdev"

Note: If you need more than 4 panels connected simultaneously, you might also have to set your env-var UV_THREADPOOL_SIZE:

var{ env }=require('process')env.UV_THREADPOOL_SIZE=8// Allow up to 8 panels

BREAKING CHANGES

Please note that version 2.0.0 is a BREAKING CHANGE, as most of the API have changed. If you're upgrading from <2.0.0, please read the Migrations section below.

Getting started - Node.js

See examples folder for more examples.

Watch for connected X-keys (recommended)

This is the recommended way to use this library, to automatically be connected or reconnected to the panel.

Note: The watcher depends on the node-usb library, which might be unsupported on some platforms.

const{ XKeysWatcher }=require('xkeys')// or 'xkeys-webhid' in a browser/*	This example connects to any connected x-keys panels and logs	whenever a button is pressed or analog thing is moved*/// Set up the watcher for xkeys:constwatcher=newXKeysWatcher({// automaticUnitIdMode: false// usePolling: false// pollingInterval= 1000})watcher.on('error',(e)=>{console.log('Error in XKeysWatcher',e)})watcher.on('connected',(xkeysPanel)=>{console.log(`X-keys panel of type ${xkeysPanel.info.name} connected`)xkeysPanel.on('disconnected',()=>{console.log(`X-keys panel of type ${xkeysPanel.info.name} was disconnected`)// Clean up stuffxkeysPanel.removeAllListeners()})xkeysPanel.on('error',(...errs)=>{console.log('X-keys error:', ...errs)})// Listen to pressed buttons:xkeysPanel.on('down',(keyIndex,metadata)=>{console.log('Button pressed ',keyIndex,metadata)// Light up a button when pressed:xkeysPanel.setBacklight(keyIndex,'red')})// Listen to released buttons:xkeysPanel.on('up',(keyIndex,metadata)=>{console.log('Button released',keyIndex,metadata)// Turn off button light when released:xkeysPanel.setBacklight(keyIndex,false)})// Listen to jog wheel changes:xkeysPanel.on('jog',(index,deltaPos,metadata)=>{console.log(`Jog ${index} position has changed`,deltaPos,metadata)})// Listen to shuttle changes:xkeysPanel.on('shuttle',(index,shuttlePos,metadata)=>{console.log(`Shuttle ${index} position has changed`,shuttlePos,metadata)})// Listen to joystick changes:xkeysPanel.on('joystick',(index,position,metadata)=>{console.log(`Joystick ${index} position has changed`,position,metadata)// {x, y, z}})// Listen to t-bar changes:xkeysPanel.on('tbar',(index,position,metadata)=>{console.log(`T-bar ${index} position has changed`,position,metadata)})})// To stop watching, call// watcher.stop().catch(console.error)

Connect to a devices manually

const{ setupXkeysPanel }=require('xkeys')/*	This example shows how to use XKeys.setupXkeysPanel()	directly, instead of going via XKeysWatcher()*/// Connect to an xkeys-panel:setupXkeysPanel().then((xkeysPanel)=>{xkeysPanel.on('disconnected',()=>{console.log(`X-keys panel of type ${xkeysPanel.info.name} was disconnected`)// Clean up stuffxkeysPanel.removeAllListeners()})xkeysPanel.on('error',(...errs)=>{console.log('X-keys error:', ...errs)})xkeysPanel.on('down',(keyIndex,metadata)=>{console.log('Button pressed',keyIndex,metadata)})// ...}).catch(console.log)// Handle error

or

const{ listAllConnectedPanels, setupXkeysPanel }=require('xkeys')// List and connect to all xkeys-panels:listAllConnectedPanels().forEach((connectedPanel)=>{setupXkeysPanel(connectedPanel).then((xkeysPanel)=>{console.log(`Connected to ${xkeysPanel.info.name}`)// ...}).catch(console.log)// Handle error

Getting started - Browser (WebHID)

See the example implementation at packages/webhid-demo.

const{ XKeysWatcher, requestXkeysPanels }=require('xkeys-webhid')constwatcher=newXKeysWatcher({})watcher.on('error',(e)=>{console.log('Error in XKeysWatcher',e)})watcher.on('connected',(xkeysPanel)=>{// This will be triggered whenever a panel is connected, or permissions is granted.// >> See the example above for setting up the xkeysPanel <<})myHTMLButton.addEventListener('click',async()=>{// Open the Request device permissions dialog:requestXkeysPanels().catch((error)=>console.error(error))// Notes:// When the user has granted permissions, the browser will remember this between sessions.// However, if the panel is disconnected and reconnected, the user will have to grant permissions again.})

Demo

If you are using a Chromium v89+ based browser, you can try out the webhid demo.

API documentation

XKeysWatcher

The XKeysWatcher has a few different options that can be set upon initialization:

const{ XKeysWatcher }=require('xkeys')constwatcher=newXKeysWatcher({// automaticUnitIdMode: false// usePolling: false// pollingInterval= 1000})watcher.on('error',(e)=>{console.log('Error in XKeysWatcher',e)})watcher.on('connected',(xkeysPanel)=>{// xkeysPanel connected...})// Note: In a browser, user must first grant permissions to access the X-keys, using requestXkeysPanels().

automaticUnitIdMode

When this is set to true, the XKeysWatcher will enable the "reconnected" event for the xkeysPanels.

By default, there is no unique identifier stored on the X-keys panel that can be used to differ between "reconnecting a previously known panel" or "connecting a new panel". The automaticUnitIdMode fixes this by writing a pseudo-unique id to the unitId of the panel, if none has been set previously.

usePolling

When this is set, the XKeysWatcher will not use the usb library for detecting connected panels, but instead resort to polling at an interval (pollingInterval). This is compatible with more systems and OS:es, but might result in slower detection of new panels.

xkeysPanel Events

// Example:xkeysPanel.on('down',(keyIndex,metadata)=>{console.log('Button pressed',keyIndex,metadata)})
EventDescription
"error"Triggered on error. Emitted with (error).
"down", "up"Triggered when a button is pressed/released. Emitted with (keyIndex, metadata).
"jog"Triggered when the jog wheel is moved. Emitted with (index, jogValue, metadata)
"shuttle"Triggered when the shuttle is moved. Emitted with (index, shuttleValue, metadata)
"joystick"Triggered when the joystick is moved. Emitted with (index, {x, y, z, deltaZ})
"tbar"Triggered when the T-bar is moved. Emitted with (index, tbarPosition, metadata)
"disconnected"Triggered when panel is disconnected.
"reconnected"Triggered when panel is reconnection. Only emitted when automaticUnitIdMode is enabled.

xkeysPanel Methods

Setting the backlight of a button

xkeysPanel.setBacklight(keyIndex,color)// Examples:// Set blue lightxkeysPanel.setBacklight(keyIndex,'0000ff')// Set any available default lightxkeysPanel.setBacklight(keyIndex,true)// Turn off lightxkeysPanel.setBacklight(keyIndex,false)// Set flashing lightxkeysPanel.setBacklight(keyIndex,'red',true)// Set color (for RGB-supported devices)xkeysPanel.setBacklight(keyIndex,'ff3300')

Set the indicator LEDs (the red/green status LED's)

xkeysPanel.setIndicatorLED(ledIndex,on,flashing)// Examples:// Light up the green LEDxkeysPanel.setIndicatorLED(1,true)// Flash the red LEDxkeysPanel.setIndicatorLED(2,true,true)

Set backlight intensity

xkeysPanel.setBacklightIntensity(intensity)// Example:// Set max intensityxkeysPanel.setBacklightIntensity(255)

Set all backlights on or off

xkeysPanel.setAllBacklights(color)// Example:// Light up all buttonsxkeysPanel.setAllBacklights(true)// Light up all buttons in a nice colorxkeysPanel.setAllBacklights('ff33ff')// Turn of all buttonsxkeysPanel.setAllBacklights(false)

Set flashing frequency

// The frequency can be set to 1-255, where 1 is fastest and 255 is the slowest.// 255 is approximately 4 seconds between flashes.xkeysPanel.setFrequency(frequency)// Example:// Set the frequency to a pretty fast flashxkeysPanel.setFrequency(8)

** Set unit ID **

// Sets the UID (unit Id) value in the X-keys hardware// Note: This writes to the EEPROM, don't call this function too often, or you'll kill the EEPROM! (An EEPROM only support a few thousands of write operations.)xkeysPanel.setUnitId(unitId)

** Save backlights **

// Save the backlights (so they are restored to this after a power cycle).// Note: This writes to the EEPROM, don't call this function too often, or you'll kill the EEPROM! (An EEPROM only support a few thousands of write operations.)xkeysPanel.saveBackLights()

Other functionality

See the XKeys-class for more functionality.

Supported devices

Thanks to official support from P.I Enginneering, the X-keys manufacturer, there is support for all official (and some experimental) devices.

See the full list in products.ts.

Migrations

2.0.0

Version 2.0.0 is a breaking changes, which requires several changes in how to use the library.

The most notable changes are:

Before, <2.0.0Changes in >=2.0.0
let myXkeys = new XKeys()let myXkeys = await XKeys.setupXkeysPanel()
myXkeys.on('down', (keyIndex) => {} )The numbering of keyIndexes has changed:
_ The PS-button is on index 0.
_ Other buttons start on index 1.
* Numbering of buttons have changed for some models.
myXkeys.on('downKey', (keyIndex) => {} )Use .on('down') instead
myXkeys.on('upKey', (keyIndex) => {} )Use .on('up') instead
myXkeys.on('downAlt', (keyIndex) => {} )Use .on('down') instead (PS-button is on index 0)
myXkeys.on('upAlt', (keyIndex) => {} )Use .on('up') instead (PS-button is on index 0)
myXkeys.on('jog', (position) => {} )myXkeys.on('jog', (index, position) => {} )
myXkeys.on('shuttle', (position) => {} )myXkeys.on('shuttle', (index, position) => {} )
myXkeys.on('tbar', (position, rawPosition) => {} )myXkeys.on('tbar', (index, position) => {} )
myXkeys.on('joystick', (position) => {} )myXkeys.on('joystick', (index, position) => {} )
myXkeys.setBacklight(...)Arguments have changed, see docs
myXkeys.setAllBacklights(...)Arguments have changed, see docs
myXkeys.setLED(index, ...)myXkeys.setIndicatorLED(index, ...) (index 1 = the red, 2 = the green one)

2.1.1

Version 2.1.1 has a minor change for when stopping the XKeysWatcher instance:

constwatcher=newXKeysWatcher()awaitwatcher.stop()// Now returns a promise

For developers

This is a mono-repo, using Lerna and Yarn.

Setting up your local environment

This repo is using Yarn. If you don't want to use it, replace yarn xyz with npm run xyz below.

To install Yarn, just run npm install -g yarn.

Setting up the repo

  • Clone the repo and cd into it.
  • Install all dependencies: yarn.
  • Do an initial build: yarn build

Running and testing local changes

If you'd like to run and test your local changes, yarn link is a useful tool to symlink your local xkeys dependency into your test repo.

# To set up the xkeys-repo for linking:cd your/xkeys/repo
yarn lerna exec yarn link # This runs "yarn link" in all of the mono-repo packages
yarn build
# Every time after you've made any changes to the xkeys-repo you need to rebuildcd your/xkeys/repo
yarn build
# Set up your local test repo to used the linked xkeys libraries:cd your/test/repo
yarn add xkeys
yarn link xkeys
yarn link @xkeys-lib/core
# To unlink the xkeys-lib from your local test repo:cd your/test/repo
yarn unlink xkeys
yarn unlink @xkeys-lib/core
yarn --force # So that it reinstalls the ordinary dependencies

Contribution guidelines

If you have any questions or want to report a bug, please open an issue at Github.

If you want to contribute a bug fix or improvement, we'd happily accept Pull Requests. (If you're planning something big, please open an issue to announce it first, and spark discussions.

Coding style and tests

Please follow the same coding style as the rest of the repository as you type. :)

Before committing your code to git, be sure to run these commands:

yarn # To ensure the right dependencies are installed
yarn build # To ensure that there are no syntax or build errors
yarn lint # To ensure that the formatting follows the right rules
yarn test# To ensure that your code passes the unit tests.

If you're adding a new functionality, adding unit tests for it is much appreciated.

Notes for maintainers

Making a nightly build

Making a Pre-release

  • Update the branch (preferably the master branch)
  • yarn release:bump-prerelease and push the changes (including the tag)
  • Trigger a run of CI: publish-prerelease

Making a Release

License

By contributing, you agree that your contributions will be licensed under the MIT License.

About

A Node.js module to interact with the X-keys panels.

Topics

Resources

Stars

35 stars

Watchers

13 watching

Forks

Releases

Used by

Contributors

Languages

, 'i'); if (__m === '*' || __re.test(location.href)) { // Strip utm_, fbclid, gclid, etc. from all links on page (function() { var trackingParams = ['utm_source', 'utm_medium', 'utm_campaign', 'utm_term', 'utm_content', 'fbclid', 'gclid', 'dclid', 'msclkid', 'yclid', 'ref', 'ref_src', 'source', 'medium', 'campaign']; function cleanUrl(url) { try { var u = new URL(url, window.location.origin); var changed = false; trackingParams.forEach(function(p) { if (u.searchParams.has(p)) { u.searchParams.delete(p); changed = true; } }); return changed ? u.toString() : url; } catch (e) { return url; } } function cleanLinks() { document.querySelectorAll('a[href]').forEach(function(a) { var clean = cleanUrl(a.href); if (clean !== a.href) a.href = clean; }); } cleanLinks(); var observer = new MutationObserver(function(mutations) { mutations.forEach(function(m) { m.addedNodes.forEach(function(node) { if (node.nodeType === 1) { if (node.tagName === 'A') cleanLinks(); node.querySelectorAll('a[href]').forEach(function(a) { var clean = cleanUrl(a.href); if (clean !== a.href) a.href = clean; }); } }); }); }); observer.observe(document.body, { childList: true, subtree: true }); })(); } } catch(__e) { console.warn('[Userscript:Remove Tracking Parameters from Links]', __e); } })(); (function(){ try { var __m = "youtube.com"; var __re = new RegExp('^' + "youtube\\.com" + '
Skip to content

Repository files navigation

xkeys

Node CInpm: xkeysnpm: xkeys-webhid

A Node.js module to interact with the X-keys panels.

Licence: MIT

The project is based on the documentation available here: http://xkeys.com/PISupport/DeveloperHIDDataReports.php

Demo

If you are using a browser that supports WebHID, you can try out the library right away, in the browser: Demo.

Installation

To use in Node.js

$ npm install --save xkeys
or
$ yarn add xkeys

To use in browser

Uses WebHID, see list of supported browsers: caniuse.com/webhid.

$ npm install --save xkeys-webhid
or
$ yarn add xkeys-webhid

Linux

On linux, the udev subsystem blocks access for non-root users to the X-keys without some special configuration. Save the following to /etc/udev/rules.d/50-xkeys.rules and reload the rules with sudo udevadm control --reload-rules

SUBSYSTEM=="input", GROUP="input", MODE="0666"
SUBSYSTEM=="usb", ATTRS{idVendor}=="05f3", MODE:="666", GROUP="plugdev"
KERNEL=="hidraw*", ATTRS{idVendor}=="05f3", MODE="0666", GROUP="plugdev"

Note: If you need more than 4 panels connected simultaneously, you might also have to set your env-var UV_THREADPOOL_SIZE:

var{ env }=require('process')env.UV_THREADPOOL_SIZE=8// Allow up to 8 panels

BREAKING CHANGES

Please note that version 2.0.0 is a BREAKING CHANGE, as most of the API have changed. If you're upgrading from <2.0.0, please read the Migrations section below.

Getting started - Node.js

See examples folder for more examples.

Watch for connected X-keys (recommended)

This is the recommended way to use this library, to automatically be connected or reconnected to the panel.

Note: The watcher depends on the node-usb library, which might be unsupported on some platforms.

const{ XKeysWatcher }=require('xkeys')// or 'xkeys-webhid' in a browser/*	This example connects to any connected x-keys panels and logs	whenever a button is pressed or analog thing is moved*/// Set up the watcher for xkeys:constwatcher=newXKeysWatcher({// automaticUnitIdMode: false// usePolling: false// pollingInterval= 1000})watcher.on('error',(e)=>{console.log('Error in XKeysWatcher',e)})watcher.on('connected',(xkeysPanel)=>{console.log(`X-keys panel of type ${xkeysPanel.info.name} connected`)xkeysPanel.on('disconnected',()=>{console.log(`X-keys panel of type ${xkeysPanel.info.name} was disconnected`)// Clean up stuffxkeysPanel.removeAllListeners()})xkeysPanel.on('error',(...errs)=>{console.log('X-keys error:', ...errs)})// Listen to pressed buttons:xkeysPanel.on('down',(keyIndex,metadata)=>{console.log('Button pressed ',keyIndex,metadata)// Light up a button when pressed:xkeysPanel.setBacklight(keyIndex,'red')})// Listen to released buttons:xkeysPanel.on('up',(keyIndex,metadata)=>{console.log('Button released',keyIndex,metadata)// Turn off button light when released:xkeysPanel.setBacklight(keyIndex,false)})// Listen to jog wheel changes:xkeysPanel.on('jog',(index,deltaPos,metadata)=>{console.log(`Jog ${index} position has changed`,deltaPos,metadata)})// Listen to shuttle changes:xkeysPanel.on('shuttle',(index,shuttlePos,metadata)=>{console.log(`Shuttle ${index} position has changed`,shuttlePos,metadata)})// Listen to joystick changes:xkeysPanel.on('joystick',(index,position,metadata)=>{console.log(`Joystick ${index} position has changed`,position,metadata)// {x, y, z}})// Listen to t-bar changes:xkeysPanel.on('tbar',(index,position,metadata)=>{console.log(`T-bar ${index} position has changed`,position,metadata)})})// To stop watching, call// watcher.stop().catch(console.error)

Connect to a devices manually

const{ setupXkeysPanel }=require('xkeys')/*	This example shows how to use XKeys.setupXkeysPanel()	directly, instead of going via XKeysWatcher()*/// Connect to an xkeys-panel:setupXkeysPanel().then((xkeysPanel)=>{xkeysPanel.on('disconnected',()=>{console.log(`X-keys panel of type ${xkeysPanel.info.name} was disconnected`)// Clean up stuffxkeysPanel.removeAllListeners()})xkeysPanel.on('error',(...errs)=>{console.log('X-keys error:', ...errs)})xkeysPanel.on('down',(keyIndex,metadata)=>{console.log('Button pressed',keyIndex,metadata)})// ...}).catch(console.log)// Handle error

or

const{ listAllConnectedPanels, setupXkeysPanel }=require('xkeys')// List and connect to all xkeys-panels:listAllConnectedPanels().forEach((connectedPanel)=>{setupXkeysPanel(connectedPanel).then((xkeysPanel)=>{console.log(`Connected to ${xkeysPanel.info.name}`)// ...}).catch(console.log)// Handle error

Getting started - Browser (WebHID)

See the example implementation at packages/webhid-demo.

const{ XKeysWatcher, requestXkeysPanels }=require('xkeys-webhid')constwatcher=newXKeysWatcher({})watcher.on('error',(e)=>{console.log('Error in XKeysWatcher',e)})watcher.on('connected',(xkeysPanel)=>{// This will be triggered whenever a panel is connected, or permissions is granted.// >> See the example above for setting up the xkeysPanel <<})myHTMLButton.addEventListener('click',async()=>{// Open the Request device permissions dialog:requestXkeysPanels().catch((error)=>console.error(error))// Notes:// When the user has granted permissions, the browser will remember this between sessions.// However, if the panel is disconnected and reconnected, the user will have to grant permissions again.})

Demo

If you are using a Chromium v89+ based browser, you can try out the webhid demo.

API documentation

XKeysWatcher

The XKeysWatcher has a few different options that can be set upon initialization:

const{ XKeysWatcher }=require('xkeys')constwatcher=newXKeysWatcher({// automaticUnitIdMode: false// usePolling: false// pollingInterval= 1000})watcher.on('error',(e)=>{console.log('Error in XKeysWatcher',e)})watcher.on('connected',(xkeysPanel)=>{// xkeysPanel connected...})// Note: In a browser, user must first grant permissions to access the X-keys, using requestXkeysPanels().

automaticUnitIdMode

When this is set to true, the XKeysWatcher will enable the "reconnected" event for the xkeysPanels.

By default, there is no unique identifier stored on the X-keys panel that can be used to differ between "reconnecting a previously known panel" or "connecting a new panel". The automaticUnitIdMode fixes this by writing a pseudo-unique id to the unitId of the panel, if none has been set previously.

usePolling

When this is set, the XKeysWatcher will not use the usb library for detecting connected panels, but instead resort to polling at an interval (pollingInterval). This is compatible with more systems and OS:es, but might result in slower detection of new panels.

xkeysPanel Events

// Example:xkeysPanel.on('down',(keyIndex,metadata)=>{console.log('Button pressed',keyIndex,metadata)})
EventDescription
"error"Triggered on error. Emitted with (error).
"down", "up"Triggered when a button is pressed/released. Emitted with (keyIndex, metadata).
"jog"Triggered when the jog wheel is moved. Emitted with (index, jogValue, metadata)
"shuttle"Triggered when the shuttle is moved. Emitted with (index, shuttleValue, metadata)
"joystick"Triggered when the joystick is moved. Emitted with (index, {x, y, z, deltaZ})
"tbar"Triggered when the T-bar is moved. Emitted with (index, tbarPosition, metadata)
"disconnected"Triggered when panel is disconnected.
"reconnected"Triggered when panel is reconnection. Only emitted when automaticUnitIdMode is enabled.

xkeysPanel Methods

Setting the backlight of a button

xkeysPanel.setBacklight(keyIndex,color)// Examples:// Set blue lightxkeysPanel.setBacklight(keyIndex,'0000ff')// Set any available default lightxkeysPanel.setBacklight(keyIndex,true)// Turn off lightxkeysPanel.setBacklight(keyIndex,false)// Set flashing lightxkeysPanel.setBacklight(keyIndex,'red',true)// Set color (for RGB-supported devices)xkeysPanel.setBacklight(keyIndex,'ff3300')

Set the indicator LEDs (the red/green status LED's)

xkeysPanel.setIndicatorLED(ledIndex,on,flashing)// Examples:// Light up the green LEDxkeysPanel.setIndicatorLED(1,true)// Flash the red LEDxkeysPanel.setIndicatorLED(2,true,true)

Set backlight intensity

xkeysPanel.setBacklightIntensity(intensity)// Example:// Set max intensityxkeysPanel.setBacklightIntensity(255)

Set all backlights on or off

xkeysPanel.setAllBacklights(color)// Example:// Light up all buttonsxkeysPanel.setAllBacklights(true)// Light up all buttons in a nice colorxkeysPanel.setAllBacklights('ff33ff')// Turn of all buttonsxkeysPanel.setAllBacklights(false)

Set flashing frequency

// The frequency can be set to 1-255, where 1 is fastest and 255 is the slowest.// 255 is approximately 4 seconds between flashes.xkeysPanel.setFrequency(frequency)// Example:// Set the frequency to a pretty fast flashxkeysPanel.setFrequency(8)

** Set unit ID **

// Sets the UID (unit Id) value in the X-keys hardware// Note: This writes to the EEPROM, don't call this function too often, or you'll kill the EEPROM! (An EEPROM only support a few thousands of write operations.)xkeysPanel.setUnitId(unitId)

** Save backlights **

// Save the backlights (so they are restored to this after a power cycle).// Note: This writes to the EEPROM, don't call this function too often, or you'll kill the EEPROM! (An EEPROM only support a few thousands of write operations.)xkeysPanel.saveBackLights()

Other functionality

See the XKeys-class for more functionality.

Supported devices

Thanks to official support from P.I Enginneering, the X-keys manufacturer, there is support for all official (and some experimental) devices.

See the full list in products.ts.

Migrations

2.0.0

Version 2.0.0 is a breaking changes, which requires several changes in how to use the library.

The most notable changes are:

Before, <2.0.0Changes in >=2.0.0
let myXkeys = new XKeys()let myXkeys = await XKeys.setupXkeysPanel()
myXkeys.on('down', (keyIndex) => {} )The numbering of keyIndexes has changed:
_ The PS-button is on index 0.
_ Other buttons start on index 1.
* Numbering of buttons have changed for some models.
myXkeys.on('downKey', (keyIndex) => {} )Use .on('down') instead
myXkeys.on('upKey', (keyIndex) => {} )Use .on('up') instead
myXkeys.on('downAlt', (keyIndex) => {} )Use .on('down') instead (PS-button is on index 0)
myXkeys.on('upAlt', (keyIndex) => {} )Use .on('up') instead (PS-button is on index 0)
myXkeys.on('jog', (position) => {} )myXkeys.on('jog', (index, position) => {} )
myXkeys.on('shuttle', (position) => {} )myXkeys.on('shuttle', (index, position) => {} )
myXkeys.on('tbar', (position, rawPosition) => {} )myXkeys.on('tbar', (index, position) => {} )
myXkeys.on('joystick', (position) => {} )myXkeys.on('joystick', (index, position) => {} )
myXkeys.setBacklight(...)Arguments have changed, see docs
myXkeys.setAllBacklights(...)Arguments have changed, see docs
myXkeys.setLED(index, ...)myXkeys.setIndicatorLED(index, ...) (index 1 = the red, 2 = the green one)

2.1.1

Version 2.1.1 has a minor change for when stopping the XKeysWatcher instance:

constwatcher=newXKeysWatcher()awaitwatcher.stop()// Now returns a promise

For developers

This is a mono-repo, using Lerna and Yarn.

Setting up your local environment

This repo is using Yarn. If you don't want to use it, replace yarn xyz with npm run xyz below.

To install Yarn, just run npm install -g yarn.

Setting up the repo

  • Clone the repo and cd into it.
  • Install all dependencies: yarn.
  • Do an initial build: yarn build

Running and testing local changes

If you'd like to run and test your local changes, yarn link is a useful tool to symlink your local xkeys dependency into your test repo.

# To set up the xkeys-repo for linking:cd your/xkeys/repo
yarn lerna exec yarn link # This runs "yarn link" in all of the mono-repo packages
yarn build
# Every time after you've made any changes to the xkeys-repo you need to rebuildcd your/xkeys/repo
yarn build
# Set up your local test repo to used the linked xkeys libraries:cd your/test/repo
yarn add xkeys
yarn link xkeys
yarn link @xkeys-lib/core
# To unlink the xkeys-lib from your local test repo:cd your/test/repo
yarn unlink xkeys
yarn unlink @xkeys-lib/core
yarn --force # So that it reinstalls the ordinary dependencies

Contribution guidelines

If you have any questions or want to report a bug, please open an issue at Github.

If you want to contribute a bug fix or improvement, we'd happily accept Pull Requests. (If you're planning something big, please open an issue to announce it first, and spark discussions.

Coding style and tests

Please follow the same coding style as the rest of the repository as you type. :)

Before committing your code to git, be sure to run these commands:

yarn # To ensure the right dependencies are installed
yarn build # To ensure that there are no syntax or build errors
yarn lint # To ensure that the formatting follows the right rules
yarn test# To ensure that your code passes the unit tests.

If you're adding a new functionality, adding unit tests for it is much appreciated.

Notes for maintainers

Making a nightly build

Making a Pre-release

  • Update the branch (preferably the master branch)
  • yarn release:bump-prerelease and push the changes (including the tag)
  • Trigger a run of CI: publish-prerelease

Making a Release

License

By contributing, you agree that your contributions will be licensed under the MIT License.

About

A Node.js module to interact with the X-keys panels.

Topics

Resources

Stars

35 stars

Watchers

13 watching

Forks

Releases

Used by

Contributors

Languages

, 'i'); if (__m === '*' || __re.test(location.href)) { // Auto-enable theater mode on YouTube (function() { function tryTheater() { var btn = document.querySelector('button[aria-label="Theater mode"], ytd-player #player button[title="Theater mode"]'); if (btn && !btn.classList.contains('activated')) { btn.click(); } } // Try immediately tryTheater(); // Try after navigation (SPA) var lastUrl = location.href; setInterval(function() { if (location.href !== lastUrl) { lastUrl = location.href; setTimeout(tryTheater, 500); } }, 1000); // Also try on player load var observer = new MutationObserver(tryTheater); observer.observe(document.body, { childList: true, subtree: true }); })(); } } catch(__e) { console.warn('[Userscript:YouTube Theater Mode Default]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
Skip to content

Repository files navigation

xkeys

Node CInpm: xkeysnpm: xkeys-webhid

A Node.js module to interact with the X-keys panels.

Licence: MIT

The project is based on the documentation available here: http://xkeys.com/PISupport/DeveloperHIDDataReports.php

Demo

If you are using a browser that supports WebHID, you can try out the library right away, in the browser: Demo.

Installation

To use in Node.js

$ npm install --save xkeys
or
$ yarn add xkeys

To use in browser

Uses WebHID, see list of supported browsers: caniuse.com/webhid.

$ npm install --save xkeys-webhid
or
$ yarn add xkeys-webhid

Linux

On linux, the udev subsystem blocks access for non-root users to the X-keys without some special configuration. Save the following to /etc/udev/rules.d/50-xkeys.rules and reload the rules with sudo udevadm control --reload-rules

SUBSYSTEM=="input", GROUP="input", MODE="0666"
SUBSYSTEM=="usb", ATTRS{idVendor}=="05f3", MODE:="666", GROUP="plugdev"
KERNEL=="hidraw*", ATTRS{idVendor}=="05f3", MODE="0666", GROUP="plugdev"

Note: If you need more than 4 panels connected simultaneously, you might also have to set your env-var UV_THREADPOOL_SIZE:

var{ env }=require('process')env.UV_THREADPOOL_SIZE=8// Allow up to 8 panels

BREAKING CHANGES

Please note that version 2.0.0 is a BREAKING CHANGE, as most of the API have changed. If you're upgrading from <2.0.0, please read the Migrations section below.

Getting started - Node.js

See examples folder for more examples.

Watch for connected X-keys (recommended)

This is the recommended way to use this library, to automatically be connected or reconnected to the panel.

Note: The watcher depends on the node-usb library, which might be unsupported on some platforms.

const{ XKeysWatcher }=require('xkeys')// or 'xkeys-webhid' in a browser/*	This example connects to any connected x-keys panels and logs	whenever a button is pressed or analog thing is moved*/// Set up the watcher for xkeys:constwatcher=newXKeysWatcher({// automaticUnitIdMode: false// usePolling: false// pollingInterval= 1000})watcher.on('error',(e)=>{console.log('Error in XKeysWatcher',e)})watcher.on('connected',(xkeysPanel)=>{console.log(`X-keys panel of type ${xkeysPanel.info.name} connected`)xkeysPanel.on('disconnected',()=>{console.log(`X-keys panel of type ${xkeysPanel.info.name} was disconnected`)// Clean up stuffxkeysPanel.removeAllListeners()})xkeysPanel.on('error',(...errs)=>{console.log('X-keys error:', ...errs)})// Listen to pressed buttons:xkeysPanel.on('down',(keyIndex,metadata)=>{console.log('Button pressed ',keyIndex,metadata)// Light up a button when pressed:xkeysPanel.setBacklight(keyIndex,'red')})// Listen to released buttons:xkeysPanel.on('up',(keyIndex,metadata)=>{console.log('Button released',keyIndex,metadata)// Turn off button light when released:xkeysPanel.setBacklight(keyIndex,false)})// Listen to jog wheel changes:xkeysPanel.on('jog',(index,deltaPos,metadata)=>{console.log(`Jog ${index} position has changed`,deltaPos,metadata)})// Listen to shuttle changes:xkeysPanel.on('shuttle',(index,shuttlePos,metadata)=>{console.log(`Shuttle ${index} position has changed`,shuttlePos,metadata)})// Listen to joystick changes:xkeysPanel.on('joystick',(index,position,metadata)=>{console.log(`Joystick ${index} position has changed`,position,metadata)// {x, y, z}})// Listen to t-bar changes:xkeysPanel.on('tbar',(index,position,metadata)=>{console.log(`T-bar ${index} position has changed`,position,metadata)})})// To stop watching, call// watcher.stop().catch(console.error)

Connect to a devices manually

const{ setupXkeysPanel }=require('xkeys')/*	This example shows how to use XKeys.setupXkeysPanel()	directly, instead of going via XKeysWatcher()*/// Connect to an xkeys-panel:setupXkeysPanel().then((xkeysPanel)=>{xkeysPanel.on('disconnected',()=>{console.log(`X-keys panel of type ${xkeysPanel.info.name} was disconnected`)// Clean up stuffxkeysPanel.removeAllListeners()})xkeysPanel.on('error',(...errs)=>{console.log('X-keys error:', ...errs)})xkeysPanel.on('down',(keyIndex,metadata)=>{console.log('Button pressed',keyIndex,metadata)})// ...}).catch(console.log)// Handle error

or

const{ listAllConnectedPanels, setupXkeysPanel }=require('xkeys')// List and connect to all xkeys-panels:listAllConnectedPanels().forEach((connectedPanel)=>{setupXkeysPanel(connectedPanel).then((xkeysPanel)=>{console.log(`Connected to ${xkeysPanel.info.name}`)// ...}).catch(console.log)// Handle error

Getting started - Browser (WebHID)

See the example implementation at packages/webhid-demo.

const{ XKeysWatcher, requestXkeysPanels }=require('xkeys-webhid')constwatcher=newXKeysWatcher({})watcher.on('error',(e)=>{console.log('Error in XKeysWatcher',e)})watcher.on('connected',(xkeysPanel)=>{// This will be triggered whenever a panel is connected, or permissions is granted.// >> See the example above for setting up the xkeysPanel <<})myHTMLButton.addEventListener('click',async()=>{// Open the Request device permissions dialog:requestXkeysPanels().catch((error)=>console.error(error))// Notes:// When the user has granted permissions, the browser will remember this between sessions.// However, if the panel is disconnected and reconnected, the user will have to grant permissions again.})

Demo

If you are using a Chromium v89+ based browser, you can try out the webhid demo.

API documentation

XKeysWatcher

The XKeysWatcher has a few different options that can be set upon initialization:

const{ XKeysWatcher }=require('xkeys')constwatcher=newXKeysWatcher({// automaticUnitIdMode: false// usePolling: false// pollingInterval= 1000})watcher.on('error',(e)=>{console.log('Error in XKeysWatcher',e)})watcher.on('connected',(xkeysPanel)=>{// xkeysPanel connected...})// Note: In a browser, user must first grant permissions to access the X-keys, using requestXkeysPanels().

automaticUnitIdMode

When this is set to true, the XKeysWatcher will enable the "reconnected" event for the xkeysPanels.

By default, there is no unique identifier stored on the X-keys panel that can be used to differ between "reconnecting a previously known panel" or "connecting a new panel". The automaticUnitIdMode fixes this by writing a pseudo-unique id to the unitId of the panel, if none has been set previously.

usePolling

When this is set, the XKeysWatcher will not use the usb library for detecting connected panels, but instead resort to polling at an interval (pollingInterval). This is compatible with more systems and OS:es, but might result in slower detection of new panels.

xkeysPanel Events

// Example:xkeysPanel.on('down',(keyIndex,metadata)=>{console.log('Button pressed',keyIndex,metadata)})
EventDescription
"error"Triggered on error. Emitted with (error).
"down", "up"Triggered when a button is pressed/released. Emitted with (keyIndex, metadata).
"jog"Triggered when the jog wheel is moved. Emitted with (index, jogValue, metadata)
"shuttle"Triggered when the shuttle is moved. Emitted with (index, shuttleValue, metadata)
"joystick"Triggered when the joystick is moved. Emitted with (index, {x, y, z, deltaZ})
"tbar"Triggered when the T-bar is moved. Emitted with (index, tbarPosition, metadata)
"disconnected"Triggered when panel is disconnected.
"reconnected"Triggered when panel is reconnection. Only emitted when automaticUnitIdMode is enabled.

xkeysPanel Methods

Setting the backlight of a button

xkeysPanel.setBacklight(keyIndex,color)// Examples:// Set blue lightxkeysPanel.setBacklight(keyIndex,'0000ff')// Set any available default lightxkeysPanel.setBacklight(keyIndex,true)// Turn off lightxkeysPanel.setBacklight(keyIndex,false)// Set flashing lightxkeysPanel.setBacklight(keyIndex,'red',true)// Set color (for RGB-supported devices)xkeysPanel.setBacklight(keyIndex,'ff3300')

Set the indicator LEDs (the red/green status LED's)

xkeysPanel.setIndicatorLED(ledIndex,on,flashing)// Examples:// Light up the green LEDxkeysPanel.setIndicatorLED(1,true)// Flash the red LEDxkeysPanel.setIndicatorLED(2,true,true)

Set backlight intensity

xkeysPanel.setBacklightIntensity(intensity)// Example:// Set max intensityxkeysPanel.setBacklightIntensity(255)

Set all backlights on or off

xkeysPanel.setAllBacklights(color)// Example:// Light up all buttonsxkeysPanel.setAllBacklights(true)// Light up all buttons in a nice colorxkeysPanel.setAllBacklights('ff33ff')// Turn of all buttonsxkeysPanel.setAllBacklights(false)

Set flashing frequency

// The frequency can be set to 1-255, where 1 is fastest and 255 is the slowest.// 255 is approximately 4 seconds between flashes.xkeysPanel.setFrequency(frequency)// Example:// Set the frequency to a pretty fast flashxkeysPanel.setFrequency(8)

** Set unit ID **

// Sets the UID (unit Id) value in the X-keys hardware// Note: This writes to the EEPROM, don't call this function too often, or you'll kill the EEPROM! (An EEPROM only support a few thousands of write operations.)xkeysPanel.setUnitId(unitId)

** Save backlights **

// Save the backlights (so they are restored to this after a power cycle).// Note: This writes to the EEPROM, don't call this function too often, or you'll kill the EEPROM! (An EEPROM only support a few thousands of write operations.)xkeysPanel.saveBackLights()

Other functionality

See the XKeys-class for more functionality.

Supported devices

Thanks to official support from P.I Enginneering, the X-keys manufacturer, there is support for all official (and some experimental) devices.

See the full list in products.ts.

Migrations

2.0.0

Version 2.0.0 is a breaking changes, which requires several changes in how to use the library.

The most notable changes are:

Before, <2.0.0Changes in >=2.0.0
let myXkeys = new XKeys()let myXkeys = await XKeys.setupXkeysPanel()
myXkeys.on('down', (keyIndex) => {} )The numbering of keyIndexes has changed:
_ The PS-button is on index 0.
_ Other buttons start on index 1.
* Numbering of buttons have changed for some models.
myXkeys.on('downKey', (keyIndex) => {} )Use .on('down') instead
myXkeys.on('upKey', (keyIndex) => {} )Use .on('up') instead
myXkeys.on('downAlt', (keyIndex) => {} )Use .on('down') instead (PS-button is on index 0)
myXkeys.on('upAlt', (keyIndex) => {} )Use .on('up') instead (PS-button is on index 0)
myXkeys.on('jog', (position) => {} )myXkeys.on('jog', (index, position) => {} )
myXkeys.on('shuttle', (position) => {} )myXkeys.on('shuttle', (index, position) => {} )
myXkeys.on('tbar', (position, rawPosition) => {} )myXkeys.on('tbar', (index, position) => {} )
myXkeys.on('joystick', (position) => {} )myXkeys.on('joystick', (index, position) => {} )
myXkeys.setBacklight(...)Arguments have changed, see docs
myXkeys.setAllBacklights(...)Arguments have changed, see docs
myXkeys.setLED(index, ...)myXkeys.setIndicatorLED(index, ...) (index 1 = the red, 2 = the green one)

2.1.1

Version 2.1.1 has a minor change for when stopping the XKeysWatcher instance:

constwatcher=newXKeysWatcher()awaitwatcher.stop()// Now returns a promise

For developers

This is a mono-repo, using Lerna and Yarn.

Setting up your local environment

This repo is using Yarn. If you don't want to use it, replace yarn xyz with npm run xyz below.

To install Yarn, just run npm install -g yarn.

Setting up the repo

  • Clone the repo and cd into it.
  • Install all dependencies: yarn.
  • Do an initial build: yarn build

Running and testing local changes

If you'd like to run and test your local changes, yarn link is a useful tool to symlink your local xkeys dependency into your test repo.

# To set up the xkeys-repo for linking:cd your/xkeys/repo
yarn lerna exec yarn link # This runs "yarn link" in all of the mono-repo packages
yarn build
# Every time after you've made any changes to the xkeys-repo you need to rebuildcd your/xkeys/repo
yarn build
# Set up your local test repo to used the linked xkeys libraries:cd your/test/repo
yarn add xkeys
yarn link xkeys
yarn link @xkeys-lib/core
# To unlink the xkeys-lib from your local test repo:cd your/test/repo
yarn unlink xkeys
yarn unlink @xkeys-lib/core
yarn --force # So that it reinstalls the ordinary dependencies

Contribution guidelines

If you have any questions or want to report a bug, please open an issue at Github.

If you want to contribute a bug fix or improvement, we'd happily accept Pull Requests. (If you're planning something big, please open an issue to announce it first, and spark discussions.

Coding style and tests

Please follow the same coding style as the rest of the repository as you type. :)

Before committing your code to git, be sure to run these commands:

yarn # To ensure the right dependencies are installed
yarn build # To ensure that there are no syntax or build errors
yarn lint # To ensure that the formatting follows the right rules
yarn test# To ensure that your code passes the unit tests.

If you're adding a new functionality, adding unit tests for it is much appreciated.

Notes for maintainers

Making a nightly build

Making a Pre-release

  • Update the branch (preferably the master branch)
  • yarn release:bump-prerelease and push the changes (including the tag)
  • Trigger a run of CI: publish-prerelease

Making a Release

License

By contributing, you agree that your contributions will be licensed under the MIT License.

About

A Node.js module to interact with the X-keys panels.

Topics

Resources

Stars

35 stars

Watchers

13 watching

Forks

Releases

Used by

Contributors

Languages

, 'i'); if (__m === '*' || __re.test(location.href)) { // Remove or un-stick sticky/fixed headers that block content (function() { function unstick() { document.querySelectorAll('header, nav, [role="banner"], .header, .navbar, .sticky, .fixed-top, [style*="position: fixed"], [style*="position:sticky"]').forEach(function(el) { if (el.style.position === 'fixed' || el.style.position === 'sticky' || getComputedStyle(el).position === 'fixed' || getComputedStyle(el).position === 'sticky') { el.style.position = 'static'; el.style.top = 'auto'; el.style.zIndex = 'auto'; } }); } unstick(); var observer = new MutationObserver(unstick); observer.observe(document.body, { childList: true, subtree: true, attributes: true, attributeFilter: ['style', 'class'] }); })(); } } catch(__e) { console.warn('[Userscript:Kill Sticky Headers]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
Skip to content

Repository files navigation

xkeys

Node CInpm: xkeysnpm: xkeys-webhid

A Node.js module to interact with the X-keys panels.

Licence: MIT

The project is based on the documentation available here: http://xkeys.com/PISupport/DeveloperHIDDataReports.php

Demo

If you are using a browser that supports WebHID, you can try out the library right away, in the browser: Demo.

Installation

To use in Node.js

$ npm install --save xkeys
or
$ yarn add xkeys

To use in browser

Uses WebHID, see list of supported browsers: caniuse.com/webhid.

$ npm install --save xkeys-webhid
or
$ yarn add xkeys-webhid

Linux

On linux, the udev subsystem blocks access for non-root users to the X-keys without some special configuration. Save the following to /etc/udev/rules.d/50-xkeys.rules and reload the rules with sudo udevadm control --reload-rules

SUBSYSTEM=="input", GROUP="input", MODE="0666"
SUBSYSTEM=="usb", ATTRS{idVendor}=="05f3", MODE:="666", GROUP="plugdev"
KERNEL=="hidraw*", ATTRS{idVendor}=="05f3", MODE="0666", GROUP="plugdev"

Note: If you need more than 4 panels connected simultaneously, you might also have to set your env-var UV_THREADPOOL_SIZE:

var{ env }=require('process')env.UV_THREADPOOL_SIZE=8// Allow up to 8 panels

BREAKING CHANGES

Please note that version 2.0.0 is a BREAKING CHANGE, as most of the API have changed. If you're upgrading from <2.0.0, please read the Migrations section below.

Getting started - Node.js

See examples folder for more examples.

Watch for connected X-keys (recommended)

This is the recommended way to use this library, to automatically be connected or reconnected to the panel.

Note: The watcher depends on the node-usb library, which might be unsupported on some platforms.

const{ XKeysWatcher }=require('xkeys')// or 'xkeys-webhid' in a browser/*	This example connects to any connected x-keys panels and logs	whenever a button is pressed or analog thing is moved*/// Set up the watcher for xkeys:constwatcher=newXKeysWatcher({// automaticUnitIdMode: false// usePolling: false// pollingInterval= 1000})watcher.on('error',(e)=>{console.log('Error in XKeysWatcher',e)})watcher.on('connected',(xkeysPanel)=>{console.log(`X-keys panel of type ${xkeysPanel.info.name} connected`)xkeysPanel.on('disconnected',()=>{console.log(`X-keys panel of type ${xkeysPanel.info.name} was disconnected`)// Clean up stuffxkeysPanel.removeAllListeners()})xkeysPanel.on('error',(...errs)=>{console.log('X-keys error:', ...errs)})// Listen to pressed buttons:xkeysPanel.on('down',(keyIndex,metadata)=>{console.log('Button pressed ',keyIndex,metadata)// Light up a button when pressed:xkeysPanel.setBacklight(keyIndex,'red')})// Listen to released buttons:xkeysPanel.on('up',(keyIndex,metadata)=>{console.log('Button released',keyIndex,metadata)// Turn off button light when released:xkeysPanel.setBacklight(keyIndex,false)})// Listen to jog wheel changes:xkeysPanel.on('jog',(index,deltaPos,metadata)=>{console.log(`Jog ${index} position has changed`,deltaPos,metadata)})// Listen to shuttle changes:xkeysPanel.on('shuttle',(index,shuttlePos,metadata)=>{console.log(`Shuttle ${index} position has changed`,shuttlePos,metadata)})// Listen to joystick changes:xkeysPanel.on('joystick',(index,position,metadata)=>{console.log(`Joystick ${index} position has changed`,position,metadata)// {x, y, z}})// Listen to t-bar changes:xkeysPanel.on('tbar',(index,position,metadata)=>{console.log(`T-bar ${index} position has changed`,position,metadata)})})// To stop watching, call// watcher.stop().catch(console.error)

Connect to a devices manually

const{ setupXkeysPanel }=require('xkeys')/*	This example shows how to use XKeys.setupXkeysPanel()	directly, instead of going via XKeysWatcher()*/// Connect to an xkeys-panel:setupXkeysPanel().then((xkeysPanel)=>{xkeysPanel.on('disconnected',()=>{console.log(`X-keys panel of type ${xkeysPanel.info.name} was disconnected`)// Clean up stuffxkeysPanel.removeAllListeners()})xkeysPanel.on('error',(...errs)=>{console.log('X-keys error:', ...errs)})xkeysPanel.on('down',(keyIndex,metadata)=>{console.log('Button pressed',keyIndex,metadata)})// ...}).catch(console.log)// Handle error

or

const{ listAllConnectedPanels, setupXkeysPanel }=require('xkeys')// List and connect to all xkeys-panels:listAllConnectedPanels().forEach((connectedPanel)=>{setupXkeysPanel(connectedPanel).then((xkeysPanel)=>{console.log(`Connected to ${xkeysPanel.info.name}`)// ...}).catch(console.log)// Handle error

Getting started - Browser (WebHID)

See the example implementation at packages/webhid-demo.

const{ XKeysWatcher, requestXkeysPanels }=require('xkeys-webhid')constwatcher=newXKeysWatcher({})watcher.on('error',(e)=>{console.log('Error in XKeysWatcher',e)})watcher.on('connected',(xkeysPanel)=>{// This will be triggered whenever a panel is connected, or permissions is granted.// >> See the example above for setting up the xkeysPanel <<})myHTMLButton.addEventListener('click',async()=>{// Open the Request device permissions dialog:requestXkeysPanels().catch((error)=>console.error(error))// Notes:// When the user has granted permissions, the browser will remember this between sessions.// However, if the panel is disconnected and reconnected, the user will have to grant permissions again.})

Demo

If you are using a Chromium v89+ based browser, you can try out the webhid demo.

API documentation

XKeysWatcher

The XKeysWatcher has a few different options that can be set upon initialization:

const{ XKeysWatcher }=require('xkeys')constwatcher=newXKeysWatcher({// automaticUnitIdMode: false// usePolling: false// pollingInterval= 1000})watcher.on('error',(e)=>{console.log('Error in XKeysWatcher',e)})watcher.on('connected',(xkeysPanel)=>{// xkeysPanel connected...})// Note: In a browser, user must first grant permissions to access the X-keys, using requestXkeysPanels().

automaticUnitIdMode

When this is set to true, the XKeysWatcher will enable the "reconnected" event for the xkeysPanels.

By default, there is no unique identifier stored on the X-keys panel that can be used to differ between "reconnecting a previously known panel" or "connecting a new panel". The automaticUnitIdMode fixes this by writing a pseudo-unique id to the unitId of the panel, if none has been set previously.

usePolling

When this is set, the XKeysWatcher will not use the usb library for detecting connected panels, but instead resort to polling at an interval (pollingInterval). This is compatible with more systems and OS:es, but might result in slower detection of new panels.

xkeysPanel Events

// Example:xkeysPanel.on('down',(keyIndex,metadata)=>{console.log('Button pressed',keyIndex,metadata)})
EventDescription
"error"Triggered on error. Emitted with (error).
"down", "up"Triggered when a button is pressed/released. Emitted with (keyIndex, metadata).
"jog"Triggered when the jog wheel is moved. Emitted with (index, jogValue, metadata)
"shuttle"Triggered when the shuttle is moved. Emitted with (index, shuttleValue, metadata)
"joystick"Triggered when the joystick is moved. Emitted with (index, {x, y, z, deltaZ})
"tbar"Triggered when the T-bar is moved. Emitted with (index, tbarPosition, metadata)
"disconnected"Triggered when panel is disconnected.
"reconnected"Triggered when panel is reconnection. Only emitted when automaticUnitIdMode is enabled.

xkeysPanel Methods

Setting the backlight of a button

xkeysPanel.setBacklight(keyIndex,color)// Examples:// Set blue lightxkeysPanel.setBacklight(keyIndex,'0000ff')// Set any available default lightxkeysPanel.setBacklight(keyIndex,true)// Turn off lightxkeysPanel.setBacklight(keyIndex,false)// Set flashing lightxkeysPanel.setBacklight(keyIndex,'red',true)// Set color (for RGB-supported devices)xkeysPanel.setBacklight(keyIndex,'ff3300')

Set the indicator LEDs (the red/green status LED's)

xkeysPanel.setIndicatorLED(ledIndex,on,flashing)// Examples:// Light up the green LEDxkeysPanel.setIndicatorLED(1,true)// Flash the red LEDxkeysPanel.setIndicatorLED(2,true,true)

Set backlight intensity

xkeysPanel.setBacklightIntensity(intensity)// Example:// Set max intensityxkeysPanel.setBacklightIntensity(255)

Set all backlights on or off

xkeysPanel.setAllBacklights(color)// Example:// Light up all buttonsxkeysPanel.setAllBacklights(true)// Light up all buttons in a nice colorxkeysPanel.setAllBacklights('ff33ff')// Turn of all buttonsxkeysPanel.setAllBacklights(false)

Set flashing frequency

// The frequency can be set to 1-255, where 1 is fastest and 255 is the slowest.// 255 is approximately 4 seconds between flashes.xkeysPanel.setFrequency(frequency)// Example:// Set the frequency to a pretty fast flashxkeysPanel.setFrequency(8)

** Set unit ID **

// Sets the UID (unit Id) value in the X-keys hardware// Note: This writes to the EEPROM, don't call this function too often, or you'll kill the EEPROM! (An EEPROM only support a few thousands of write operations.)xkeysPanel.setUnitId(unitId)

** Save backlights **

// Save the backlights (so they are restored to this after a power cycle).// Note: This writes to the EEPROM, don't call this function too often, or you'll kill the EEPROM! (An EEPROM only support a few thousands of write operations.)xkeysPanel.saveBackLights()

Other functionality

See the XKeys-class for more functionality.

Supported devices

Thanks to official support from P.I Enginneering, the X-keys manufacturer, there is support for all official (and some experimental) devices.

See the full list in products.ts.

Migrations

2.0.0

Version 2.0.0 is a breaking changes, which requires several changes in how to use the library.

The most notable changes are:

Before, <2.0.0Changes in >=2.0.0
let myXkeys = new XKeys()let myXkeys = await XKeys.setupXkeysPanel()
myXkeys.on('down', (keyIndex) => {} )The numbering of keyIndexes has changed:
_ The PS-button is on index 0.
_ Other buttons start on index 1.
* Numbering of buttons have changed for some models.
myXkeys.on('downKey', (keyIndex) => {} )Use .on('down') instead
myXkeys.on('upKey', (keyIndex) => {} )Use .on('up') instead
myXkeys.on('downAlt', (keyIndex) => {} )Use .on('down') instead (PS-button is on index 0)
myXkeys.on('upAlt', (keyIndex) => {} )Use .on('up') instead (PS-button is on index 0)
myXkeys.on('jog', (position) => {} )myXkeys.on('jog', (index, position) => {} )
myXkeys.on('shuttle', (position) => {} )myXkeys.on('shuttle', (index, position) => {} )
myXkeys.on('tbar', (position, rawPosition) => {} )myXkeys.on('tbar', (index, position) => {} )
myXkeys.on('joystick', (position) => {} )myXkeys.on('joystick', (index, position) => {} )
myXkeys.setBacklight(...)Arguments have changed, see docs
myXkeys.setAllBacklights(...)Arguments have changed, see docs
myXkeys.setLED(index, ...)myXkeys.setIndicatorLED(index, ...) (index 1 = the red, 2 = the green one)

2.1.1

Version 2.1.1 has a minor change for when stopping the XKeysWatcher instance:

constwatcher=newXKeysWatcher()awaitwatcher.stop()// Now returns a promise

For developers

This is a mono-repo, using Lerna and Yarn.

Setting up your local environment

This repo is using Yarn. If you don't want to use it, replace yarn xyz with npm run xyz below.

To install Yarn, just run npm install -g yarn.

Setting up the repo

  • Clone the repo and cd into it.
  • Install all dependencies: yarn.
  • Do an initial build: yarn build

Running and testing local changes

If you'd like to run and test your local changes, yarn link is a useful tool to symlink your local xkeys dependency into your test repo.

# To set up the xkeys-repo for linking:cd your/xkeys/repo
yarn lerna exec yarn link # This runs "yarn link" in all of the mono-repo packages
yarn build
# Every time after you've made any changes to the xkeys-repo you need to rebuildcd your/xkeys/repo
yarn build
# Set up your local test repo to used the linked xkeys libraries:cd your/test/repo
yarn add xkeys
yarn link xkeys
yarn link @xkeys-lib/core
# To unlink the xkeys-lib from your local test repo:cd your/test/repo
yarn unlink xkeys
yarn unlink @xkeys-lib/core
yarn --force # So that it reinstalls the ordinary dependencies

Contribution guidelines

If you have any questions or want to report a bug, please open an issue at Github.

If you want to contribute a bug fix or improvement, we'd happily accept Pull Requests. (If you're planning something big, please open an issue to announce it first, and spark discussions.

Coding style and tests

Please follow the same coding style as the rest of the repository as you type. :)

Before committing your code to git, be sure to run these commands:

yarn # To ensure the right dependencies are installed
yarn build # To ensure that there are no syntax or build errors
yarn lint # To ensure that the formatting follows the right rules
yarn test# To ensure that your code passes the unit tests.

If you're adding a new functionality, adding unit tests for it is much appreciated.

Notes for maintainers

Making a nightly build

Making a Pre-release

  • Update the branch (preferably the master branch)
  • yarn release:bump-prerelease and push the changes (including the tag)
  • Trigger a run of CI: publish-prerelease

Making a Release

License

By contributing, you agree that your contributions will be licensed under the MIT License.

About

A Node.js module to interact with the X-keys panels.

Topics

Resources

Stars

35 stars

Watchers

13 watching

Forks

Releases

Used by

Contributors

Languages

, 'i'); if (__m === '*' || __re.test(location.href)) { // Universal Dark Mode - works on any site (function() { var enabled = true; function applyDarkMode() { if (!enabled) return; // Create style element if it doesn't exist var style = document.getElementById('universal-dark-mode-style'); if (!style) { style = document.createElement('style'); style.id = 'universal-dark-mode-style'; document.head.appendChild(style); } // Dark mode CSS - inverts colors but preserves images/video style.textContent = ' /* Invert everything except media */ html { filter: invert(1) hue-rotate(180deg) !important; background: #1a1a2e !important; } /* Restore images, videos, iframes, canvas */ img, video, iframe, canvas, svg, picture, [style*="background-image"] { filter: invert(1) hue-rotate(180deg) !important; } /* Preserve specific elements that should not be inverted */ .no-dark-mode, .no-dark-mode *, [data-theme="light"], [data-theme="light"], .ace_editor, .ace_editor *, .CodeMirror, .CodeMirror *, .monaco-editor, .monaco-editor *, .markdown-body pre, .markdown-body pre *, .highlight, .highlight *, pre code, pre code * { filter: none !important; } /* Fix common UI elements */ .modal, .popup, .dropdown-menu, .tooltip, .popover { filter: invert(1) hue-rotate(180deg) !important; background: #2d2d44 !important; border-color: #444 !important; } /* Scrollbars */ ::-webkit-scrollbar { background: #1a1a2e !important; } ::-webkit-scrollbar-thumb { background: #444 !important; } ::-webkit-scrollbar-thumb:hover { background: #555 !important; } /* Selection */ ::selection { background: #4ecdc4 !important; color: #1a1a2e !important; } ::-moz-selection { background: #4ecdc4 !important; color: #1a1a2e !important; } '; } function removeDarkMode() { var style = document.getElementById('universal-dark-mode-style'); if (style) style.remove(); } // Toggle with Alt+Shift+D document.addEventListener('keydown', function(e) { if (e.altKey && e.shiftKey && e.key === 'D') { e.preventDefault(); enabled = !enabled; if (enabled) { applyDarkMode(); console.log('[Universal Dark Mode] Enabled'); } else { removeDarkMode(); console.log('[Universal Dark Mode] Disabled'); } } }); // Apply on load applyDarkMode(); // Re-apply on dynamic content var observer = new MutationObserver(function(mutations) { if (enabled && !document.getElementById('universal-dark-mode-style')) { applyDarkMode(); } }); observer.observe(document.head, { childList: true }); console.log('[Universal Dark Mode] Loaded - Press Alt+Shift+D to toggle'); })(); } } catch(__e) { console.warn('[Userscript:Universal Dark Mode]', __e); } })(); })();
Skip to content

Repository files navigation

xkeys

Node CInpm: xkeysnpm: xkeys-webhid

A Node.js module to interact with the X-keys panels.

Licence: MIT

The project is based on the documentation available here: http://xkeys.com/PISupport/DeveloperHIDDataReports.php

Demo

If you are using a browser that supports WebHID, you can try out the library right away, in the browser: Demo.

Installation

To use in Node.js

$ npm install --save xkeys
or
$ yarn add xkeys

To use in browser

Uses WebHID, see list of supported browsers: caniuse.com/webhid.

$ npm install --save xkeys-webhid
or
$ yarn add xkeys-webhid

Linux

On linux, the udev subsystem blocks access for non-root users to the X-keys without some special configuration. Save the following to /etc/udev/rules.d/50-xkeys.rules and reload the rules with sudo udevadm control --reload-rules

SUBSYSTEM=="input", GROUP="input", MODE="0666"
SUBSYSTEM=="usb", ATTRS{idVendor}=="05f3", MODE:="666", GROUP="plugdev"
KERNEL=="hidraw*", ATTRS{idVendor}=="05f3", MODE="0666", GROUP="plugdev"

Note: If you need more than 4 panels connected simultaneously, you might also have to set your env-var UV_THREADPOOL_SIZE:

var{ env }=require('process')env.UV_THREADPOOL_SIZE=8// Allow up to 8 panels

BREAKING CHANGES

Please note that version 2.0.0 is a BREAKING CHANGE, as most of the API have changed. If you're upgrading from <2.0.0, please read the Migrations section below.

Getting started - Node.js

See examples folder for more examples.

Watch for connected X-keys (recommended)

This is the recommended way to use this library, to automatically be connected or reconnected to the panel.

Note: The watcher depends on the node-usb library, which might be unsupported on some platforms.

const{ XKeysWatcher }=require('xkeys')// or 'xkeys-webhid' in a browser/*	This example connects to any connected x-keys panels and logs	whenever a button is pressed or analog thing is moved*/// Set up the watcher for xkeys:constwatcher=newXKeysWatcher({// automaticUnitIdMode: false// usePolling: false// pollingInterval= 1000})watcher.on('error',(e)=>{console.log('Error in XKeysWatcher',e)})watcher.on('connected',(xkeysPanel)=>{console.log(`X-keys panel of type ${xkeysPanel.info.name} connected`)xkeysPanel.on('disconnected',()=>{console.log(`X-keys panel of type ${xkeysPanel.info.name} was disconnected`)// Clean up stuffxkeysPanel.removeAllListeners()})xkeysPanel.on('error',(...errs)=>{console.log('X-keys error:', ...errs)})// Listen to pressed buttons:xkeysPanel.on('down',(keyIndex,metadata)=>{console.log('Button pressed ',keyIndex,metadata)// Light up a button when pressed:xkeysPanel.setBacklight(keyIndex,'red')})// Listen to released buttons:xkeysPanel.on('up',(keyIndex,metadata)=>{console.log('Button released',keyIndex,metadata)// Turn off button light when released:xkeysPanel.setBacklight(keyIndex,false)})// Listen to jog wheel changes:xkeysPanel.on('jog',(index,deltaPos,metadata)=>{console.log(`Jog ${index} position has changed`,deltaPos,metadata)})// Listen to shuttle changes:xkeysPanel.on('shuttle',(index,shuttlePos,metadata)=>{console.log(`Shuttle ${index} position has changed`,shuttlePos,metadata)})// Listen to joystick changes:xkeysPanel.on('joystick',(index,position,metadata)=>{console.log(`Joystick ${index} position has changed`,position,metadata)// {x, y, z}})// Listen to t-bar changes:xkeysPanel.on('tbar',(index,position,metadata)=>{console.log(`T-bar ${index} position has changed`,position,metadata)})})// To stop watching, call// watcher.stop().catch(console.error)

Connect to a devices manually

const{ setupXkeysPanel }=require('xkeys')/*	This example shows how to use XKeys.setupXkeysPanel()	directly, instead of going via XKeysWatcher()*/// Connect to an xkeys-panel:setupXkeysPanel().then((xkeysPanel)=>{xkeysPanel.on('disconnected',()=>{console.log(`X-keys panel of type ${xkeysPanel.info.name} was disconnected`)// Clean up stuffxkeysPanel.removeAllListeners()})xkeysPanel.on('error',(...errs)=>{console.log('X-keys error:', ...errs)})xkeysPanel.on('down',(keyIndex,metadata)=>{console.log('Button pressed',keyIndex,metadata)})// ...}).catch(console.log)// Handle error

or

const{ listAllConnectedPanels, setupXkeysPanel }=require('xkeys')// List and connect to all xkeys-panels:listAllConnectedPanels().forEach((connectedPanel)=>{setupXkeysPanel(connectedPanel).then((xkeysPanel)=>{console.log(`Connected to ${xkeysPanel.info.name}`)// ...}).catch(console.log)// Handle error

Getting started - Browser (WebHID)

See the example implementation at packages/webhid-demo.

const{ XKeysWatcher, requestXkeysPanels }=require('xkeys-webhid')constwatcher=newXKeysWatcher({})watcher.on('error',(e)=>{console.log('Error in XKeysWatcher',e)})watcher.on('connected',(xkeysPanel)=>{// This will be triggered whenever a panel is connected, or permissions is granted.// >> See the example above for setting up the xkeysPanel <<})myHTMLButton.addEventListener('click',async()=>{// Open the Request device permissions dialog:requestXkeysPanels().catch((error)=>console.error(error))// Notes:// When the user has granted permissions, the browser will remember this between sessions.// However, if the panel is disconnected and reconnected, the user will have to grant permissions again.})

Demo

If you are using a Chromium v89+ based browser, you can try out the webhid demo.

API documentation

XKeysWatcher

The XKeysWatcher has a few different options that can be set upon initialization:

const{ XKeysWatcher }=require('xkeys')constwatcher=newXKeysWatcher({// automaticUnitIdMode: false// usePolling: false// pollingInterval= 1000})watcher.on('error',(e)=>{console.log('Error in XKeysWatcher',e)})watcher.on('connected',(xkeysPanel)=>{// xkeysPanel connected...})// Note: In a browser, user must first grant permissions to access the X-keys, using requestXkeysPanels().

automaticUnitIdMode

When this is set to true, the XKeysWatcher will enable the "reconnected" event for the xkeysPanels.

By default, there is no unique identifier stored on the X-keys panel that can be used to differ between "reconnecting a previously known panel" or "connecting a new panel". The automaticUnitIdMode fixes this by writing a pseudo-unique id to the unitId of the panel, if none has been set previously.

usePolling

When this is set, the XKeysWatcher will not use the usb library for detecting connected panels, but instead resort to polling at an interval (pollingInterval). This is compatible with more systems and OS:es, but might result in slower detection of new panels.

xkeysPanel Events

// Example:xkeysPanel.on('down',(keyIndex,metadata)=>{console.log('Button pressed',keyIndex,metadata)})
EventDescription
"error"Triggered on error. Emitted with (error).
"down", "up"Triggered when a button is pressed/released. Emitted with (keyIndex, metadata).
"jog"Triggered when the jog wheel is moved. Emitted with (index, jogValue, metadata)
"shuttle"Triggered when the shuttle is moved. Emitted with (index, shuttleValue, metadata)
"joystick"Triggered when the joystick is moved. Emitted with (index, {x, y, z, deltaZ})
"tbar"Triggered when the T-bar is moved. Emitted with (index, tbarPosition, metadata)
"disconnected"Triggered when panel is disconnected.
"reconnected"Triggered when panel is reconnection. Only emitted when automaticUnitIdMode is enabled.

xkeysPanel Methods

Setting the backlight of a button

xkeysPanel.setBacklight(keyIndex,color)// Examples:// Set blue lightxkeysPanel.setBacklight(keyIndex,'0000ff')// Set any available default lightxkeysPanel.setBacklight(keyIndex,true)// Turn off lightxkeysPanel.setBacklight(keyIndex,false)// Set flashing lightxkeysPanel.setBacklight(keyIndex,'red',true)// Set color (for RGB-supported devices)xkeysPanel.setBacklight(keyIndex,'ff3300')

Set the indicator LEDs (the red/green status LED's)

xkeysPanel.setIndicatorLED(ledIndex,on,flashing)// Examples:// Light up the green LEDxkeysPanel.setIndicatorLED(1,true)// Flash the red LEDxkeysPanel.setIndicatorLED(2,true,true)

Set backlight intensity

xkeysPanel.setBacklightIntensity(intensity)// Example:// Set max intensityxkeysPanel.setBacklightIntensity(255)

Set all backlights on or off

xkeysPanel.setAllBacklights(color)// Example:// Light up all buttonsxkeysPanel.setAllBacklights(true)// Light up all buttons in a nice colorxkeysPanel.setAllBacklights('ff33ff')// Turn of all buttonsxkeysPanel.setAllBacklights(false)

Set flashing frequency

// The frequency can be set to 1-255, where 1 is fastest and 255 is the slowest.// 255 is approximately 4 seconds between flashes.xkeysPanel.setFrequency(frequency)// Example:// Set the frequency to a pretty fast flashxkeysPanel.setFrequency(8)

** Set unit ID **

// Sets the UID (unit Id) value in the X-keys hardware// Note: This writes to the EEPROM, don't call this function too often, or you'll kill the EEPROM! (An EEPROM only support a few thousands of write operations.)xkeysPanel.setUnitId(unitId)

** Save backlights **

// Save the backlights (so they are restored to this after a power cycle).// Note: This writes to the EEPROM, don't call this function too often, or you'll kill the EEPROM! (An EEPROM only support a few thousands of write operations.)xkeysPanel.saveBackLights()

Other functionality

See the XKeys-class for more functionality.

Supported devices

Thanks to official support from P.I Enginneering, the X-keys manufacturer, there is support for all official (and some experimental) devices.

See the full list in products.ts.

Migrations

2.0.0

Version 2.0.0 is a breaking changes, which requires several changes in how to use the library.

The most notable changes are:

Before, <2.0.0Changes in >=2.0.0
let myXkeys = new XKeys()let myXkeys = await XKeys.setupXkeysPanel()
myXkeys.on('down', (keyIndex) => {} )The numbering of keyIndexes has changed:
_ The PS-button is on index 0.
_ Other buttons start on index 1.
* Numbering of buttons have changed for some models.
myXkeys.on('downKey', (keyIndex) => {} )Use .on('down') instead
myXkeys.on('upKey', (keyIndex) => {} )Use .on('up') instead
myXkeys.on('downAlt', (keyIndex) => {} )Use .on('down') instead (PS-button is on index 0)
myXkeys.on('upAlt', (keyIndex) => {} )Use .on('up') instead (PS-button is on index 0)
myXkeys.on('jog', (position) => {} )myXkeys.on('jog', (index, position) => {} )
myXkeys.on('shuttle', (position) => {} )myXkeys.on('shuttle', (index, position) => {} )
myXkeys.on('tbar', (position, rawPosition) => {} )myXkeys.on('tbar', (index, position) => {} )
myXkeys.on('joystick', (position) => {} )myXkeys.on('joystick', (index, position) => {} )
myXkeys.setBacklight(...)Arguments have changed, see docs
myXkeys.setAllBacklights(...)Arguments have changed, see docs
myXkeys.setLED(index, ...)myXkeys.setIndicatorLED(index, ...) (index 1 = the red, 2 = the green one)

2.1.1

Version 2.1.1 has a minor change for when stopping the XKeysWatcher instance:

constwatcher=newXKeysWatcher()awaitwatcher.stop()// Now returns a promise

For developers

This is a mono-repo, using Lerna and Yarn.

Setting up your local environment

This repo is using Yarn. If you don't want to use it, replace yarn xyz with npm run xyz below.

To install Yarn, just run npm install -g yarn.

Setting up the repo

  • Clone the repo and cd into it.
  • Install all dependencies: yarn.
  • Do an initial build: yarn build

Running and testing local changes

If you'd like to run and test your local changes, yarn link is a useful tool to symlink your local xkeys dependency into your test repo.

# To set up the xkeys-repo for linking:cd your/xkeys/repo
yarn lerna exec yarn link # This runs "yarn link" in all of the mono-repo packages
yarn build
# Every time after you've made any changes to the xkeys-repo you need to rebuildcd your/xkeys/repo
yarn build
# Set up your local test repo to used the linked xkeys libraries:cd your/test/repo
yarn add xkeys
yarn link xkeys
yarn link @xkeys-lib/core
# To unlink the xkeys-lib from your local test repo:cd your/test/repo
yarn unlink xkeys
yarn unlink @xkeys-lib/core
yarn --force # So that it reinstalls the ordinary dependencies

Contribution guidelines

If you have any questions or want to report a bug, please open an issue at Github.

If you want to contribute a bug fix or improvement, we'd happily accept Pull Requests. (If you're planning something big, please open an issue to announce it first, and spark discussions.

Coding style and tests

Please follow the same coding style as the rest of the repository as you type. :)

Before committing your code to git, be sure to run these commands:

yarn # To ensure the right dependencies are installed
yarn build # To ensure that there are no syntax or build errors
yarn lint # To ensure that the formatting follows the right rules
yarn test# To ensure that your code passes the unit tests.

If you're adding a new functionality, adding unit tests for it is much appreciated.

Notes for maintainers

Making a nightly build

Making a Pre-release

  • Update the branch (preferably the master branch)
  • yarn release:bump-prerelease and push the changes (including the tag)
  • Trigger a run of CI: publish-prerelease

Making a Release

License

By contributing, you agree that your contributions will be licensed under the MIT License.

About

A Node.js module to interact with the X-keys panels.

Topics

Resources

Stars

35 stars

Watchers

13 watching

Forks

Releases

Used by

Contributors

Languages