Socket
Socket
Sign inDemoInstall

node-hid

Package Overview
Dependencies
2
Maintainers
5
Versions
43
Alerts
File Explorer

Advanced tools

Install Socket

Detect and block malicious and high-risk dependencies

Install

Comparing version 2.2.0 to 3.0.0

binding-options.js

106

nodehid.js
var os = require('os')
const EventEmitter = require("events").EventEmitter;
const util = require("util");
var EventEmitter = require("events").EventEmitter,
util = require("util");
var driverType = null;
let driverType = null;
function setDriverType(type) {

@@ -13,16 +11,10 @@ driverType = type;

// lazy load the C++ binding
var binding = null;
let binding = null;
function loadBinding() {
if( !binding ) {
if( os.platform() === 'linux' ) {
// Linux defaults to hidraw
if( !driverType || driverType === 'hidraw' ) {
binding = require('bindings')('HID_hidraw.node');
} else {
binding = require('bindings')('HID.node');
}
if (!binding) {
const options = require('./binding-options');
if (process.platform === "linux" && (!driverType || driverType === "hidraw")) {
options.name = 'HID_hidraw';
}
else {
binding = require('bindings')('HID.node');
}
binding = require("pkg-prebuilds/bindings")(__dirname, options);
}

@@ -49,5 +41,3 @@ }

following won't work since `new` keyword isn't used:
`binding.HID.apply(this, arguments);`
So... we do this craziness instead...

@@ -71,3 +61,2 @@ */

/* We are now done inheriting from `binding.HID` and EventEmitter.
Now upon adding a new listener for "data" events, we start

@@ -136,2 +125,71 @@ polling the HID device using `read(...)`

class HIDAsync extends EventEmitter {
constructor(raw) {
super()
if (!(raw instanceof binding.HIDAsync)) {
throw new Error(`HIDAsync cannot be constructed directly. Use HIDAsync.open() instead`)
}
this._raw = raw
/* Now we have `this._raw` Object from which we need to
inherit. So, one solution is to simply copy all
prototype methods over to `this` and binding them to
`this._raw`.
We explicitly wrap them in an async method, to ensure
that any thrown errors are promise rejections
*/
for (let i in this._raw) {
this[i] = async (...args) => this._raw[i](...args);
}
/* Now upon adding a new listener for "data" events, we start
the read thread executing. See `resume()` for more details.
*/
this.on("newListener", (eventName, listener) =>{
if(eventName == "data")
process.nextTick(this.resume.bind(this) );
});
this.on("removeListener", (eventName, listener) => {
if(eventName == "data" && this.listenerCount("data") == 0)
process.nextTick(this.pause.bind(this) );
})
}
static async open(...args) {
loadBinding();
const native = await binding.openAsyncHIDDevice(...args);
return new HIDAsync(native)
}
async close() {
this._closing = true;
this.removeAllListeners();
await this._raw.close();
this._closed = true;
}
//Pauses the reader, which stops "data" events from being emitted
pause() {
this._raw.readStop();
}
resume() {
if(this.listenerCount("data") > 0)
{
//Start polling & reading loop
this._raw.readStart((err, data) => {
if (err) {
if(!this._closing)
this.emit("error", err);
//else ignore any errors if I'm closing the device
} else {
this.emit("data", data);
}
})
}
}
}
function showdevices() {

@@ -142,5 +200,13 @@ loadBinding();

function showdevicesAsync(...args) {
loadBinding();
return binding.devicesAsync(...args);
}
//Expose API
exports.HID = HID;
exports.HIDAsync = HIDAsync;
exports.devices = showdevices;
exports.devicesAsync = showdevicesAsync;
exports.setDriverType = setDriverType;

38

package.json
{
"name": "node-hid",
"description": "USB HID device access library",
"version": "2.2.0",
"version": "3.0.0",
"author": "Hans Hübner <hans.huebner@gmail.com> (https://github.com/hanshuebner)",

@@ -21,10 +21,5 @@ "bugs": "https://github.com/node-hid/node-hid/issues",

"prepublishOnly": "git submodule update --init",
"install": "prebuild-install --runtime napi || node-gyp rebuild",
"prebuild": "prebuild --runtime napi --all --verbose --include-regex \"HID.*node$\"",
"prebuild-upload": "prebuild --runtime napi --upload-all",
"prebuild-ci": "prebuild-ci",
"gypclean": "node-gyp clean",
"gypconfigure": "node-gyp configure",
"gypbuild": "node-gyp build",
"gyprebuild": "node-gyp rebuild",
"install": "pkg-prebuilds-verify ./binding-options.js || node-gyp rebuild",
"build": "node-gyp build",
"rebuild": "node-gyp clean configure build",
"clean": "rimraf build node_modules prebuilds package-lock.json",

@@ -37,21 +32,32 @@ "distclean": "npm run clean && rimraf hidapi"

"main": "./nodehid.js",
"types": "./nodehid.d.ts",
"binary": {
"napi_versions": [
3
4
]
},
"engines": {
"node": ">=10"
"node": ">=10.16"
},
"license": "(MIT OR X11)",
"dependencies": {
"bindings": "^1.5.0",
"node-addon-api": "^3.0.2",
"prebuild-install": "^7.1.1"
"node-addon-api": "^3.2.1",
"pkg-prebuilds": "^0.2.1"
},
"devDependencies": {
"prebuild": "^12.1.0",
"rimraf": "^2.6.2"
},
"gypfile": true
"gypfile": true,
"files": [
"nodehid.js",
"nodehid.d.ts",
"binding-options.js",
"hidapi",
"prebuilds",
"src/*.cc",
"src/*.h",
"LICENSE*",
"README.md",
"binding.gyp"
]
}
# node-hid - Access USB HID devices from Node.js #
[![npm](https://img.shields.io/npm/dm/node-hid.svg?maxAge=2592000)](http://npmjs.com/package/node-hid)
[![build macos](https://github.com/node-hid/node-hid/workflows/macos/badge.svg)](https://github.com/node-hid/node-hid/actions?query=workflow%3Amacos)
[![build windows](https://github.com/node-hid/node-hid/workflows/windows/badge.svg)](https://github.com/node-hid/node-hid/actions?query=workflow%3Awindows)
[![build linux](https://github.com/node-hid/node-hid/workflows/linux/badge.svg)](https://github.com/node-hid/node-hid/actions?query=workflow%3Alinux)
[![Prebuild](https://github.com/node-hid/node-hid/actions/workflows/build.yml/badge.svg)](https://github.com/node-hid/node-hid/actions/workflows/build.yml)

@@ -17,26 +15,6 @@

* [Examples](#examples)
* [Usage](#usage)
* [List all HID devices connected](#list-all-hid-devices-connected)
* [Cost of HID.devices() and <code>new HID.HID()</code> for detecting device plug/unplug](#cost-of-hiddevices-and-new-hidhid-for-detecting-device-plugunplug)
* [Opening a device](#opening-a-device)
* [Picking a device from the device list](#picking-a-device-from-the-device-list)
* [Reading from a device](#reading-from-a-device)
* [Writing to a device](#writing-to-a-device)
* [Complete API](#complete-api)
* [devices = HID.devices()](#devices--hiddevices)
* [HID.setDriverType(type)](#hidsetdrivertypetype)
* [device = new HID.HID(path)](#device--new-hidhidpath)
* [device = new HID.HID(vid,pid)](#device--new-hidhidvidpid)
* [device.on('data', function(data) {} )](#deviceondata-functiondata--)
* [device.on('error, function(error) {} )](#deviceonerror-functionerror--)
* [device.write(data)](#devicewritedata)
* [device.close()](#deviceclose)
* [device.pause()](#devicepause)
* [device.resume()](#deviceresume)
* [device.read(callback)](#devicereadcallback)
* [device.readSync()](#devicereadsync)
* [device.readTimeout(time_out)](#devicereadtimeouttime_out)
* [device.sendFeatureReport(data)](#devicesendfeaturereportdata)
* [device.getFeatureReport(report_id, report_length)](#devicegetfeaturereportreport_id-report_length)
* [device.setNonBlocking(no_block)](#devicesetnonblockingno_block)
* [Async API Usage](#async-api-usage)
* [Sync API Usage](#sync-api-usage)
* [Complete Async API](#complete-async-api)
* [Complete Sync API](#complete-sync-api)
* [General notes:](#general-notes)

@@ -64,4 +42,4 @@ * [Thread safety, Worker threads, Context-aware modules](#thread-safety-worker-threads-context-aware-modules)

## Platform Support
`node-hid` supports Node.js v6 and upwards. For versions before that,
you will need to build from source. The platforms, architectures and node versions `node-hid` supports are the following.
`node-hid` currently supports Node.js v10 and upwards. For versions before that, you will need to use an older version.
The platforms, architectures and node versions `node-hid` supports are the following.
In general we try to provide pre-built native library binaries for the most common platforms, Node and Electron versions.

@@ -73,3 +51,3 @@

### Supported Platforms ###
- Windows x86 (32-bit) (¹)
- Windows x86 (32-bit)
- Windows x64 (64-bit)

@@ -79,3 +57,4 @@ - Mac OSX 10.9+

- Linux x86 (¹)
- Linux ARM / Raspberry Pi (¹)
- Linux ARM / Raspberry Pi / Various SBC (²)
- Linux ARM64 / Various SBC (²)
- Linux MIPSel (¹)

@@ -85,8 +64,8 @@ - Linux PPC64 (¹)

¹ prebuilt-binaries not provided for these platforms
² prebuilt binary built on Ubuntu 18.04 x64
² prebuilt binary built on Debian 10 Buster
### Supported Node versions ###
* Node v8 to
* Node v16
* Node v10 to
* Node v20

@@ -96,5 +75,5 @@ ### Supported Electron versions ###

* Electron v3 to
* Electron v16
* Electron v24
Future versions of Node or Electron should work, since `node-hid` is now based on NAPI.
Future versions of Node or Electron should work with no extra work, since `node-hid` is now based on NAPI.

@@ -134,5 +113,21 @@ ## Installation

----
## Async vs sync API
## Usage
Since 3.0.0, `node-hid` supports both the old synchronous api, and a newer async api.
It is recommended to use the async api to avoid `node-hid` from blocking your code from executing. For prototyping or tiny applications, this likely will not matter, but for npm libraries or larger applications it can be problematic.
Additionally, the sync api is limited to only beind able to read up to the `UV_THREADPOOL_SIZE` (default is 4) number of devices at once. Reading from multiple could degrade performance of your application, as there will be fewer than expected uv workers available for nodejs and other libraries to use for other tasks.
The async API is identical to the sync API described below, except every method returns a `Promise` that must be handled. Any unhandled promise can crash your application.
It is safe to use the sync api for some devices in an application, and the async api for other devices. The thread safety of `hidapi` is handled for you here to avoid crashes.
## Cost of `HID.devices()`, `HID.devicesAsync()`, `new HID.HID()` and `HIDAsync.open()` for detecting device plug/unplug
All of `HID.devices()`, `HID.devicesAsync()`, `new HID.HID()` and `HIDAsync.open()` are relatively costly, each causing a USB (and potentially Bluetooth) enumeration. This takes time and OS resources. Doing either can slow down the read/write that you do in parallel with a device, and cause other USB devices to slow down too. This is how USB works.
If you are polling `HID.devices()` or `HID.devicesAsync()` or other inefficient methods to detect device plug / unplug, consider instead using [node-usb](https://github.com/node-usb/node-usb#usbdetection). `node-usb` uses OS-specific, non-bus enumeration ways to detect device plug / unplug.
## Async API Usage
### List all HID devices connected

@@ -142,2 +137,122 @@

var HID = require('node-hid');
var devices = await HID.devicesAsync();
```
`devices` will contain an array of objects, one for each HID device
available. Of particular interest are the `vendorId` and
`productId`, as they uniquely identify a device, and the
`path`, which is needed to open a particular device.
Sample output:
```js
await HID.devicesAsync();
{ vendorId: 10168,
productId: 493,
path: 'IOService:/AppleACPIPl...HIDDevice@14210000,0',
serialNumber: '20002E8C',
manufacturer: 'ThingM',
product: 'blink(1) mk2',
release: 2,
interface: -1,
usagePage: 65280,
usage: 1 },
{ vendorId: 1452,
productId: 610,
path: 'IOService:/AppleACPIPl...Keyboard@14400000,0',
serialNumber: '',
manufacturer: 'Apple Inc.',
product: 'Apple Internal Keyboard / Trackpad',
release: 549,
interface: -1,
usagePage: 1,
usage: 6 },
<and more>
```
### Opening a device
Before a device can be read from or written to, it must be opened.
Use either the `path` from the list returned by a prior call to `HID.devicesAsync()`:
```js
var device = await HID.HIDAsync.open(path);
```
or open the first device matching a VID/PID pair:
```js
var device = await HID.HIDAsync.open(vid,pid);
```
The `device` variable will contain a handle to the device.
If an error occurs opening the device, an exception will be thrown.
A `node-hid` device is an `EventEmitter`.
While it shares some method names and usage patterns with
`Readable` and `Writable` streams, it is not a stream and the semantics vary.
For example, `device.write` does not take encoding or callback args and
`device.pause` does not do the same thing as `readable.pause`.
There is also no `pipe` method.
### Reading from a device
To receive FEATURE reports, use `await device.getFeatureReport()`.
To receive INPUT reports, use `device.on("data",...)`.
A `node-hid` device is an EventEmitter.
Reading from a device is performed by registering a "data" event handler:
```js
device.on("data", function(data) {});
```
You can also listen for errors like this:
```js
device.on("error", function(err) {});
```
For FEATURE reports:
```js
var buf = await device.getFeatureReport(reportId, reportLength)
```
Notes:
- Reads via `device.on("data")` are asynchronous
- To remove an event handler, close the device with `device.close()`
- When there is not yet a data handler or no data handler exists,
data is not read at all -- there is no buffer.
### Writing to a device
To send FEATURE reports, use `device.sendFeatureReport()`.
To send OUTPUT reports, use `device.write()`.
The ReportId is the first byte of the array sent to `device.sendFeatureReport()` or `device.write()`, meaning the array should be one byte bigger than your report.
If your device does NOT use numbered reports, set the first byte of the 0x00.
```js
device.write([0x00, 0x01, 0x01, 0x05, 0xff, 0xff]);
```
```js
device.sendFeatureReport( [0x01, 'c', 0, 0xff,0x33,0x00, 70,0, 0] );
```
Notes:
- All writes and other operations performed with the HIDAsync device are done in a work-queue, so will happen in the order you issue them with the returned `Promise` resolving once the operation is completed
- You must send the exact number of bytes for your chosen OUTPUT or FEATURE report.
- Both `device.write()` and `device.sendFeatureReport()` return a Promise containing the number of bytes written + 1.
- For devices using Report Ids, the first byte of the array to `write()` or `sendFeatureReport()` must be the Report Id.
## Sync API Usage
### List all HID devices connected
```js
var HID = require('node-hid');
var devices = HID.devices();

@@ -178,11 +293,6 @@ ```

#### Cost of `HID.devices()` and `new HID.HID()` for detecting device plug/unplug
Both `HID.devices()` and `new HID.HID()` are relatively costly, each causing a USB (and potentially Bluetooth) enumeration. This takes time and OS resources. Doing either can slow down the read/write that you do in parallel with a device, and cause other USB devices to slow down too. This is how USB works.
If you are polling `HID.devices()` or doing repeated `new HID.HID(vid,pid)` to detect device plug / unplug, consider instead using [node-usb-detection](https://github.com/MadLittleMods/node-usb-detection). `node-usb-detection` uses OS-specific, non-bus enumeration ways to detect device plug / unplug.
### Opening a device
Before a device can be read from or written to, it must be opened.
The `path` can be determined by a prior HID.devices() call.
Use either the `path` from the list returned by a prior call to `HID.devices()`:

@@ -287,4 +397,77 @@

## Complete API
## Complete Async API
### `devices = await HID.devicesAsync()`
- Return array listing all connected HID devices
### `devices = await HID.devicesAsync(vid,pid)`
- Return array listing all connected HID devices with specific VendorId and ProductId
### `device = await HID.HIDAsync.open(path)`
- Open a HID device at the specified platform-specific path
### `device = await HID.HIDAsync.open(vid,pid)`
- Open first HID device with specific VendorId and ProductId
### `device.on('data', function(data) {} )`
- `data` - Buffer - the data read from the device
### `device.on('error, function(error) {} )`
- `error` - The error Object emitted
### `device.write(data)`
- `data` - the data to be synchronously written to the device,
first byte is Report Id or 0x00 if not using numbered reports.
- Returns number of bytes actually written
### `device.close()`
- Closes the device. Subsequent reads will raise an error.
### `device.pause()`
- Pauses reading and the emission of `data` events.
This means the underlying device is _silenced_ until resumption --
it is not like pausing a stream, where data continues to accumulate.
### `device.resume()`
- This method will cause the HID device to resume emmitting `data` events.
If no listeners are registered for the `data` event, data will be lost.
- When a `data` event is registered for this HID device, this method will
be automatically called.
### `device.read(time_out)`
- (optional) `time_out` - timeout in milliseconds
- Low-level function call to initiate an asynchronous read from the device.
- Returns a Promise containing a Buffer or the Promise will reject upon error.
- This can only be used when `device.on('data', () => {})` is not being used. It will fail if a data handler is registered
### `device.sendFeatureReport(data)`
- `data` - data of HID feature report, with 0th byte being report_id (`[report_id,...]`)
- Returns number of bytes actually written
### `device.getFeatureReport(report_id, report_length)`
- `report_id` - HID feature report id to get
- `report_length` - length of report
### `device.setNonBlocking(no_block)`
- `no_block` - boolean. Set to `true` to enable non-blocking reads
- exactly mirrors `hid_set_nonblocking()` in [`hidapi`](https://github.com/libusb/hidapi)
## Complete Sync API
### `devices = HID.devices()`

@@ -378,4 +561,3 @@

In general `node-hid` is not thread-safe because the underlying C-library it wraps (`hidapi`) is not thread-safe.
However, `node-hid` is now reporting as minimally Context Aware to allow use in Electron v9+.
Until `node-hid` (or `hidapi`) is rewritten to be thread-safe, please constrain all accesses to it via a single thread.
To mitigate this we are doing locking to ensure operations are performed safely. If you are using the sync api from multiple worker_threads, this will result in them waiting on each other at times.

@@ -544,14 +726,3 @@ ### Devices `node-hid` cannot read

## Electron projects using `node-hid`
In your electron project, add `electron-rebuild` to your `devDependencies`.
Then in your package.json `scripts` add:
```
"postinstall": "electron-rebuild"
```
This will cause `npm` to rebuild `node-hid` for the version of Node that is in Electron.
If you get an error similar to `The module "HID.node" was compiled against a different version of Node.js`
then `electron-rebuild` hasn't been run and Electron is trying to use `node-hid`
compiled for Node.js and not for Electron.
If using `node-hid` with `webpack` or similar bundler, you may need to exclude

@@ -591,2 +762,2 @@ `node-hid` and other libraries with native code. In webpack, you say which

Please use the [node-hid github issues page](https://github.com/node-hid/node-hid/issues)
for support questions and issues.
for support questions and issues.

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

SocketSocket SOC 2 Logo

Product

  • Package Alerts
  • Integrations
  • Docs
  • Pricing
  • FAQ
  • Roadmap

Packages

Stay in touch

Get open source security insights delivered straight into your inbox.


  • Terms
  • Privacy
  • Security

Made with ⚡️ by Socket Inc