Skip to content
This repository has been archived by the owner on Jun 27, 2022. It is now read-only.

Only poll USB devices list when a change is detected #191

Merged
merged 17 commits into from
Jul 31, 2018
Merged
Show file tree
Hide file tree
Changes from 9 commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 3 additions & 1 deletion packages/hw-transport-node-hid/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,9 @@
"license": "Apache-2.0",
"dependencies": {
"@ledgerhq/hw-transport": "^4.19.0",
"node-hid": "^0.7.2"
"lodash.debounce": "^4.0.8",
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

it's a lil detail, but I would prefer we depend on lodash and doing import debounce from "lodash/debounce" , like on live

"node-hid": "^0.7.2",
"usb-detection": "^3.1.0"
},
"devDependencies": {
"flow-bin": "^0.68.0",
Expand Down
13 changes: 12 additions & 1 deletion packages/hw-transport-node-hid/src/TransportNodeHid.js
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@ function defer<T>(): Defer<T> {

let listenDevicesPollingInterval = 500;
let listenDevicesPollingSkip = () => false;
let listenDevicesDebug = () => {};

/**
* node-hid Transport implementation
Expand Down Expand Up @@ -68,6 +69,15 @@ export default class TransportNodeHid extends Transport<string> {
listenDevicesPollingSkip = conditionToSkip;
};

static setListenDevicesDebug = (debug: boolean | ((log: string) => void)) => {
listenDevicesDebug =
typeof debug === "function"
? debug
: debug
? (...log) => console.log("[listenDevices]", ...log)
: () => {};
};

/**
*/
static listen = (
Expand All @@ -85,7 +95,8 @@ export default class TransportNodeHid extends Transport<string> {
});
const { events, stop } = listenDevices(
listenDevicesPollingInterval,
listenDevicesPollingSkip
listenDevicesPollingSkip,
listenDevicesDebug
);

const onAdd = device => {
Expand Down
64 changes: 53 additions & 11 deletions packages/hw-transport-node-hid/src/listenDevices.js
Original file line number Diff line number Diff line change
@@ -1,19 +1,23 @@
// @flow

import EventEmitter from "events";
import usbDetect from "usb-detection";
import debounce from "lodash.debounce";
import getDevices from "./getDevices";

const VENDOR_ID = 11415; // Ledger's Vendor ID for filtering

export default (
delay: number,
listenDevicesPollingSkip: () => boolean
listenDevicesPollingSkip: () => boolean,
debug: (...any) => void
): {
events: EventEmitter,
stop: () => void
} => {
const events = new EventEmitter();
events.setMaxListeners(0);

let timeoutDetection;
let listDevices = getDevices();

const flatDevice = d => d.path;
Expand All @@ -27,37 +31,75 @@ export default (

let lastDevices = getFlatDevices();

const checkDevices = () => {
const poll = () => {
if (!listenDevicesPollingSkip()) {
const currentDevices = getFlatDevices();
debug("Polling for added or removed devices");

let changeFound = false;
const currentDevices = getFlatDevices();
const newDevices = currentDevices.filter(d => !lastDevices.includes(d));
const removeDevices = lastDevices.filter(
d => !currentDevices.includes(d)
);

if (newDevices.length > 0) {
debug("New device found:", newDevices);

listDevices = getDevices();
events.emit("add", getDeviceByPaths(newDevices));

changeFound = true;
} else {
debug("No new device found");
}

const removeDevices = lastDevices.filter(
d => !currentDevices.includes(d)
);

if (removeDevices.length > 0) {
debug("Removed device found:", removeDevices);

events.emit("remove", getDeviceByPaths(removeDevices));
listDevices = listDevices.filter(
d => !removeDevices.includes(flatDevice(d))
);

changeFound = true;
} else {
debug("No removed device found");
}

lastDevices = currentDevices;
if (changeFound) {
lastDevices = currentDevices;
}
} else {
debug("Polling skipped, re-debouncing");
debouncedPoll();
}
setTimeout(checkDevices, delay);
};

timeoutDetection = setTimeout(checkDevices, delay);
const debouncedPoll = debounce(poll, delay);

debug("Starting to monitor USB for ledger devices");
usbDetect.startMonitoring();

// Detect add
usbDetect.on(`add:${VENDOR_ID}`, device => {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is a pre-filter, taking advantage of the vendor ID filtering offered by usb-detection, our isLedgerDevice is used by getDevices afterwards

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

👌

debug("Device add detected:", device.deviceName);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

and this device don't have the path ? 😢

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🆗


debouncedPoll();
});

// Detect remove
usbDetect.on(`remove:${VENDOR_ID}`, device => {
debug("Device removal detected:", device.deviceName);

debouncedPoll();
});

return {
stop: () => {
clearTimeout(timeoutDetection);
debug("Stopping USB monitoring");
debouncedPoll.cancel();
usbDetect.stopMonitoring();
},
events
};
Expand Down