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

Commit

Permalink
Merge pull request #191 from MortalKastor/usb-detect
Browse files Browse the repository at this point in the history
Only poll USB devices list when a change is detected
  • Loading branch information
gre committed Jul 31, 2018
2 parents 3381d3c + a1987cb commit 067be87
Show file tree
Hide file tree
Showing 6 changed files with 112 additions and 28 deletions.
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.21.0",
"node-hid": "^0.7.2"
"lodash": "^4.17.10",
"node-hid": "^0.7.2",
"usb": "^1.3.2"
},
"devDependencies": {
"flow-bin": "^0.68.0",
Expand Down
21 changes: 16 additions & 5 deletions packages/hw-transport-node-hid/src/TransportNodeHid.js
Original file line number Diff line number Diff line change
Expand Up @@ -26,8 +26,9 @@ function defer<T>(): Defer<T> {
return { promise, resolve, reject };
}

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

/**
* node-hid Transport implementation
Expand Down Expand Up @@ -60,14 +61,23 @@ export default class TransportNodeHid extends Transport<string> {
static list = (): Promise<string[]> =>
Promise.resolve(getDevices().map(d => d.path));

static setListenDevicesPollingInterval = (delay: number) => {
listenDevicesPollingInterval = delay;
static setListenDevicesDebounce = (delay: number) => {
listenDevicesDebounce = delay;
};

static setListenDevicesPollingSkip = (conditionToSkip: () => boolean) => {
listenDevicesPollingSkip = conditionToSkip;
};

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

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

const onAdd = device => {
Expand Down
9 changes: 7 additions & 2 deletions packages/hw-transport-node-hid/src/getDevices.js
Original file line number Diff line number Diff line change
@@ -1,6 +1,11 @@
// @flow
import HID from "node-hid";
import isLedgerDevice from "./isLedgerDevice";

const filterInterface = device =>
["win32", "darwin"].includes(process.platform)
? device.usagePage === 0xffa0
: device.interface === 0;

export default function getDevices(): Array<*> {
return HID.devices().filter(isLedgerDevice);
return HID.devices(0x2c97, 0x0).filter(filterInterface);
}
6 changes: 0 additions & 6 deletions packages/hw-transport-node-hid/src/isLedgerDevice.js

This file was deleted.

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,21 @@
// @flow

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

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 +29,77 @@ 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);

const attachDetected = device => {
debug("Device add detected:", device);

debouncedPoll();
};
usb.on("attach", attachDetected);
debug("attach listener added");

const detachDetected = device => {
debug("Device removal detected:", device);

debouncedPoll();
};
usb.on("detach", detachDetected);
debug("detach listener added");

return {
stop: () => {
clearTimeout(timeoutDetection);
debug(
"Stop received, removing listeners and cancelling pending debounced polls"
);
debouncedPoll.cancel();
usb.removeListener("attach", attachDetected);
usb.removeListener("detach", detachDetected);
},
events
};
Expand Down
36 changes: 33 additions & 3 deletions yarn.lock
Original file line number Diff line number Diff line change
Expand Up @@ -6761,7 +6761,7 @@ lodash.uniq@^4.5.0:
version "4.17.5"
resolved "https://registry.yarnpkg.com/lodash/-/lodash-4.17.5.tgz#99a92d65c0272debe8c96b6057bc8fbfa3bed511"

lodash@^4.15.0, lodash@^4.17.4, lodash@^4.2.0:
lodash@^4.15.0, lodash@^4.17.10, lodash@^4.17.4, lodash@^4.2.0:
version "4.17.10"
resolved "https://registry.yarnpkg.com/lodash/-/lodash-4.17.10.tgz#1b7793cf7259ea38fb3661d4d38b3260af8ae4e7"

Expand Down Expand Up @@ -7289,7 +7289,7 @@ mute-stream@0.0.7:
version "0.0.7"
resolved "https://registry.yarnpkg.com/mute-stream/-/mute-stream-0.0.7.tgz#3075ce93bc21b8fab43e1bc4da7e8115ed1e7bab"

nan@^2.0.5, nan@^2.0.7, nan@^2.10.0, nan@^2.2.1, nan@^2.3.0, nan@^2.9.2:
nan@^2.0.5, nan@^2.0.7, nan@^2.10.0, nan@^2.2.1, nan@^2.3.0, nan@^2.8.0, nan@^2.9.2:
version "2.10.0"
resolved "https://registry.yarnpkg.com/nan/-/nan-2.10.0.tgz#96d0cd610ebd58d4b4de9cc0c6828cda99c7548f"

Expand Down Expand Up @@ -7322,6 +7322,14 @@ needle@^2.2.0:
iconv-lite "^0.4.4"
sax "^1.2.4"

needle@^2.2.1:
version "2.2.1"
resolved "https://registry.yarnpkg.com/needle/-/needle-2.2.1.tgz#b5e325bd3aae8c2678902fa296f729455d1d3a7d"
dependencies:
debug "^2.1.2"
iconv-lite "^0.4.4"
sax "^1.2.4"

negotiator@0.6.1:
version "0.6.1"
resolved "https://registry.yarnpkg.com/negotiator/-/negotiator-0.6.1.tgz#2b327184e8992101177b28563fb5e7102acd0ca9"
Expand Down Expand Up @@ -7418,6 +7426,21 @@ node-notifier@^5.0.2:
shellwords "^0.1.1"
which "^1.3.0"

node-pre-gyp@^0.10.0:
version "0.10.3"
resolved "https://registry.yarnpkg.com/node-pre-gyp/-/node-pre-gyp-0.10.3.tgz#3070040716afdc778747b61b6887bf78880b80fc"
dependencies:
detect-libc "^1.0.2"
mkdirp "^0.5.1"
needle "^2.2.1"
nopt "^4.0.1"
npm-packlist "^1.1.6"
npmlog "^4.0.2"
rc "^1.2.7"
rimraf "^2.6.1"
semver "^5.3.0"
tar "^4"

node-pre-gyp@^0.6.39:
version "0.6.39"
resolved "https://registry.yarnpkg.com/node-pre-gyp/-/node-pre-gyp-0.6.39.tgz#c00e96860b23c0e1420ac7befc5044e1d78d8649"
Expand Down Expand Up @@ -8752,7 +8775,7 @@ rc@^1.0.1, rc@^1.1.7:
minimist "^1.2.0"
strip-json-comments "~2.0.1"

rc@^1.1.6:
rc@^1.1.6, rc@^1.2.7:
version "1.2.8"
resolved "https://registry.yarnpkg.com/rc/-/rc-1.2.8.tgz#cd924bf5200a075b83c188cd6b9e211b7fc0d3ed"
dependencies:
Expand Down Expand Up @@ -10929,6 +10952,13 @@ url@^0.11.0, url@~0.11.0:
punycode "1.3.2"
querystring "0.2.0"

usb@^1.3.2:
version "1.3.2"
resolved "https://registry.yarnpkg.com/usb/-/usb-1.3.2.tgz#4563a32323856e26c97dae374b34c66c3d83b5f4"
dependencies:
nan "^2.8.0"
node-pre-gyp "^0.10.0"

use@^3.1.0:
version "3.1.0"
resolved "https://registry.yarnpkg.com/use/-/use-3.1.0.tgz#14716bf03fdfefd03040aef58d8b4b85f3a7c544"
Expand Down

0 comments on commit 067be87

Please sign in to comment.