Skip to content

Commit 63b951c

Browse files
committed
Updates
1 parent d441af6 commit 63b951c

4 files changed

Lines changed: 180 additions & 107 deletions

File tree

README.md

Lines changed: 128 additions & 42 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,9 @@ Node.JS library for communicating with USB devices.
88

99
This is a complete rewrite in rust using [@kevinmehall](https://github.com/kevinmehall)'s excellent [nusb library](https://docs.rs/nusb/latest/nusb) and [napi-rs](https://napi.rs/).
1010

11+
# License
12+
[MIT](LICENSE.md)
13+
1114
# Prerequisites
1215

1316
[Node.js >= v12.22.0](https://nodejs.org), which includes `npm`.
@@ -26,7 +29,22 @@ You may need to modify your udev and permission rules in order to access your de
2629
SUBSYSTEM=="usb", ATTR{idVendor}=="USB-VENDOR-ID", ATTR{idProduct}=="USB-PRODUCT-ID", MODE="0660", GROUP="GROUP-YOUR-USER-IS-IN"
2730
```
2831

29-
# Installation
32+
# Getting Started
33+
34+
## Supported Architectures and Operating Systems
35+
36+
- i686-pc-windows-msvc
37+
- x86_64-apple-darwin
38+
- x86_64-pc-windows-msvc
39+
- x86_64-unknown-linux-gnu
40+
- x86_64-unknown-linux-musl
41+
- aarch64-apple-darwin
42+
- aarch64-pc-windows-msvc
43+
- aarch64-unknown-linux-gnu
44+
- aarch64-unknown-linux-musl
45+
- armv7-unknown-linux-gnueabihf
46+
47+
## Installation
3048

3149
Native modules are bundled as separate optional packages, so installation should be as simple as installing the package.
3250

@@ -42,27 +60,85 @@ With `yarn`:
4260
yarn add usb
4361
```
4462

45-
# License
46-
[MIT](LICENSE.md)
63+
## Examples
64+
Use the following examples to kickstart your development. Once you have a desired device, use the APIs below to interact with it.
4765

48-
# Limitations
49-
Does not support:
66+
### List all devices
67+
```typescript
68+
import { usb } from 'usb';
5069

51-
- Isochronous transfers
70+
const devices = await usb.getDevices();
5271

53-
# Getting Started
54-
Use the following examples to kickstart your development. Once you have a desired device, use the APIs below to interact with it.
72+
for (const device of devices) {
73+
console.log(device); // WebUSB device
74+
}
75+
```
5576

56-
## APIs
57-
Since `v3.0.0`, the `node-usb` library supports the WebUSB API which follows the [WebUSB Specification](https://wicg.github.io/webusb/)
77+
### Find device by vid/pid
78+
```typescript
79+
import { usb } from 'usb';
5880

59-
Convenience methods also exist to easily list or find devices.
81+
const device = await usb.findDeviceByIds(0x59e3, 0x0a23);
6082

61-
Full auto-generated API documentation can be seen here:
83+
if (device) {
84+
console.log(device); // WebUSB device
85+
}
86+
```
6287

63-
https://node-usb.github.io/node-usb-rs/
88+
### Find device by SerialNumber
89+
```typescript
90+
import { usb } from 'usb';
91+
92+
const device = await usb.findDeviceBySerial('TEST_DEVICE');
93+
94+
if (device) {
95+
console.log(device); // WebUSB device
96+
}
97+
```
98+
99+
### Watch for connect/disconnect events
100+
```typescript
101+
import { usb } from 'usb';
102+
103+
usb.addEventListener('connect', (event) => {
104+
console.log('Device connected:', event.device.serialNumber);
105+
});
106+
107+
usb.addEventListener('disconnect', (event) => {
108+
console.log('Device disconnected:', event.device.serialNumber);
109+
});
110+
```
111+
112+
### Use WebUSB approach to find a device
113+
```typescript
114+
import { webusb } from 'usb';
115+
116+
// Returns first matching device
117+
const device = await webusb.requestDevice({
118+
filters: [{}]
119+
})
64120

65-
## Electron
121+
console.log(device); // WebUSB device
122+
```
123+
124+
### Use WebUSB approach to find a device with custom selection method
125+
```typescript
126+
import { WebUSB } from 'usb';
127+
128+
const customWebUSB = new WebUSB({
129+
// This function can return a promise which allows a UI to be displayed if required
130+
devicesFound: devices => devices.find(device => device.serialNumber === 'TEST_DEVICE')
131+
});
132+
133+
// Returns device based on injected 'devicesFound' function
134+
const device = await customWebUSB.requestDevice({
135+
filters: [{}]
136+
})
137+
138+
console.log(device); // WebUSB device
139+
```
140+
141+
### Electron
66142
Please refer to the maintained example for using `node-usb` in electron:
67143

68144
https://github.com/node-usb/node-usb-example-electron
@@ -73,46 +149,39 @@ If using a packaging system for electron, ensure the `node-usb` library does not
73149
- nodeGypRebuild: false
74150
- npmRebuild: false
75151

76-
## Convenience Functions
152+
# APIs
153+
Since `v3.0.0`, the `node-usb` API follows the WebUSB specification which can be found here:
77154

78-
### getDeviceList()
79-
Return a list of `USB` objects for the USB devices attached to the system.
80-
81-
### findByIds(vid, pid)
82-
Convenience method to get the first device with the specified VID and PID, or `undefined` if no such device is present.
155+
https://wicg.github.io/webusb/
83156

84-
### findBySerialNumber(serialNumber)
85-
Convenience method to get the device with the specified serial number, or `undefined` if no such device is present.
157+
Two versions of the WebUSB API exist by default:
86158

87-
## WebUSB
159+
- `usb` - which exposes all functionality in an unrestricted manner (e.g. without needing to `requestDevice()` first)
160+
- `webusb` - which follows the WebUSB specification exactly and requires the user to authorise devices via `requestDevice()` first.
88161

89-
Please refer to the WebUSB specification which be found here:
162+
You may also construct your own WebUSB (e.g. to specify a `requestDevice()` callback) using the exported `WebUSB` class.
90163

91-
https://wicg.github.io/webusb/
164+
Full auto-generated API documentation can be seen here:
92165

93-
### Implementation Status
166+
https://node-usb.github.io/node-usb-rs/
94167

95-
#### Architectures and Operating Systems
168+
## Implementation Status
96169

97-
- i686-pc-windows-msvc
98-
- x86_64-apple-darwin
99-
- x86_64-pc-windows-msvc
100-
- x86_64-unknown-linux-gnu
101-
- x86_64-unknown-linux-musl
102-
- aarch64-apple-darwin
103-
- aarch64-pc-windows-msvc
104-
- aarch64-unknown-linux-gnu
105-
- aarch64-unknown-linux-musl
106-
- armv7-unknown-linux-gnueabihf
170+
### USB
107171

108-
#### USB
172+
#### WebUSB Features
109173

110174
- [x] getDevices()
111175
- [x] requestDevice()
112176

113-
#### USBDevice
177+
#### Extended Features
178+
179+
- [x] findDeviceByIds()
180+
- [x] findDeviceBySerial()
181+
182+
### USBDevice
114183

115-
##### WebUSB Features
184+
#### WebUSB Features
116185

117186
- [x] usbVersionMajor
118187
- [x] usbVersionMinor
@@ -147,7 +216,7 @@ https://wicg.github.io/webusb/
147216
- [ ] isochronousTransferIn()
148217
- [ ] isochronousTransferOut()
149218

150-
##### Extended Features
219+
#### Extended Features
151220

152221
- [x] bus
153222
- [x] address
@@ -156,11 +225,28 @@ https://wicg.github.io/webusb/
156225
- [x] detachKernelDriver() (Linux only)
157226
- [x] attachKernelDriver() (Linux only)
158227

159-
#### Events
228+
### Events
160229

161230
- [x] connect
162231
- [x] disconnect
163232

233+
## Extended Functions
234+
235+
This library extends the WebUSB specification to add further functionality and convenience
236+
237+
### findDeviceByIds(vid, pid)
238+
Convenience method to get the first device with the specified VID and PID, or `undefined` if no such device is present.
239+
240+
### findDeviceBySerial(serialNumber)
241+
Convenience method to get the device with the specified serial number, or `undefined` if no such device is present.
242+
243+
### detachKernelDriver(interfaceNumber) (Linux only)
244+
Detaches the kernel driver from the interface.
245+
You may need to execute this with elevated privileges.
246+
247+
### attachKernelDriver(interfaceNumber) (Linux only)
248+
Re-attaches the kernel driver for the interface.
249+
164250
# Development
165251
The library is based on native rust bindings wrapping the [nusb](https://docs.rs/nusb/latest/nusb) crate.
166252

src/lib.rs

Lines changed: 15 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -3,10 +3,11 @@
33
mod webusb_device;
44

55
use futures_lite::StreamExt;
6+
use napi::bindgen_prelude::*;
67
use napi::threadsafe_function::{ThreadsafeFunction, ThreadsafeFunctionCallMode};
78
use napi_derive::napi;
8-
use nusb::{hotplug::HotplugEvent, MaybeFuture};
9-
use webusb_device::{UsbDevice};
9+
use nusb::hotplug::HotplugEvent;
10+
use webusb_device::UsbDevice;
1011

1112
#[napi]
1213
pub struct Emitter {
@@ -66,29 +67,20 @@ impl Emitter {
6667
}
6768
}
6869

69-
#[napi(js_name = "nativeGetDeviceList")]
70-
pub async fn getDeviceList() -> Vec<UsbDevice> {
71-
nusb::list_devices().wait().unwrap().map(|dev| UsbDevice::new(dev)).collect()
70+
#[napi(js_name = "nativeGetDevices")]
71+
pub async fn getDevices() -> Result<Vec<UsbDevice>> {
72+
let devices = nusb::list_devices().await.map_err(|e| napi::Error::from_reason(format!("getDevices error: {e}")))?;
73+
Ok(devices.map(UsbDevice::new).collect())
7274
}
7375

74-
#[napi(js_name = "nativeFindByIds")]
75-
pub async fn findByIds(vendorId: u16, productId: u16) -> UsbDevice {
76-
let device = nusb::list_devices()
77-
.wait()
78-
.unwrap()
79-
.find(|dev| dev.vendor_id() == vendorId && dev.product_id() == productId)
80-
.expect("device not found");
81-
82-
UsbDevice::new(device)
76+
#[napi(js_name = "nativeFindDeviceByIds")]
77+
pub async fn findDeviceByIds(vendorId: u16, productId: u16) -> Result<Option<UsbDevice>> {
78+
let mut devices = nusb::list_devices().await.map_err(|e| napi::Error::from_reason(format!("findDeviceByIds error: {e}")))?;
79+
Ok(devices.find(|dev| dev.vendor_id() == vendorId && dev.product_id() == productId).map(UsbDevice::new))
8380
}
8481

85-
#[napi(js_name = "nativeFindBySerialNumber")]
86-
pub async fn findBySerialNumber(serialNumber: String) -> UsbDevice {
87-
let device = nusb::list_devices()
88-
.wait()
89-
.unwrap()
90-
.find(|dev| dev.serial_number() == Some(serialNumber.as_str()))
91-
.expect("device not found");
92-
93-
UsbDevice::new(device)
82+
#[napi(js_name = "nativeFindDeviceBySerial")]
83+
pub async fn findDeviceBySerial(serialNumber: String) -> Result<Option<UsbDevice>> {
84+
let mut devices = nusb::list_devices().await.map_err(|e| napi::Error::from_reason(format!("findDeviceBySerial error: {e}")))?;
85+
Ok(devices.find(|dev| dev.serial_number() == Some(serialNumber.as_str())).map(UsbDevice::new))
9486
}

test/webusb.coffee

Lines changed: 8 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1,26 +1,24 @@
11
assert = require('assert')
22
util = require('util')
3+
usb = require('../dist').usb
34
webusb = require('../dist').webusb
45
WebUSB = require('../dist').WebUSB
5-
getDeviceList = require('../dist').getDeviceList
6-
findByIds = require('../dist').findByIds
7-
findBySerialNumber = require('../dist').findBySerialNumber
86

97
if typeof gc is 'function'
108
# running with --expose-gc, do a sweep between tests so valgrind blames the right one
119
afterEach -> gc()
1210

13-
describe 'helpers', ->
14-
it 'should getDeviceList', ->
15-
devs = await getDeviceList()
11+
describe 'usb Module', ->
12+
it 'should getDevices', ->
13+
devs = await usb.getDevices()
1614
assert.ok(devs.length > 0, "Demo device is not attached")
1715

18-
it 'should findByIds', ->
19-
dev = await findByIds(0x59e3, 0x0a23)
16+
it 'should findDeviceByIds', ->
17+
dev = await usb.findDeviceByIds(0x59e3, 0x0a23)
2018
assert.ok(dev, "Demo device is not attached")
2119

22-
it 'should findBySerialNumber', ->
23-
dev = await findBySerialNumber('TEST_DEVICE')
20+
it 'should findDeviceBySerial', ->
21+
dev = await usb.findDeviceBySerial('TEST_DEVICE')
2422
assert.ok(dev, "Demo device is not attached")
2523

2624
describe 'WebUSB Module', ->

0 commit comments

Comments
 (0)