Research
Security News
Malicious npm Packages Inject SSH Backdoors via Typosquatted Libraries
Socket’s threat research team has detected six malicious npm packages typosquatting popular libraries to insert SSH backdoors.
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.
We strive to make node-hid
cross-platform so there's a good chance any
combination not listed here will compile and work.
¹ prebuilt-binaries not provided for these platforms
² prebuilt binary built on Debian 10 Buster
Future versions of Node or Electron should work with no extra work, since node-hid
is now based on NAPI.
For most "standard" use cases (macOS, Windows, Linux x86), node-hid
will install like a standard npm package:
npm install node-hid
If you install globally, the test program src/show-devices.js
is installed as hid-showdevices
. On Linux you can use it to try the difference between hidraw
and libusb
driverTypes:
$ npm install -g node-hid
$ hid-showdevices libusb
$ hid-showdevices hidraw
We are using prebuild to compile and post binaries of the library for most common use cases (Linux, MacOS, Windows on standard processor platforms). If a prebuild is not available, node-hid
will work, but npm install node-hid
will compile the binary when you install. For more details on compiler setup, see Compling from source below.
In the src/
directory, various JavaScript programs can be found
that talk to specific devices in some way. Some interesting ones:
show-devices.js
- display all HID devices in the systemtest-ps3-rumbleled.js
- Read PS3 joystick and control its LED & rumblerstest-powermate.js
- Read Griffin PowerMate knob and change its LEDtest-blink1.js
- Fade colors on blink(1) RGB LEDtest-bigredbutton.js
- Read Dreamcheeky Big Red Buttontest-teensyrawhid.js
- Read/write Teensy running RawHID "Basic" Arduino sketchTo try them out, run them with node src/showdevices.js
from within the node-hid directory.
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.
HID.devices()
, HID.devicesAsync()
, new HID.HID()
and HIDAsync.open()
for detecting device plug/unplugAll 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. node-usb
uses OS-specific, non-bus enumeration ways to detect device plug / unplug.
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:
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>
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()
:
var device = await HID.HIDAsync.open(path);
or open the first device matching a VID/PID pair:
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.
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:
device.on("data", function(data) {});
You can also listen for errors like this:
device.on("error", function(err) {});
For FEATURE reports:
var buf = await device.getFeatureReport(reportId, reportLength)
Notes:
device.on("data")
are asynchronousdevice.close()
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.
device.write([0x00, 0x01, 0x01, 0x05, 0xff, 0xff]);
device.sendFeatureReport( [0x01, 'c', 0, 0xff,0x33,0x00, 70,0, 0] );
Notes:
Promise
resolving once the operation is completeddevice.write()
and device.sendFeatureReport()
return a Promise containing the number of bytes written + 1.write()
or sendFeatureReport()
must be the Report Id.var HID = require('node-hid');
var devices = HID.devices();
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:
HID.devices();
{ 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>
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.devices()
:
var device = new HID.HID(path);
or open the first device matching a VID/PID pair:
var device = new HID.HID(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.
If you need to filter down the HID.devices()
list, you can use
standard Javascript array techniques:
var devices = HID.devices();
var deviceInfo = devices.find( function(d) {
var isTeensy = d.vendorId===0x16C0 && d.productId===0x0486;
return isTeensy && d.usagePage===0xFFAB && d.usage===0x200;
});
if( deviceInfo ) {
var device = new HID.HID( deviceInfo.path );
// ... use device
}
You can also find device of interest by passing VID and PID
//return all the devices that match specified VID and PID
var devices = HID.devices(0x16C0,0x0486);
To receive FEATURE reports, use 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:
device.on("data", function(data) {});
You can also listen for errors like this:
device.on("error", function(err) {});
For FEATURE reports:
var buf = device.getFeatureReport(reportId, reportLength)
Notes:
device.on("data")
are asynchronousdevice.getFeatureReport()
are synchronousdevice.close()
To send FEATURE reports, use device.sendFeatureReport()
.
To send OUTPUT reports, use device.write()
.
All writing is synchronous.
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.
device.write([0x00, 0x01, 0x01, 0x05, 0xff, 0xff]);
device.sendFeatureReport( [0x01, 'c', 0, 0xff,0x33,0x00, 70,0, 0] );
Notes:
device.write()
and device.sendFeatureReport()
return
number of bytes written + 1.write()
or
sendFeatureReport()
must be the Report Id.devices = await HID.devicesAsync()
devices = await HID.devicesAsync(vid,pid)
device = await HID.HIDAsync.open(path)
device = await HID.HIDAsync.open(vid,pid)
device.on('data', function(data) {} )
data
- Buffer - the data read from the devicedevice.on('error, function(error) {} )
error
- The error Object emitteddevice.write(data)
data
- the data to be synchronously written to the device,
first byte is Report Id or 0x00 if not using numbered reports.device.close()
device.pause()
data
events.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)
time_out
- timeout in millisecondsdevice.on('data', () => {})
is not being used. It will fail if a data handler is registereddevice.sendFeatureReport(data)
data
- data of HID feature report, with 0th byte being report_id ([report_id,...]
)device.getFeatureReport(report_id, report_length)
report_id
- HID feature report id to getreport_length
- length of reportdevice.setNonBlocking(no_block)
no_block
- boolean. Set to true
to enable non-blocking readshid_set_nonblocking()
in hidapi
devices = HID.devices()
devices = HID.devices(vid,pid)
HID.setDriverType(type)
type
can be "hidraw"
or "libusb"
, defaults to "hidraw"
device = new HID.HID(path)
device = new HID.HID(vid,pid)
device.on('data', function(data) {} )
data
- Buffer - the data read from the devicedevice.on('error', function(error) {} )
error
- The error Object emitteddevice.write(data)
data
- the data to be synchronously written to the device,
first byte is Report Id or 0x00 if not using numbered reports.device.close()
device.pause()
data
events.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(callback)
callback
is of the form callback(err, data)
device.readSync()
device.readTimeout(time_out)
time_out
- timeout in millisecondsdevice.sendFeatureReport(data)
data
- data of HID feature report, with 0th byte being report_id ([report_id,...]
)device.getFeatureReport(report_id, report_length)
report_id
- HID feature report id to getreport_length
- length of reportdevice.setNonBlocking(no_block)
no_block
- boolean. Set to true
to enable non-blocking readshid_set_nonblocking()
in hidapi
In general node-hid
is not thread-safe because the underlying C-library it wraps (hidapi
) is not thread-safe.
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.
node-hid
cannot readThe following devices are unavailable to node-hid
because the OS owns them:
Most OSes will prevent USB HID keyboards or mice, or devices that appear as a keyboard to the OS. This includes many RFID scanners, barcode readers, USB HID scales, and many other devices. This is a security precaution. Otherwise, it would be trivial to build keyloggers.
Some keyboard-pretending devices like barcode or RFID readers can be configured to be in
"HID data" mode or "Serial / UART" mode. If in "HID Data" mode then node-hid
can access them,
if in "Serial / UART" mode, you should use node-serialport
instead.
See General notes above Keyboards
See General notes above about Keyboards
For reasons similar to mice & keyboards it appears you can't access this controller on Windows 10.
See General notes above about Keyboards
Most Linux distros use udev
to manage access to physical devices,
and USB HID devices are normally owned by the root
user.
To allow non-root access, you must create a udev rule for the device,
based on the devices vendorId and productId.
This rule is a text file placed in /etc/udev/rules.d
.
For an example HID device (say a blink(1) light with vendorId = 0x27b8 and productId = 0x01ed,
the rules file to support both hidraw
and libusb
would look like:
SUBSYSTEM=="input", GROUP="input", MODE="0666"
SUBSYSTEM=="usb", ATTRS{idVendor}=="27b8", ATTRS{idProduct}=="01ed", MODE:="666", GROUP="plugdev"
KERNEL=="hidraw*", ATTRS{idVendor}=="27b8", ATTRS{idProduct}=="01ed", MODE="0666", GROUP="plugdev"
Note that the values for idVendor and idProduct must be in hex and lower-case.
Save this file as /etc/udev/rules.d/51-blink1.rules
, unplug the HID device,
and reload the rules with:
sudo udevadm control --reload-rules
For a complete example, see the blink1 udev rules.
By default as of node-hid@0.7.0
, the hidraw driver is used to talk to HID devices. Before node-hid@0.7.0
, the more older but less capable libusb driver was used. With hidraw
Linux apps can now see usage
and usagePage
attributes of devices.
If you would still like to use the libusb
driver, then you can do either:
During runtime, you can use HID.setDriverType('libusb')
immediately after require()-ing node-hid
:
var HID = require('node-hid');
HID.setDriverType('libusb');
If you must have the libusb version and cannot use setDriverType()
,
you can install older node-hid or build from source:
npm install node-hid@0.5.7
or:
npm install node-hid --build-from-source --driver=libusb
To compile & develop locally or if prebuild
cannot download a pre-built
binary for you, you will need the following compiler tools and libraries:
apt install build-essential git pkg-config
apt install libudev-dev
(Debian/Ubuntu) /
yum install libusbx-devel
(Fedora)apt install libusb-1.0-0 libusb-1.0-0-dev
pkg install git gcc gmake libiconv node npm
The below is slightly stale. The 2021 solution is to use the official NodeJs Windows installer and pick "install native module tools"
npm install --global windows-build-tools
%USERPROFILE%\.windows-build-tools\python27
to PATH
,
like PowerShell: $env:Path += ";$env:USERPROFILE\.windows-build-tools\python27"
node-hid
from source, for your projectsnpm install node-hid --build-from-source
node-hid
for node-hid
developmentFor example:
git clone https://github.com/node-hid/node-hid.git
cd node-hid # must change into node-hid directory
npm install -g rimraf # just so it doesn't get 'clean'ed
npm run prepublishOnly # get the needed hidapi submodule
npm install --build-from-source # rebuilds the module with C code
npm run showdevices # list connected HID devices
node ./src/show-devices.js # same as above
You may see some warnings from the C compiler as it compiles
hidapi (the underlying C library node-hid
uses).
This is expected.
For ease of development, there are also the scripts:
npm run gypclean # "node-gyp clean" clean gyp build directory
npm run gypconfigure # "node-gyp configure" configure makefiles
npm run gypbuild # "node-gyp build" build native code
node-hid
for cross-compilingWhen cross-compiling you need to override node-hid
's normal behavior
of using Linux pkg-config
to determine CLFAGS and LDFLAGS for libusb
.
To do this, you can use the node-gyp
variable node_hid_no_pkg_config
and then invoke a node-hid
rebuild with either:
node-gyp rebuild --node_hid_no_pkg_config=1
or
npm gyprebuild --node_hid_no_pkg_config=1
node-hid
If using node-hid
with webpack
or similar bundler, you may need to exclude
node-hid
and other libraries with native code. In webpack, you say which
externals
you have in your webpack-config.js
:
externals: {
"node-hid": 'commonjs node-hid'
}
Examples of node-hid
in Electron:
node-hid
, should track latest Electron releasenode-hid
using electron-react-boilerplatenode-hid
, showing packaging and signingnode-hid
Without knowing much about NW.js, a quick hacky solution that works is:
cd my-nwjs-app
npm install node-hid --save
npm install -g nw-gyp
cd node_modules/node-hid
nw-gyp rebuild --target=0.42.3 --arch=x64 // or whatever NW.js version you have
cd ../..
nwjs .
Please use the node-hid github issues page for support questions and issues.
FAQs
USB HID device access library
We found that node-hid demonstrated a healthy version release cadence and project activity because the last version was released less than a year ago. It has 0 open source maintainers collaborating on the project.
Did you know?
Socket for GitHub automatically highlights issues in each pull request and monitors the health of all your open source dependencies. Discover the contents of your packages and block harmful activity before you install or update your dependencies.
Research
Security News
Socket’s threat research team has detected six malicious npm packages typosquatting popular libraries to insert SSH backdoors.
Security News
MITRE's 2024 CWE Top 25 highlights critical software vulnerabilities like XSS, SQL Injection, and CSRF, reflecting shifts due to a refined ranking methodology.
Security News
In this segment of the Risky Business podcast, Feross Aboukhadijeh and Patrick Gray discuss the challenges of tracking malware discovered in open source softare.