Skip to content

Commit

Permalink
feat: replace usb-detection with usb
Browse files Browse the repository at this point in the history
  • Loading branch information
Julusian committed Jul 30, 2022
1 parent 615db0c commit d6349ef
Show file tree
Hide file tree
Showing 6 changed files with 101 additions and 153 deletions.
79 changes: 42 additions & 37 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -58,7 +58,7 @@ If you're upgrading from `<2.0.0`, please read the [_Migrations_](#Migrations) s

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-detection](https://github.com/MadLittleMods/node-usb-detection) library, which might be unsupported on some platforms._
_Note: The watcher depends on the [node-usb](https://github.com/node-usb/node-usb) library, which might be unsupported on some platforms._

```javascript
const { XKeysWatcher } = require('xkeys')
Expand Down Expand Up @@ -199,6 +199,7 @@ watcher.on('connected', (xkeysPanel) => {
```
#### 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
Expand All @@ -208,7 +209,7 @@ if none has been set previously.
#### usePolling
When this is set, the XKeysWatcher will not use the `usb-detection` library for detecting connected panels,
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.
Expand All @@ -221,16 +222,16 @@ xkeysPanel.on('down', (keyIndex, metadata) => {
})
```
| Event | Description |
| -- | --- |
| `"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](#automaticUnitIdMode) is enabled. |
| Event | Description |
| ---------------- | --------------------------------------------------------------------------------------------------------------- |
| `"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](#automaticUnitIdMode) is enabled. |
### xkeysPanel Methods
Expand Down Expand Up @@ -335,25 +336,26 @@ Version `2.0.0` is a breaking changes, which requires several changes in how to
The most notable changes are:
| Before, `<2.0.0` | Changes in `>=2.0.0` |
| -- | -- |
| `let myXkeys = new XKeys()` | `let myXkeys = await XKeys.setupXkeysPanel()` |
| Before, `<2.0.0` | Changes in `>=2.0.0` |
| ---------------------------------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `let myXkeys = new XKeys()` | `let myXkeys = await XKeys.setupXkeysPanel()` |
| `myXkeys.on('down', (keyIndex) => {} )` | The numbering of `keyIndexes` has changed:<br/>_ The PS-button is on index 0.<br/>_ Other buttons start on index 1.<br/>\* 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) |
| `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:
```javascript
const watcher = new XKeysWatcher()
await watcher.stop() // Now returns a promise
Expand All @@ -379,7 +381,7 @@ To install Yarn, just run `npm install -g yarn`.
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.
``` bash
```bash
# 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
Expand Down Expand Up @@ -421,25 +423,28 @@ 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.
If you're adding a new functionality, adding unit tests for it is much appreciated.

### Notes to maintainers

#### Making a nightly build
* Push your changes to any branch
* Trigger a run of [CI: publish-nightly](https://github.com/SuperFlyTV/xkeys/actions/workflows/publish-nightly.yml)

- Push your changes to any branch
- Trigger a run of [CI: publish-nightly](https://github.com/SuperFlyTV/xkeys/actions/workflows/publish-nightly.yml)

#### Making a Pre-release
* Update the branch (preferrably the master branch)
* `yarn release:bump-prerelease` and push the changes (including the tag)
* Trigger a run of [CI: publish-prerelease](https://github.com/SuperFlyTV/xkeys/actions/workflows/publish-prerelease.yml)

- Update the branch (preferrably the master branch)
- `yarn release:bump-prerelease` and push the changes (including the tag)
- Trigger a run of [CI: publish-prerelease](https://github.com/SuperFlyTV/xkeys/actions/workflows/publish-prerelease.yml)

#### Making a Release
* Update the the master branch
* `yarn release:bump-release` and push the changes (including the tag)
* Trigger a run of [CI: publish-release](https://github.com/SuperFlyTV/xkeys/actions/workflows/publish-release.yml) to publish to NPM.
* Trigger a run of [CI: publish-demo](https://github.com/SuperFlyTV/xkeys/actions/workflows/publish-demo.yml) to update the docs.

- Update the the master branch
- `yarn release:bump-release` and push the changes (including the tag)
- Trigger a run of [CI: publish-release](https://github.com/SuperFlyTV/xkeys/actions/workflows/publish-release.yml) to publish to NPM.
- Trigger a run of [CI: publish-demo](https://github.com/SuperFlyTV/xkeys/actions/workflows/publish-demo.yml) to update the docs.

### License

Expand Down
2 changes: 1 addition & 1 deletion packages/node/examples/multiple-panels.js
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ const memory = {}
const watcher = new XKeysWatcher({
automaticUnitIdMode: true,

// If running on a system (such as some linux flavors) where the 'usb-detection' library doesn't work, enable usePolling instead:
// If running on a system (such as some linux flavors) where the 'usb' library doesn't work, enable usePolling instead:
// usePolling: true,
// pollingInterval: 1000,
})
Expand Down
2 changes: 1 addition & 1 deletion packages/node/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -46,7 +46,7 @@
"tslib": "^2.4.0"
},
"optionalDependencies": {
"usb-detection": "^4.14.1"
"usb": "^2.5.0"
},
"keywords": [
"xkeys",
Expand Down
80 changes: 32 additions & 48 deletions packages/node/src/watcher.ts
Original file line number Diff line number Diff line change
@@ -1,36 +1,30 @@
import type * as USBDetectNS from 'usb-detection'
import type { usb } from 'usb'
import { EventEmitter } from 'events'
import { XKeys, XKEYS_VENDOR_ID } from '@xkeys-lib/core'
import { listAllConnectedPanels, setupXkeysPanel } from '.'

interface USBDetectType {
startMonitoring: typeof USBDetectNS.startMonitoring
stopMonitoring: typeof USBDetectNS.stopMonitoring
on: typeof USBDetectNS.on
}

let USBDetectImport: USBDetectType | undefined
let USBImport: typeof usb | undefined
let hasTriedImport = false

// Because usb-detection is an optional dependency, we have to use in a somewhat messy way:
function USBDetect(): USBDetectType {
if (USBDetectImport) return USBDetectImport
// Because usb is an optional dependency, we have to use in a somewhat messy way:
function USBDetect(): typeof usb {
if (USBImport) return USBImport

if (!hasTriedImport) {
hasTriedImport = true
try {
// eslint-disable-next-line @typescript-eslint/no-var-requires
const usbDetection = require('usb-detection')
USBDetectImport = usbDetection
return usbDetection
const usb: typeof import('usb') = require('usb')
USBImport = usb.usb
return USBImport
} catch (err) {
// It's not installed
}
}
// else emit error:
throw `XKeysWatcher requires the dependency "usb-detection" to be installed, it might have been skipped due to your platform being unsupported (this is an issue with "usb-detection", not the X-keys library).
throw `XKeysWatcher requires the dependency "usb" to be installed, it might have been skipped due to your platform being unsupported (this is an issue with "usb", not the X-keys library).
Possible solutions are:
* You can try to install the depencency manually, by running "npm install usb-detection".
* You can try to install the depencency manually, by running "npm install usb".
* Use the fallback "usePolling" functionality instead: new XKeysWatcher({ usePolling: true})
* Otherwise you can still connect to X-keys panels manually by using XKeys.setupXkeysPanel().
`
Expand All @@ -47,7 +41,6 @@ export declare interface XKeysWatcher {
on<U extends keyof XKeysWatcherEvents>(event: U, listener: XKeysWatcherEvents[U]): this
emit<U extends keyof XKeysWatcherEvents>(event: U, ...args: Parameters<XKeysWatcherEvents[U]>): boolean
}
let watcherCount = 0
/**
* Set up a watcher for newly connected X-keys panels.
* Note: It is highly recommended to set up a listener for the disconnected event on the X-keys panel, to clean up after a disconnected device.
Expand Down Expand Up @@ -76,15 +69,9 @@ export class XKeysWatcher extends EventEmitter {
super()

if (!this.options?.usePolling) {
watcherCount++
if (watcherCount === 1) {
// We've just started watching
USBDetect().startMonitoring()
}

// Watch for added devices:
USBDetect().on(`add:${XKEYS_VENDOR_ID}`, this.onAddedUSBDevice)
USBDetect().on(`remove:${XKEYS_VENDOR_ID}`, this.onRemovedUSBDevice)
USBDetect().on('attach', this.onAddedUSBDevice)
USBDetect().on('detach', this.onRemovedUSBDevice)
} else {
this.pollingInterval = setInterval(() => {
this.triggerUpdateConnectedDevices(true)
Expand All @@ -103,15 +90,8 @@ export class XKeysWatcher extends EventEmitter {

if (!this.options?.usePolling) {
// Remove the listeners:
// @ts-expect-error usb-detection exposes wrong types:
USBDetect().removeListener(`add:${XKEYS_VENDOR_ID}`, this.onAddedUSBDevice)
// @ts-expect-error usb-detection exposes wrong types:
USBDetect().removeListener(`remove:${XKEYS_VENDOR_ID}`, this.onRemovedUSBDevice)

watcherCount--
if (watcherCount === 0) {
USBDetect().stopMonitoring()
}
USBDetect().off('attach', this.onAddedUSBDevice)
USBDetect().off('detach', this.onRemovedUSBDevice)
}

if (this.pollingInterval) {
Expand All @@ -129,20 +109,24 @@ export class XKeysWatcher extends EventEmitter {
await Promise.all(ps)
}
}
private onAddedUSBDevice = (_device: USBDetectNS.Device) => {
// Called whenever a new USB device is added
this.debugLog('onAddedUSBDevice')
if (this.isMonitoring) {
this.shouldFindChangedReTries++
this.triggerUpdateConnectedDevices(true)
private onAddedUSBDevice = (device: usb.Device) => {
if (device.deviceDescriptor.idVendor === XKEYS_VENDOR_ID) {
// Called whenever a new USB device is added
this.debugLog('onAddedUSBDevice')
if (this.isMonitoring) {
this.shouldFindChangedReTries++
this.triggerUpdateConnectedDevices(true)
}
}
}
private onRemovedUSBDevice = (_device: USBDetectNS.Device) => {
// Called whenever a new USB device is removed
this.debugLog('onRemovedUSBDevice')
if (this.isMonitoring) {
this.shouldFindChangedReTries++
this.triggerUpdateConnectedDevices(true)
private onRemovedUSBDevice = (device: usb.Device) => {
if (device.deviceDescriptor.idVendor === XKEYS_VENDOR_ID) {
// Called whenever a new USB device is removed
this.debugLog('onRemovedUSBDevice')
if (this.isMonitoring) {
this.shouldFindChangedReTries++
this.triggerUpdateConnectedDevices(true)
}
}
}
private triggerUpdateConnectedDevices(asap: boolean): void {
Expand Down Expand Up @@ -186,8 +170,8 @@ export class XKeysWatcher extends EventEmitter {
this.debugLog('updateConnectedDevices')
// Note:
// This implementation is a bit awkward,
// the reason for that is that I couldn't find a good way to relate the output from usb-detection to node-hid devices
// So we're just using the usb-detection to trigger a re-check for new devices and cache the seen devices
// there isnt a good way to relate the output from usb to node-hid devices
// So we're just using the events to trigger a re-check for new devices and cache the seen devices

listAllConnectedPanels().forEach((xkeysDevice) => {
if (xkeysDevice.path) {
Expand Down
3 changes: 2 additions & 1 deletion tsconfig.build.json
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@
"declarationMap": true,
// Target: node 10
"target": "es2018",
"lib": ["es2018"]
"lib": ["es2018"],
"skipLibCheck": true
}
}
Loading

0 comments on commit d6349ef

Please sign in to comment.