diff --git a/README.md b/README.md index 9f45914..efecbf5 100644 --- a/README.md +++ b/README.md @@ -54,6 +54,8 @@ If you're upgrading from `<2.0.0`, please read the [_Migrations_](#Migrations) s ## Getting started - Node.js +_[See examples folder for more examples.](./packages/node/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. diff --git a/packages/core/src/xkeys.ts b/packages/core/src/xkeys.ts index 3348278..c9bb730 100644 --- a/packages/core/src/xkeys.ts +++ b/packages/core/src/xkeys.ts @@ -47,6 +47,25 @@ export class XKeys extends EventEmitter { static get vendorId(): number { return XKEYS_VENDOR_ID } + static filterDevice(deviceInfo: DeviceInfo): { product: Product; productId: number; interface: number } | null { + if (deviceInfo.vendorId !== XKEYS_VENDOR_ID) return null + + for (const product of Object.values(PRODUCTS)) { + for (const hidDevice of product.hidDevices) { + if ( + hidDevice[0] === deviceInfo.productId && + (deviceInfo.interface === null || hidDevice[1] === deviceInfo.interface) + ) { + return { + product, + productId: hidDevice[0], + interface: hidDevice[1], + } // Return & break out of the loops + } + } + } + return null + } constructor(private device: HIDDevice, private deviceInfo: DeviceInfo, private _devicePath: string | undefined) { super() @@ -54,27 +73,11 @@ export class XKeys extends EventEmitter { this.product = this._setupDevice(deviceInfo) } private _setupDevice(deviceInfo: DeviceInfo) { - const findProduct = (): { product: Product; productId: number; interface: number } => { - for (const product of Object.values(PRODUCTS)) { - for (const hidDevice of product.hidDevices) { - if ( - hidDevice[0] === deviceInfo.productId && - (deviceInfo.interface === null || hidDevice[1] === deviceInfo.interface) - ) { - return { - product, - productId: hidDevice[0], - interface: hidDevice[1], - } // Return & break out of the loops - } - } - } - // else: + const found = XKeys.filterDevice(deviceInfo) + if (!found) throw new Error( `Unknown/Unsupported X-keys: "${deviceInfo.product}" (productId: "${deviceInfo.productId}", interface: "${deviceInfo.interface}").\nPlease report this as an issue on our github page!` ) - } - const found = findProduct() this.device.on('data', (data: Buffer) => { if (deviceInfo.productId === 210) { @@ -813,7 +816,9 @@ export class XKeys extends EventEmitter { } type HIDMessage = (string | number)[] interface DeviceInfo { + /** Name of the panel */ product: string | undefined + vendorId: number productId: number interface: number | null // null means "anything goes", used when interface isn't available } diff --git a/packages/node/examples/manually-connect.js b/packages/node/examples/simply-connect.js similarity index 100% rename from packages/node/examples/manually-connect.js rename to packages/node/examples/simply-connect.js diff --git a/packages/node/examples/using-node-hid.js b/packages/node/examples/using-node-hid.js new file mode 100644 index 0000000..a3ae3f1 --- /dev/null +++ b/packages/node/examples/using-node-hid.js @@ -0,0 +1,48 @@ +const HID = require('node-hid') +const { setupXkeysPanel, XKeys } = require('xkeys') + +/* + This example shows how to use node-hid to list all connected usb devices, then + connecting to any supported X-keys panels. +*/ + +Promise.resolve().then(async () => { + + // List all connected usb devices: + const devices = await HID.devicesAsync() + + for (const device of devices) { + + // Filter for supported X-keys devices: + if (XKeys.filterDevice(device)) { + + console.log('Connecting to X-keys device:', device.product) + + setupXkeysPanel(device) + .then((xkeysPanel) => { + xkeysPanel.on('disconnected', () => { + console.log(`X-keys panel of type ${xkeysPanel.info.name} was disconnected`) + // Clean up stuff + xkeysPanel.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 + + } else { + // is not an X-keys device + console.log('Not a supported X-keys device:', device.product || device.productId) + } + + } + +}).catch(console.log) + diff --git a/packages/node/src/methods.ts b/packages/node/src/methods.ts index 65ee576..eac8c61 100644 --- a/packages/node/src/methods.ts +++ b/packages/node/src/methods.ts @@ -1,5 +1,4 @@ -import { Product, XKeys } from '@xkeys-lib/core' -import { PRODUCTS } from '@xkeys-lib/core' +import { XKeys } from '@xkeys-lib/core' import * as HID from 'node-hid' import { NodeHIDDevice } from './node-hid-wrapper' @@ -24,6 +23,7 @@ export async function setupXkeysPanel( let deviceInfo: | { product: string | undefined + vendorId: number productId: number interface: number } @@ -42,6 +42,7 @@ export async function setupXkeysPanel( deviceInfo = { product: connectedXkeys[0].product, + vendorId: connectedXkeys[0].vendorId, productId: connectedXkeys[0].productId, interface: connectedXkeys[0].interface, } @@ -55,6 +56,7 @@ export async function setupXkeysPanel( deviceInfo = { product: devicePathOrHIDDevice.product, + vendorId: devicePathOrHIDDevice.vendorId, productId: devicePathOrHIDDevice.productId, interface: devicePathOrHIDDevice.interface, } @@ -84,6 +86,7 @@ export async function setupXkeysPanel( deviceInfo = { product: dInfo.product, + vendorId: dInfo.vendorId, productId: dInfo.productId, interface: dInfo.interface, } @@ -97,6 +100,7 @@ export async function setupXkeysPanel( // Look through HID.devices(), because HID.Device contains the productId deviceInfo = { product: nodeHidInfo.product, + vendorId: nodeHidInfo.vendorId, productId: nodeHidInfo.productId, interface: nodeHidInfo.interface, } @@ -125,20 +129,16 @@ export function listAllConnectedPanels(): HID_Device[] { const connectedXkeys = HID.devices().filter((device) => { // Filter to only return the supported devices: - if (device.vendorId !== XKeys.vendorId) return false if (!device.path) return false - let found = false - for (const product of Object.values(PRODUCTS)) { - for (const hidDevice of product.hidDevices) { - if (hidDevice[0] === device.productId && hidDevice[1] === device.interface) { - found = true - break - } - } - if (found) break - } - return found + const found = XKeys.filterDevice({ + product: device.product, + interface: device.interface, + vendorId: device.vendorId, + productId: device.productId, + }) + if (!found) return false + return true }) return connectedXkeys as HID_Device[] } diff --git a/packages/webhid/src/methods.ts b/packages/webhid/src/methods.ts index 96b57df..13becec 100644 --- a/packages/webhid/src/methods.ts +++ b/packages/webhid/src/methods.ts @@ -38,6 +38,7 @@ export async function setupXkeysPanel(browserDevice: HIDDevice): Promise if (!isValidXkeysUsage(browserDevice)) throw new Error(`Device has incorrect usage/interface`) if (!browserDevice.productId) throw Error(`Device has no productId!`) + const vendorId = browserDevice.vendorId const productId = browserDevice.productId if (!browserDevice.opened) { @@ -50,6 +51,7 @@ export async function setupXkeysPanel(browserDevice: HIDDevice): Promise deviceWrap, { product: browserDevice.productName, + vendorId: vendorId, productId: productId, interface: null, // todo: Check what to use here (collection.usage?) },