Skip to content

Commit

Permalink
feat: use async node-hid
Browse files Browse the repository at this point in the history
  • Loading branch information
Julusian committed May 2, 2023
1 parent 827d401 commit 429c5ea
Show file tree
Hide file tree
Showing 5 changed files with 130 additions and 72 deletions.
5 changes: 2 additions & 3 deletions packages/node/package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "xkeys",
"version": "2.4.0",
"version": "2.5.0-0",
"description": "An npm module for interfacing with the X-keys panels in Node.js",
"main": "dist/index.js",
"typings": "dist/index.d.ts",
Expand Down Expand Up @@ -40,9 +40,8 @@
"node": ">=10"
},
"dependencies": {
"@types/node-hid": "^1.3.1",
"@xkeys-lib/core": "2.4.0",
"node-hid": "^2.1.1",
"node-hid": "npm:@julusian/hid@^3.0.0-0",
"tslib": "^2.4.0"
},
"optionalDependencies": {
Expand Down
6 changes: 3 additions & 3 deletions packages/node/src/lib.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,16 +3,16 @@ import type * as HID from 'node-hid'
* This file contains internal convenience functions
*/

export function isHID_Device(device: HID.Device | HID.HID | string): device is HID.Device {
export function isHID_Device(device: HID.Device | HID.HIDAsync | string): device is HID.Device {
return (
typeof device === 'object' &&
(device as HID.Device).vendorId !== undefined &&
(device as HID.Device).productId !== undefined &&
(device as HID.Device).interface !== undefined
)
}
type HID_HID = HID.HID & { devicePath: string }
export function isHID_HID(device: HID.Device | HID.HID | string): device is HID_HID {
type HID_HID = HID.HIDAsync & { devicePath: string }
export function isHID_HID(device: HID.Device | HID.HIDAsync | string): device is HID_HID {
return (
typeof device === 'object' &&
(device as HID_HID).write !== undefined &&
Expand Down
125 changes: 64 additions & 61 deletions packages/node/src/methods.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,11 +10,11 @@ import { HID_Device } from './api'
/** Sets up a connection to a HID device (the X-keys panel) */
export function setupXkeysPanel(): Promise<XKeys>
export function setupXkeysPanel(HIDDevice: HID.Device): Promise<XKeys>
export function setupXkeysPanel(HIDDevice: HID.HID): Promise<XKeys>
export function setupXkeysPanel(HIDDevice: HID.HIDAsync): Promise<XKeys>
export function setupXkeysPanel(devicePath: string): Promise<XKeys>
export async function setupXkeysPanel(devicePathOrHIDDevice?: HID.Device | HID.HID | string): Promise<XKeys> {
export async function setupXkeysPanel(devicePathOrHIDDevice?: HID.Device | HID.HIDAsync | string): Promise<XKeys> {
let devicePath: string
let device: HID.HID
let device: HID.HIDAsync | undefined
let deviceInfo:
| {
product: string | undefined
Expand All @@ -23,76 +23,79 @@ export async function setupXkeysPanel(devicePathOrHIDDevice?: HID.Device | HID.H
}
| undefined

if (!devicePathOrHIDDevice) {
// Device not provided, will then select any connected device:
const connectedXkeys = listAllConnectedPanels()
if (!connectedXkeys.length) {
throw new Error('Could not find any connected X-keys panels.')
}
// Just select the first one:
devicePath = connectedXkeys[0].path
device = new HID.HID(devicePath)

deviceInfo = {
product: connectedXkeys[0].product,
productId: connectedXkeys[0].productId,
interface: connectedXkeys[0].interface,
}
} else if (isHID_Device(devicePathOrHIDDevice)) {
// is HID.Device
try {
if (!devicePathOrHIDDevice) {
// Device not provided, will then select any connected device:
const connectedXkeys = listAllConnectedPanels()
if (!connectedXkeys.length) {
throw new Error('Could not find any connected X-keys panels.')
}
// Just select the first one:
devicePath = connectedXkeys[0].path
device = await HID.HIDAsync.open(devicePath)

deviceInfo = {
product: connectedXkeys[0].product,
productId: connectedXkeys[0].productId,
interface: connectedXkeys[0].interface,
}
} else if (isHID_Device(devicePathOrHIDDevice)) {
// is HID.Device

if (!devicePathOrHIDDevice.path) throw new Error('HID.Device path not set!')
if (!devicePathOrHIDDevice.path) throw new Error('HID.Device path not set!')

devicePath = devicePathOrHIDDevice.path
device = new HID.HID(devicePath)
devicePath = devicePathOrHIDDevice.path
device = await HID.HIDAsync.open(devicePath)

deviceInfo = {
product: devicePathOrHIDDevice.product,
productId: devicePathOrHIDDevice.productId,
interface: devicePathOrHIDDevice.interface,
deviceInfo = {
product: devicePathOrHIDDevice.product,
productId: devicePathOrHIDDevice.productId,
interface: devicePathOrHIDDevice.interface,
}
} else if (isHID_HID(devicePathOrHIDDevice)) {
// is HID.HID

device = devicePathOrHIDDevice
devicePath = devicePathOrHIDDevice.devicePath
// deviceInfo is set later
} else if (typeof devicePathOrHIDDevice === 'string') {
// is string (path)

devicePath = devicePathOrHIDDevice
device = await HID.HIDAsync.open(devicePath)
// deviceInfo is set later
} else {
throw new Error('setupXkeysPanel: invalid arguments')
}
} else if (isHID_HID(devicePathOrHIDDevice)) {
// is HID.HID

device = devicePathOrHIDDevice
devicePath = devicePathOrHIDDevice.devicePath
// deviceInfo is set later
} else if (typeof devicePathOrHIDDevice === 'string') {
// is string (path)

devicePath = devicePathOrHIDDevice
device = new HID.HID(devicePath)
// deviceInfo is set later
} else {
throw new Error('setupXkeysPanel: invalid arguments')
}

if (!deviceInfo) {
// Look through HID.devices(), bevause HID.Device contains the productId
for (const hidDevice of HID.devices()) {
if (hidDevice.path === devicePath) {
deviceInfo = {
product: hidDevice.product,
productId: hidDevice.productId,
interface: hidDevice.interface,
}
break
if (!deviceInfo) {
// @ts-expect-error getDeviceInfo missing in typings
const nodeHidInfo: HID.Device = await this.device.getDeviceInfo()
// Look through HID.devices(), bevause HID.Device contains the productId
deviceInfo = {
product: nodeHidInfo.product,
productId: nodeHidInfo.productId,
interface: nodeHidInfo.interface,
}
}
}

if (!device) throw new Error('Error setting up X-keys: device not found')
if (!devicePath) throw new Error('Error setting up X-keys: devicePath not found')
if (!deviceInfo) throw new Error('Error setting up X-keys: deviceInfo not found')
if (!device) throw new Error('Error setting up X-keys: device not found')
if (!devicePath) throw new Error('Error setting up X-keys: devicePath not found')
if (!deviceInfo) throw new Error('Error setting up X-keys: deviceInfo not found')

const deviceWrap = new NodeHIDDevice(device)
const deviceWrap = new NodeHIDDevice(device)

const xkeys = new XKeys(deviceWrap, deviceInfo, devicePath)
const xkeys = new XKeys(deviceWrap, deviceInfo, devicePath)

// Wait for the device to initialize:
await xkeys.init()
// Wait for the device to initialize:
await xkeys.init()

return xkeys
return xkeys
} catch (e) {
if (device) await device.close().catch(() => null) // Suppress error

throw e
}
}
/** Returns a list of all connected X-keys-HID-devices */
export function listAllConnectedPanels(): HID_Device[] {
Expand Down
8 changes: 5 additions & 3 deletions packages/node/src/node-hid-wrapper.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ import * as HID from 'node-hid'
* This translates it into the common format (@see HIDDevice) defined by @xkeys-lib/core
*/
export class NodeHIDDevice extends EventEmitter implements HIDDevice {
constructor(private device: HID.HID) {
constructor(private device: HID.HIDAsync) {
super()
this._handleData = this._handleData.bind(this)
this._handleError = this._handleError.bind(this)
Expand All @@ -17,11 +17,13 @@ export class NodeHIDDevice extends EventEmitter implements HIDDevice {
}

public write(data: number[]): void {
this.device.write(data)
this.device.write(data).catch((err) => {
this.emit('error', err)
})
}

public async close(): Promise<void> {
this.device.close()
await this.device.close()

// For some unknown reason, we need to wait a bit before returning because it
// appears that the HID-device isn't actually closed properly until after a short while.
Expand Down
58 changes: 56 additions & 2 deletions yarn.lock
Original file line number Diff line number Diff line change
Expand Up @@ -2881,6 +2881,15 @@ cliui@^7.0.2:
strip-ansi "^6.0.0"
wrap-ansi "^7.0.0"

cliui@^8.0.1:
version "8.0.1"
resolved "https://registry.yarnpkg.com/cliui/-/cliui-8.0.1.tgz#0c04b075db02cbfe60dc8e6cf2f5486b1a3608aa"
integrity sha512-BSeNnyus75C4//NQ9gQt1/csTXyo/8Sb+afLAkzAptFuMsod9HFokGNudZpi/oQV73hnVK+sR+5PVRMd+Dr7YQ==
dependencies:
string-width "^4.2.0"
strip-ansi "^6.0.1"
wrap-ansi "^7.0.0"

clone-deep@^4.0.1:
version "4.0.1"
resolved "https://registry.yarnpkg.com/clone-deep/-/clone-deep-4.0.1.tgz#c19fd9bdbbf85942b4fd979c84dcf7d5f07c2387"
Expand Down Expand Up @@ -7160,7 +7169,7 @@ node-abi@^2.21.0:
dependencies:
semver "^5.4.1"

node-addon-api@^3.0.2:
node-addon-api@^3.0.2, node-addon-api@^3.2.1:
version "3.2.1"
resolved "https://registry.yarnpkg.com/node-addon-api/-/node-addon-api-3.2.1.tgz#81325e0a2117789c0128dab65e7e38f07ceba161"
integrity sha512-mmcei9JghVNDYydghQmeDX8KoAm0FAiYyIcUt/N4nhyAipB17pllZQDOJD2fotxABnt4Mdz+dKTO7eftLg4d0A==
Expand Down Expand Up @@ -7229,6 +7238,14 @@ node-hid@^2.1.1:
node-addon-api "^3.0.2"
prebuild-install "^6.0.0"

"node-hid@npm:@julusian/hid@^3.0.0-0":
version "3.0.0-0"
resolved "https://registry.yarnpkg.com/@julusian/hid/-/hid-3.0.0-0.tgz#f3cbde6a13781e3c436f388c24225805006582fa"
integrity sha512-7k1lpDZcI8zH8Qsu6ivBWMhcJL7TxaJLZiYGx0KoxGPJhOvkEf7z+ZwHk02wZmh+z5gccHGKIpMKhP5P54aZeQ==
dependencies:
node-addon-api "^3.2.1"
pkg-prebuilds "^0.2.1"

node-int64@^0.4.0:
version "0.4.0"
resolved "https://registry.yarnpkg.com/node-int64/-/node-int64-0.4.0.tgz#87a9065cdb355d3182d8f94ce11188b825c68a3b"
Expand Down Expand Up @@ -8008,6 +8025,13 @@ pkg-dir@^4.2.0:
dependencies:
find-up "^4.0.0"

pkg-prebuilds@^0.2.1:
version "0.2.1"
resolved "https://registry.yarnpkg.com/pkg-prebuilds/-/pkg-prebuilds-0.2.1.tgz#4b91f410ab600df4eb657634d623549cc188c5ed"
integrity sha512-FdOlDiRqRL7i9aYzQflhGWCoiJf/8u6Qgzq48gKsRDYejtfjvGb1U5QGSzllcqpNg2a8Swx/9fMgtuVefwU+zw==
dependencies:
yargs "^17.5.1"

portfinder@^1.0.26:
version "1.0.28"
resolved "https://registry.yarnpkg.com/portfinder/-/portfinder-1.0.28.tgz#67c4622852bd5374dd1dd900f779f53462fac778"
Expand Down Expand Up @@ -9337,7 +9361,7 @@ string-width@^1.0.1:
is-fullwidth-code-point "^1.0.0"
strip-ansi "^3.0.0"

"string-width@^1.0.2 || 2 || 3 || 4", string-width@^4.1.0, string-width@^4.2.0:
"string-width@^1.0.2 || 2 || 3 || 4", string-width@^4.1.0, string-width@^4.2.0, string-width@^4.2.3:
version "4.2.3"
resolved "https://registry.yarnpkg.com/string-width/-/string-width-4.2.3.tgz#269c7117d27b05ad2e536830a8ec895ef9c6d010"
integrity sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==
Expand Down Expand Up @@ -10570,6 +10594,18 @@ ws@^7.4.6:
resolved "https://registry.yarnpkg.com/ws/-/ws-7.5.9.tgz#54fa7db29f4c7cec68b1ddd3a89de099942bb591"
integrity sha512-F+P9Jil7UiSKSkppIiD94dN07AwvFixvLIj1Og1Rl9GGMuNipJnV9JzjD6XuqmAeiswGvUmNLjr5cFuXwNS77Q==

[email protected]:
version "2.4.0"
resolved "https://registry.yarnpkg.com/xkeys/-/xkeys-2.4.0.tgz#bad32c6968290fa89c66900c53ee4515b4aede54"
integrity sha512-/9+U26SlopufMlMvoVEbFCTnSPzl9jKeb3HWjHg5Tl/MQKKQG98yUDWsFF2BGPUKxFwBqZfmYGaQ2jsaD2S5XQ==
dependencies:
"@types/node-hid" "^1.3.1"
"@xkeys-lib/core" "2.4.0"
node-hid "^2.1.1"
tslib "^2.4.0"
optionalDependencies:
usb "^2.5.2"

xml-name-validator@^3.0.0:
version "3.0.0"
resolved "https://registry.yarnpkg.com/xml-name-validator/-/xml-name-validator-3.0.0.tgz#6ae73e06de4d8c6e47f9fb181f78d648ad457c6a"
Expand Down Expand Up @@ -10636,6 +10672,11 @@ yargs-parser@^18.1.2:
camelcase "^5.0.0"
decamelize "^1.2.0"

yargs-parser@^21.1.1:
version "21.1.1"
resolved "https://registry.yarnpkg.com/yargs-parser/-/yargs-parser-21.1.1.tgz#9096bceebf990d21bb31fa9516e0ede294a77d35"
integrity sha512-tVpsJW7DdjecAiFpbIB1e3qxIQsE6NoPc5/eTdrbbIC4h0LVsWhnoa3g+m2HclBIujHzsxZ4VJVA+GUuc2/LBw==

yargs@^13.3.2:
version "13.3.2"
resolved "https://registry.yarnpkg.com/yargs/-/yargs-13.3.2.tgz#ad7ffefec1aa59565ac915f82dccb38a9c31a2dd"
Expand Down Expand Up @@ -10682,6 +10723,19 @@ yargs@^16.2.0:
y18n "^5.0.5"
yargs-parser "^20.2.2"

yargs@^17.5.1:
version "17.7.2"
resolved "https://registry.yarnpkg.com/yargs/-/yargs-17.7.2.tgz#991df39aca675a192b816e1e0363f9d75d2aa269"
integrity sha512-7dSzzRQ++CKnNI/krKnYRV7JKKPUXMEh61soaHKg9mrWEhzFWhFnxPxGl+69cD1Ou63C13NUPCnmIcrvqCuM6w==
dependencies:
cliui "^8.0.1"
escalade "^3.1.1"
get-caller-file "^2.0.5"
require-directory "^2.1.1"
string-width "^4.2.3"
y18n "^5.0.5"
yargs-parser "^21.1.1"

yauzl@^2.4.2:
version "2.10.0"
resolved "https://registry.yarnpkg.com/yauzl/-/yauzl-2.10.0.tgz#c7eb17c93e112cb1086fa6d8e51fb0667b79a5f9"
Expand Down

0 comments on commit 429c5ea

Please sign in to comment.