working on DS4 controller

This commit is contained in:
danbulant 2020-01-05 18:18:41 +01:00
parent a19487d016
commit 99d943a41a
40 changed files with 3161 additions and 41 deletions

74
controller.js Normal file
View file

@ -0,0 +1,74 @@
var dualShock = require('./dist/dualshock-controller/src/dualshock');
var controller = dualShock({
config: "dualshock4-generic-driver"
});
controller.on('error', err => console.log(err));
function setExtras(obj){
controller.setExtras(obj);
}
var connected = false;
controller.on('connected', () => {
connected = true;
});
controller.on("disconnected", ()=>{
connected = false;
})
function isConnected(){
return connected;
}
//add event handlers:
// controller.on('left:move', data => console.log('left Moved: ' + data.x + ' | ' + data.y));
// controller.on('right:move', data => console.log('right Moved: ' + data.x + ' | ' + data.y));
// controller.on('square:press', () => console.log('square press'));
// controller.on('square:release', () => console.log('square release'));
//sixasis motion events:
//the object returned from each of the movement events is as follows:
//{
// direction : values can be: 1 for right, forward and up. 2 for left, backwards and down.
// value : values will be from 0 to 120 for directions right, forward and up and from 0 to -120 for left, backwards and down.
//}
function on(event, handler){
controller.on(event, handler);
}
module.exports = {
on,
isConnected,
setExtras
}
// controller.on('touchpad:x1:active', () => console.log('touchpad one finger active'));
// controller.on('touchpad:x2:active', () => console.log('touchpad two fingers active'));
// controller.on('touchpad:x2:inactive', () => console.log('touchpad back to single finger'));
// controller.on('touchpad:x1', data => console.log('touchpad x1:', data.x, data.y));
// controller.on('touchpad:x2', data => console.log('touchpad x2:', data.x, data.y));
// //right-left movement
// controller.on('rightLeft:motion', data => console.log(data));
// //forward-back movement
// controller.on('forwardBackward:motion', data => console.log(data));
// //up-down movement
// controller.on('upDown:motion', data => console.log(data));
// //controller status
// //as of version 0.6.2 you can get the battery %, if the controller is connected and if the controller is charging
// controller.on('battery:change', data => console.log(data));
// controller.on('connection:change', data => console.log(data));
// controller.on('charging:change', data => console.log(data));

View file

@ -0,0 +1,9 @@
root = true
[*]
indent_style = space
indent_size = 4
charset = utf-8
trim_trailing_whitespace = true
end_of_line = lf
insert_final_newline = true

6
dist/dualshock-controller/.gitignore vendored Normal file
View file

@ -0,0 +1,6 @@
node_modules
*.log
coverage
*.iml
*.idea
.DS_Store

6
dist/dualshock-controller/.travis.yml vendored Normal file
View file

@ -0,0 +1,6 @@
language: node_js
node_js:
- "6.9.2"
before_script:
install: echo "node-hid installation will fail in travis. Overiding `npm install`"
compiler: clang

194
dist/dualshock-controller/README.md vendored Normal file
View file

@ -0,0 +1,194 @@
node-dualshock-controller
=========================
[![Build Status](https://travis-ci.org/rdepena/node-dualshock-controller.png?branch=master)](https://travis-ci.org/rdepena/node-dualshock-controller) [![Code Climate](https://codeclimate.com/github/rdepena/node-dualshock-controller.png)](https://codeclimate.com/github/rdepena/node-dualshock-controller)
`dualshock-controller` Eventing API layer over HID for the Sony DualShock 3 and DualShock 4 controllers
## Installation:
#### OSX/Windows:
```bash
npm install dualshock-controller
```
#### Linux:
Review the [Linux support](#linux-support) section.
## Using the DualShock library
`Important: THE CONTROLLER WILL NOT SEND ANY DATA IF YOU DO NOT PRESS THE PS BUTTON.`
Also, to use the touchpad, rumble and LED capabilities of the controller you
must connect the controller to your computer using a micro-USB cable.
~~~~ javascript
var dualShock = require('dualshock-controller');
//pass options to init the controller.
var controller = dualShock(
{
//you can use a ds4 by uncommenting this line.
//config: "dualshock4-generic-driver",
//if the above configuration doesn't work for you,
//try uncommenting the following line instead.
//config: "dualshock4-alternate-driver"
//if using ds4 comment this line.
config: "dualShock3",
//smooths the output from the acelerometers (moving averages) defaults to true
accelerometerSmoothing: true,
//smooths the output from the analog sticks (moving averages) defaults to false
analogStickSmoothing: false
});
//make sure you add an error event handler
controller.on('error', err => console.log(err));
//DualShock 4 control rumble and light settings for the controller
controller.setExtras({
rumbleLeft: 0, // 0-255 (Rumble left intensity)
rumbleRight: 0, // 0-255 (Rumble right intensity)
red: 0, // 0-255 (Red intensity)
green: 75, // 0-255 (Blue intensity)
blue: 225, // 0-255 (Green intensity)
flashOn: 40, // 0-255 (Flash on time)
flashOff: 10 // 0-255 (Flash off time)
});
//DualShock 3 control rumble and light settings for the controller
controller.setExtras({
rumbleLeft: 0, // 0-1 (Rumble left on/off)
rumbleRight: 0, // 0-255 (Rumble right intensity)
led: 2 // 2 | 4 | 8 | 16 (Leds 1-4 on/off, bitmasked)
});
//add event handlers:
controller.on('left:move', data => console.log('left Moved: ' + data.x + ' | ' + data.y));
controller.on('right:move', data => console.log('right Moved: ' + data.x + ' | ' + data.y));
controller.on('connected', () => console.log('connected'));
controller.on('square:press', ()=> console.log('square press'));
controller.on('square:release', () => console.log('square release'));
//sixasis motion events:
//the object returned from each of the movement events is as follows:
//{
// direction : values can be: 1 for right, forward and up. 2 for left, backwards and down.
// value : values will be from 0 to 120 for directions right, forward and up and from 0 to -120 for left, backwards and down.
//}
//DualShock 4 TouchPad
//finger 1 is x1 finger 2 is x2
controller.on('touchpad:x1:active', () => console.log('touchpad one finger active'));
controller.on('touchpad:x2:active', () => console.log('touchpad two fingers active'));
controller.on('touchpad:x2:inactive', () => console.log('touchpad back to single finger'));
controller.on('touchpad:x1', data => console.log('touchpad x1:', data.x, data.y));
controller.on('touchpad:x2', data => console.log('touchpad x2:', data.x, data.y));
//right-left movement
controller.on('rightLeft:motion', data => console.log(data));
//forward-back movement
controller.on('forwardBackward:motion', data => console.log(data));
//up-down movement
controller.on('upDown:motion', data => console.log(data));
//controller status
//as of version 0.6.2 you can get the battery %, if the controller is connected and if the controller is charging
controller.on('battery:change', data => console.log(data));
controller.on('connection:change', data => console.log(data));
controller.on('charging:change', data => console.log(data));
~~~~
## <a name="linux-support"></a> Linux support:
In order to provide Rumble/Gyro and LED support for all platforms the linux specific joystick implementation has been removed. This means you will need to:
* [Install node-hid build requirements](#node-hid-build)
* [Install node-hid with hidraw support](#node-hid-hidraw)
* [create udev rules](#create-udev-rules)
#### <a name="node-hid-build"></a> Install node-hid build requirements
To build node-hid you will need to install:
* libudev-dev
* libusb-1.0-0
* libusb-1.0-0-dev
* build-essential
* git
* node-gyp
* node-pre-gyp
Using apt-get:
```bash
sudo apt-get install libudev-dev libusb-1.0-0 libusb-1.0-0-dev build-essential git
```
```bash
npm install -g node-gyp node-pre-gyp
```
#### <a name="node-hid-hidraw"></a> Install node-hid with hidraw support
Once you have run the installation scripts above you can install the node-dualshock module, then replace the installed node-hid with hidraw support enabled node-hid:
```bash
npm install dualshock-controller
```
```bash
npm install node-hid --driver=hidraw --build-from-source
```
#### <a name="create-udev-rules"></a> Create udev rules
You will need to create a udev rule to be able to access the hid stream as a non root user.
Write the following file in `/etc/udev/rules.d/61-dualshock.rules`
```
SUBSYSTEM=="input", GROUP="input", MODE="0666"
SUBSYSTEM=="usb", ATTRS{idVendor}=="054c", ATTRS{idProduct}=="0268", MODE:="666", GROUP="plugdev"
KERNEL=="hidraw*", SUBSYSTEM=="hidraw", MODE="0664", GROUP="plugdev"
SUBSYSTEM=="input", GROUP="input", MODE="0666"
SUBSYSTEM=="usb", ATTRS{idVendor}=="054c", ATTRS{idProduct}=="05c4", MODE:="666", GROUP="plugdev"
KERNEL=="hidraw*", SUBSYSTEM=="hidraw", MODE="0664", GROUP="plugdev"
```
Reload the rules `sudo udevadm control --reload-rules`, then disconnect/connect the controller.
The MIT License (MIT)
Copyright (c) 2017 Ricardo de Pena
Permission is hereby granted, free of charge, to any person obtaining a copy of
this software and associated documentation files (the "Software"), to deal in
the Software without restriction, including without limitation the rights to
use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
the Software, and to permit persons to whom the Software is furnished to do so,
subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.

View file

@ -0,0 +1,35 @@
These json files are used to map the controller:
=========================
## To add a new controller:
Use DeviceDiscoveryHelp.js in the examples folder, using node-hid you can obtain the values you need to create a controller.json file and leverage the library to wire it up.
### To connect the right controller
the vendorId and the productId need to be set to the right values, you can use node-hid to determine what these are.
### Analogs are mapped as:
~~~~ js
"analogSticks" : [
{
"name" : "left",
"x" : 7,
"y" : 6
},
{
"name" : "right",
"x" : 9,
"y" : 8
}
]
### Buttons are usually grouped by a block but should be added as:
~~~~ js
"buttons" : [
{
"name": name of the button used for events,
"buttonBlock": int representing the button block,
"buttonValue": bit value,
"analogPin" : int representing the pin used for analog.
},
]

View file

@ -0,0 +1,244 @@
{
"vendorId" : 1356,
"productId" : 616,
"analogSticks" : [
{
"name" : "left",
"x" : 6,
"y" : 7,
"joystickXNumber" : 0,
"joystickYNumber" : 1
},
{
"name" : "right",
"x" : 8,
"y" : 9,
"joystickXNumber" : 2,
"joystickYNumber" : 3
}
],
"buttons" : [
{
"name": "l2",
"buttonBlock": 3,
"buttonValue": "0x01",
"analogPin" : 18,
"joystickNumber": 8
},
{
"name": "r2",
"buttonBlock": 3,
"buttonValue": "0x02",
"analogPin" : 19,
"joystickNumber" : 9
},
{
"name": "l1",
"buttonBlock": 3,
"buttonValue": "0x04",
"analogPin" : 20,
"joystickNumber" : 10
},
{
"name":"r1",
"buttonBlock": 3,
"buttonValue": "0x08",
"analogPin" : 21,
"joystickNumber" : 11
},
{
"name": "triangle",
"buttonBlock": 3,
"buttonValue": "0x10",
"analogPin" : 22,
"joystickNumber" : 12
},
{
"name": "circle",
"buttonBlock": 3,
"buttonValue": "0x20",
"analogPin" : 23,
"joystickNumber" : 13
},
{
"name": "x",
"buttonBlock": 3,
"buttonValue": "0x40",
"analogPin" : 24,
"joystickNumber" : 14
},
{
"name": "square",
"buttonBlock": 3,
"buttonValue": "0x80",
"analogPin": 25,
"joystickNumber" : 15
},
{
"name": "select",
"buttonBlock": 2,
"buttonValue": "0x1",
"joystickNumber" : 0
},
{
"name": "leftAnalogBump",
"buttonBlock": 2,
"buttonValue": "0x2",
"joystickNumber" : 1
},
{
"name": "rightAnalogBump",
"buttonBlock": 2,
"buttonValue": "0x4",
"joystickNumber" : 2
},
{
"name": "start",
"buttonBlock": 2,
"buttonValue": "0x08",
"joystickNumber" : 3
},
{
"name": "dpadUp",
"buttonBlock": 2,
"buttonValue": "0x10",
"analogPin": 14,
"joystickNumber" : 4
},
{
"name": "dpadRight",
"buttonBlock": 2,
"buttonValue": "0x20",
"analogPin": 15,
"joystickNumber" : 5
},
{
"name": "dpadDown",
"buttonBlock": 2,
"buttonValue" : "0x40",
"analogPin" : 16,
"joystickNumber" : 6
},
{
"name": "dpadLeft",
"buttonBlock": 2,
"buttonValue": "0x80",
"analogPin": 17,
"joystickNumber" : 7
},
{
"name": "psxButton",
"buttonBlock":4,
"buttonValue": "0x01",
"joystickNumber" : 16
}
],
"motionInputs": [
{
"name" : "rightLeft",
"directionPin" : 41,
"valuePin" : 42
},
{
"name" : "forwardBackward",
"directionPin" : 43,
"valuePin" : 44
},
{
"name" : "upDown",
"directionPin" : 45,
"valuePin" : 46
},
{
"name" : "yaw",
"directionPin" : 47,
"valuePin" : 48
}
],
"status": [
{
"name" : "charging",
"pin" : 29,
"states" : [
{
"value" : 0,
"state" : "Charging"
},
{
"value" : 2,
"state" : "Charging"
},
{
"value" : 3,
"state" : "Not Charging"
}
]
},
{
"name" : "battery",
"pin" : 30,
"states" : [
{
"value" : 238,
"state" : "Charging"
},
{
"value" : 0,
"state" : "No charge"
},
{
"value" : 1,
"state" : "20%"
},
{
"value" : 2,
"state" : "40%"
},
{
"value" : 3,
"state" : "60%"
},
{
"value" : 4,
"state" : "80%"
},
{
"value" : 5,
"state" : "100%"
}
]
},
{
"name" : "connection",
"pin" : 31,
"states" : [
{
"value" : 18,
"state" : "Usb"
},
{
"value" : 22,
"state" : "Bluetooth"
},
{
"value" : 20,
"state" : "Rumbling"
}
]
}
],
"output": {
"defaultBuffer":[
1, 0, 254, 0, 254, 0, 0, 0, 0, 0, 0,
255, 39, 16, 0, 50, 255, 39, 16, 0, 50, 255,
39, 16, 0, 50, 255, 39, 16, 0, 50, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0
],
"indexes": {
"rumbleLeft": 3,
"rumbleRight": 5,
"led": 10
}
}
}

View file

@ -0,0 +1,177 @@
{
"vendorId" : 1356,
"productId" : 2508,
"analogSticks" : [
{
"name" : "left",
"x" : 1,
"y" : 2
},
{
"name" : "right",
"x" : 3,
"y" : 4
}
],
"buttons" : [
{
"name": "l2",
"buttonBlock": 6,
"buttonValue": "0x04",
"analogPin" : 8
},
{
"name": "r2",
"buttonBlock": 6,
"buttonValue": "0x08",
"analogPin" : 9
},
{
"name": "l1",
"buttonBlock": 6,
"buttonValue": "0x01"
},
{
"name": "r1",
"buttonBlock": 6,
"buttonValue": "0x02"
},
{
"name": "leftAnalogBump",
"buttonBlock": 6,
"buttonValue": "0x04"
},
{
"name": "rightAnalogBump",
"buttonBlock": 6,
"buttonValue": "0x08"
},
{
"name": "psxButton",
"buttonBlock": 7,
"buttonValue": "0x01"
},
{
"name": "touchPad",
"buttonBlock": 7,
"buttonValue": "0x02"
},
{
"name": "square",
"buttonBlock": 5,
"buttonValue": "0x10"
},
{
"name": "triangle",
"buttonBlock": 5,
"buttonValue": "0x80"
},
{
"name": "circle",
"buttonBlock": 5,
"buttonValue": "0x40"
},
{
"name": "x",
"buttonBlock": 5,
"buttonValue": "0x20"
},
{
"name": "dpadUp",
"buttonBlock": 5,
"buttonValue": "0x00",
"mask": "0xF"
},
{
"name": "dpadUpRight",
"buttonBlock": 5,
"buttonValue": "0x01",
"mask": "0xF"
},
{
"name": "dpadRight",
"buttonBlock": 5,
"buttonValue": "0x02",
"mask": "0xF"
},
{
"name": "dpadDownRight",
"buttonBlock": 5,
"buttonValue": "0x03",
"mask": "0xF"
},
{
"name": "dpadDown",
"buttonBlock": 5,
"buttonValue" : "0x04",
"mask": "0xF"
},
{
"name": "dpadDownLeft",
"buttonBlock": 5,
"buttonValue" : "0x05",
"mask": "0xF"
},
{
"name": "dpadLeft",
"buttonBlock": 5,
"buttonValue": "0x06",
"mask": "0xF"
},
{
"name": "dpadUpLeft",
"buttonBlock": 5,
"buttonValue": "0x07",
"mask": "0xF"
},
{
"name": "share",
"buttonBlock": 6,
"buttonValue": "0x10"
},
{
"name": "options",
"buttonBlock": 6,
"buttonValue": "0x20"
},
{
"name": "leftStick",
"buttonBlock": 6,
"buttonValue": "0x40"
},
{
"name": "rightStick",
"buttonBlock": 6,
"buttonValue": "0x80"
}
],
"motionInputs" : [],
"status" : [],
"output": {
"defaultBuffer":[
5,255,4,0,0,0,0,0,0,0,0
],
"indexes": {
"rumbleLeft": 4,
"rumbleRight": 5,
"red": 6,
"green": 7,
"blue": 8,
"flashOn": 9,
"flashOff": 10
}
},
"touchPad": [{
"name":"x1",
"activePin": 35,
"dataPinA": 37,
"dataPinB": 36,
"dataPinC": 38
},{
"name": "x2",
"activePin": 39,
"dataPinA": 41,
"dataPinB": 40,
"dataPinC": 42
}]
}

View file

@ -0,0 +1,177 @@
{
"vendorId" : 1356,
"productId" : 1476,
"analogSticks" : [
{
"name" : "left",
"x" : 1,
"y" : 2
},
{
"name" : "right",
"x" : 3,
"y" : 4
}
],
"buttons" : [
{
"name": "l2",
"buttonBlock": 6,
"buttonValue": "0x04",
"analogPin" : 8
},
{
"name": "r2",
"buttonBlock": 6,
"buttonValue": "0x08",
"analogPin" : 9
},
{
"name": "l1",
"buttonBlock": 6,
"buttonValue": "0x01"
},
{
"name": "r1",
"buttonBlock": 6,
"buttonValue": "0x02"
},
{
"name": "leftAnalogBump",
"buttonBlock": 6,
"buttonValue": "0x04"
},
{
"name": "rightAnalogBump",
"buttonBlock": 6,
"buttonValue": "0x08"
},
{
"name": "psxButton",
"buttonBlock": 7,
"buttonValue": "0x01"
},
{
"name": "touchPad",
"buttonBlock": 7,
"buttonValue": "0x02"
},
{
"name": "square",
"buttonBlock": 5,
"buttonValue": "0x10"
},
{
"name": "triangle",
"buttonBlock": 5,
"buttonValue": "0x80"
},
{
"name": "circle",
"buttonBlock": 5,
"buttonValue": "0x40"
},
{
"name": "x",
"buttonBlock": 5,
"buttonValue": "0x20"
},
{
"name": "dpadUp",
"buttonBlock": 5,
"buttonValue": "0x00",
"mask": "0xF"
},
{
"name": "dpadUpRight",
"buttonBlock": 5,
"buttonValue": "0x01",
"mask": "0xF"
},
{
"name": "dpadRight",
"buttonBlock": 5,
"buttonValue": "0x02",
"mask": "0xF"
},
{
"name": "dpadDownRight",
"buttonBlock": 5,
"buttonValue": "0x03",
"mask": "0xF"
},
{
"name": "dpadDown",
"buttonBlock": 5,
"buttonValue" : "0x04",
"mask": "0xF"
},
{
"name": "dpadDownLeft",
"buttonBlock": 5,
"buttonValue" : "0x05",
"mask": "0xF"
},
{
"name": "dpadLeft",
"buttonBlock": 5,
"buttonValue": "0x06",
"mask": "0xF"
},
{
"name": "dpadUpLeft",
"buttonBlock": 5,
"buttonValue": "0x07",
"mask": "0xF"
},
{
"name": "share",
"buttonBlock": 6,
"buttonValue": "0x10"
},
{
"name": "options",
"buttonBlock": 6,
"buttonValue": "0x20"
},
{
"name": "leftStick",
"buttonBlock": 6,
"buttonValue": "0x40"
},
{
"name": "rightStick",
"buttonBlock": 6,
"buttonValue": "0x80"
}
],
"motionInputs" : [],
"status" : [],
"output": {
"defaultBuffer":[
5,255,4,0,0,0,0,0,0,0,0
],
"indexes": {
"rumbleLeft": 4,
"rumbleRight": 5,
"red": 6,
"green": 7,
"blue": 8,
"flashOn": 9,
"flashOff": 10
}
},
"touchPad": [{
"name":"x1",
"activePin": 35,
"dataPinA": 37,
"dataPinB": 36,
"dataPinC": 38
},{
"name": "x2",
"activePin": 39,
"dataPinA": 41,
"dataPinB": 40,
"dataPinC": 42
}]
}

View file

@ -0,0 +1,195 @@
{
"vendorId" : 1356,
"productId" : 1476,
"analogSticks" : [
{
"name" : "left",
"x" : 1,
"y" : 2,
"joystickXNumber" : 0,
"joystickYNumber" : 1
},
{
"name" : "right",
"x" : 3,
"y" : 4,
"joystickXNumber" : 2,
"joystickYNumber" : 5
},
{
"name" : "l2Analog",
"x" : 100,
"joystickXNumber" : 3
},
{
"name" : "r2Analog",
"x" : 101,
"joystickXNumber" : 4
}
],
"buttons" : [
{
"name": "l2",
"buttonBlock": 6,
"buttonValue": "0x04",
"analogPin" : 8,
"joystickNumber": 6
},
{
"name": "r2",
"buttonBlock": 6,
"buttonValue": "0x08",
"analogPin" : 9,
"joystickNumber": 7
},
{
"name": "l1",
"buttonBlock": 6,
"buttonValue": "0x01",
"joystickNumber": 4
},
{
"name": "r1",
"buttonBlock": 6,
"buttonValue": "0x02",
"joystickNumber": 5
},
{
"name": "psxButton",
"buttonBlock": 7,
"buttonValue": "0x01",
"joystickNumber": 12
},
{
"name": "touchPad",
"buttonBlock": 7,
"buttonValue": "0x02",
"joystickNumber": 13
},
{
"name": "square",
"buttonBlock": 5,
"buttonValue": "0x10",
"joystickNumber": 0
},
{
"name": "triangle",
"buttonBlock": 5,
"buttonValue": "0x80",
"joystickNumber": 3
},
{
"name": "circle",
"buttonBlock": 5,
"buttonValue": "0x40",
"joystickNumber": 2
},
{
"name": "x",
"buttonBlock": 5,
"buttonValue": "0x20",
"joystickNumber": 1
},
{
"name": "dpadUp",
"buttonBlock": 5,
"buttonValue": "0x00",
"mask": "0xF"
},
{
"name": "dpadUpRight",
"buttonBlock": 5,
"buttonValue": "0x01",
"mask": "0xF"
},
{
"name": "dpadRight",
"buttonBlock": 5,
"buttonValue": "0x02",
"mask": "0xF"
},
{
"name": "dpadDownRight",
"buttonBlock": 5,
"buttonValue": "0x03",
"mask": "0xF"
},
{
"name": "dpadDown",
"buttonBlock": 5,
"buttonValue" : "0x04",
"mask": "0xF"
},
{
"name": "dpadDownLeft",
"buttonBlock": 5,
"buttonValue" : "0x05",
"mask": "0xF"
},
{
"name": "dpadLeft",
"buttonBlock": 5,
"buttonValue": "0x06",
"mask": "0xF"
},
{
"name": "dpadUpLeft",
"buttonBlock": 5,
"buttonValue": "0x07",
"mask": "0xF"
},
{
"name": "share",
"buttonBlock": 6,
"buttonValue": "0x10",
"joystickNumber": 8
},
{
"name": "options",
"buttonBlock": 6,
"buttonValue": "0x20",
"joystickNumber": 9
},
{
"name": "leftStick",
"buttonBlock": 6,
"buttonValue": "0x40",
"joystickNumber": 10
},
{
"name": "rightStick",
"buttonBlock": 6,
"buttonValue": "0x80",
"joystickNumber": 11
}
],
"motionInputs" : [],
"status" : [],
"output": {
"defaultBuffer":[
5,255,4,0,0,0,0,0,0,0,0
],
"indexes": {
"rumbleLeft": 4,
"rumbleRight": 5,
"red": 6,
"green": 7,
"blue": 8,
"flashOn": 9,
"flashOff": 10
}
},
"touchPad": [{
"name":"x1",
"activePin": 35,
"dataPinA": 37,
"dataPinB": 36,
"dataPinC": 38
},{
"name": "x2",
"activePin": 39,
"dataPinA": 41,
"dataPinB": 40,
"dataPinC": 42
}]
}

View file

@ -0,0 +1,8 @@
Examples:
=========================
## Don't use this code as a starting point.
I do things here that are not neccesary, you are better off looking at the readme in root as it has a cleaner implementation, this is here just so you can test it right away.
If you'd like to see more examples for the DualShock 4, feel free to check out
[ds-examples](https://github.com/itaisteinherz/ds-examples).

View file

@ -0,0 +1,61 @@
var printcontrollerEvents = function(controller, controllerConfiguration) {
'use strict';
controller.on('left:move', function(data) {
console.log('left Moved');
console.log(data);
});
controller.on('right:move', function(data) {
console.log('right Moved');
console.log(data);
});
controller.on('connected', function() {
console.log('connected');
});
controller.on('error', function(data) {
console.log(data);
});
var pressed = function(data) {
console.log(data + ": press");
};
var released = function(data) {
console.log(data + ": release");
};
var analog = function(data) {
console.log(data + ": analog");
};
var hold = function(data) {
console.log(data + ": hold");
};
var motion = function(motionInput, data) {
console.log(motionInput);
console.log(data);
};
//subscribe to all the buttons:
for (var i = 0; i < controllerConfiguration.buttons.length; i++) {
controller.on(controllerConfiguration.buttons[i].name + ":press", pressed);
controller.on(controllerConfiguration.buttons[i].name + ":release", released);
controller.on(controllerConfiguration.buttons[i].name + ":analog", analog);
controller.on(controllerConfiguration.buttons[i].name + ":hold", hold);
}
//subscribe to all the status events:
if (controllerConfiguration.status && controllerConfiguration.status.length) {
for (i = 0; i < controllerConfiguration.status.length; i++) {
controller.on(controllerConfiguration.status[i].name + ":change", console.log);
}
}
//subscribe to the motion events.
controller.on('rightLeft' + ':motion', function(data) {
motion('rightLeft', data);
});
controller.on('forwardBackward' + ':motion', function(data) {
motion('forwardBackward', data);
});
controller.on('upDown' + ':motion', function(data) {
motion('upDown', data);
});
};
module.exports = printcontrollerEvents;

View file

@ -0,0 +1,52 @@
'use strict';
var dualShock = require('./../src/dualshock.js');
var dualShock3;
//init the controller
dualShock3 = dualShock({
analogStickSmoothing: false,
config: "dualShock3",
logging: true
});
// //for a client implementation we do not need this, this is only to test the inputs.
var controllerConfiguration = require('./../controllerConfigurations/dualShock3');
// //init the print events
var consolePrintEvents = require('./consolePrintControllerEvents')(dualShock3, controllerConfiguration);
dualShock3.on("dpadup:press", () => {
dualShock3.setExtras({
led: 2
});
});
dualShock3.on("dpadright:press", () => {
dualShock3.setExtras({
led: 4
});
});
dualShock3.on("dpaddown:press", () => {
dualShock3.setExtras({
led: 8
});
});
dualShock3.on("dpadleft:press", () => {
dualShock3.setExtras({
led: 16
});
});
dualShock3.on("r2:analog", (d) => {
dualShock3.setExtras({
rumbleRight: d
});
});
dualShock3.on("l2:press", (d) => {
dualShock3.setExtras({
rumbleLeft: 1
});
});

View file

@ -0,0 +1,59 @@
'use strict';
var dualShock = require('./../src/dualshock.js');
var dualShock4 = dualShock({
config: "dualShock4",
logging: true
});
//for a client implementation we do not need this, this is only to test the inputs.
var controllerConfiguration = require('./../controllerConfigurations/dualShock4');
//init the print events
var consolePrintEvents = require('./consolePrintControllerEvents')(dualShock4, controllerConfiguration);
dualShock4.on("dpadup:press", () => {
dualShock4.setExtras({
red: 255
});
});
dualShock4.on("dpadright:press", () => {
dualShock4.setExtras({
green: 255
});
});
dualShock4.on("dpaddown:press", () => {
dualShock4.setExtras({
blue: 255
});
});
dualShock4.on("dpadleft:press", () => {
dualShock4.setExtras({
red: 255,
green: 255,
blue: 255
});
});
dualShock4.on("x:press", (d) => {
dualShock4.setExtras({
red: 255,
flashOn: 50,
flashOff: 10
});
});
dualShock4.on("r2:analog", (d) => {
dualShock4.setExtras({
rumbleRight: d
});
});
dualShock4.on("l2:analog", (d) => {
dualShock4.setExtras({
rumbleLeft: d
});
});

View file

@ -0,0 +1,12 @@
var HID = require('node-hid');
console.log(HID.devices());
var controller = new HID.HID(1356, 616);
controller.on('data', function(data) {
for (var i = 0; i < data.length; i++) {
if (i === 30) {
console.log(i + " " + data[i]);
}
}
});

View file

@ -0,0 +1,6 @@
var joystick = require('joystick'),
controller = new joystick(0, 256, 500);
controller.on('button', console.log);
controller.on('axis', console.log);

67
dist/dualshock-controller/gruntfile.js vendored Normal file
View file

@ -0,0 +1,67 @@
const files = ['gruntfile.js',
'src/*.js',
'src/inputProcessors/*.js',
'test/*.js',
'examples/*.js'
];
module.exports = function(grunt) {
grunt.initConfig({
watch: {
files: files,
tasks: ['default']
},
jshint: {
files: files,
options: {
reporterOutput: "",
esnext: true,
node: true,
globals: {
describe: true,
it: true,
beforeEach: true
}
}
},
mochaTest: {
test: {
options: {
reporter: 'spec'
},
src: ['test/*.js']
}
},
jsbeautifier: {
files: files,
options: {
js: {
braceStyle: "collapse",
breakChainedMethods: false,
e4x: false,
evalCode: false,
indentChar: " ",
indentLevel: 0,
indentSize: 4,
indentWithTabs: false,
jslintHappy: false,
keepArrayIndentation: false,
keepFunctionIndentation: false,
maxPreserveNewlines: 10,
preserveNewlines: true,
spaceBeforeConditional: true,
spaceInParen: false,
unescapeStrings: false,
wrapLineLength: 0
}
}
}
});
grunt.loadNpmTasks('grunt-contrib-watch');
grunt.loadNpmTasks('grunt-contrib-jshint');
grunt.loadNpmTasks('grunt-mocha-test');
grunt.loadNpmTasks('grunt-jsbeautifier');
grunt.registerTask('test', ['jshint', 'mochaTest']);
grunt.registerTask('default', ['jshint', 'mochaTest', 'jsbeautifier']);
};

47
dist/dualshock-controller/package.json vendored Normal file
View file

@ -0,0 +1,47 @@
{
"name": "dualshock-controller",
"version": "1.1.1",
"description": "Eventing API layer over HID for the Sony DualShock 3 and DualShock 4 controllers ",
"main": "./src/dualshock.js",
"dependencies": {
"node-hid": "^0.5.0"
},
"devDependencies": {
"grunt": "^1.0.1",
"grunt-cli": "^1.2.0",
"grunt-contrib-jshint": "^1.1.0",
"grunt-contrib-watch": "^1.0.0",
"grunt-mocha-test": "^0.13.2",
"mocha": "^3.2.0",
"sinon": "^1.8",
"nyc": "^10.0.0",
"grunt-jsbeautifier": "^0.2.7",
"jshint": "^2.5.0",
"mockery": "^2.0.0"
},
"scripts": {
"test": "grunt test",
"coverage": "nyc npm test"
},
"engines": {
"node": ">=6.9.2"
},
"repository": {
"type": "git",
"url": "https://github.com/rdepena/node-dualshock-controller.git"
},
"author": "ricardo de pena",
"license": "MIT",
"readmeFilename": "README.md",
"keywords": [
"ps3",
"controller",
"gamepad",
"dualshock",
"dualshock3",
"dualshock4",
"dual shock 3",
"dual shock 4",
"node-hid"
]
}

BIN
dist/dualshock-controller/src/HID.node vendored Normal file

Binary file not shown.

View file

@ -0,0 +1,51 @@
'use strict';
// Module dependencies.
var dsutilities = require('./utilities'),
Smoothing = require('./smoothing'),
config = require('./config');
//Proccess Analog stick events.
var Analogs = function(controller) {
var varianceThreshhold = 1,
smoothInput = config.getOptions().analogStickSmoothing,
outputSmoothing = new Smoothing(smoothInput),
analogSticks = config.getControllerConfig().analogSticks;
//Private methods
var processStick = function(analogStick, data) {
var currentValue = {
x: data[analogStick.x],
y: data[analogStick.y]
},
previousValue = {
x: outputSmoothing.readLastPosition(analogStick.name + 'x'),
y: outputSmoothing.readLastPosition(analogStick.name + 'y')
};
//we only raise an event if both
if (dsutilities.isWithinVariance(previousValue.x, currentValue.x, varianceThreshhold) ||
dsutilities.isWithinVariance(previousValue.y, currentValue.y, varianceThreshhold)) {
currentValue.x = outputSmoothing.smooth(analogStick.name + 'x', currentValue.x);
currentValue.y = outputSmoothing.smooth(analogStick.name + 'y', currentValue.y);
// Update and emit
if (controller[analogStick.name]) {
controller[analogStick.name].x = currentValue.x;
controller[analogStick.name].y = currentValue.y;
}
controller.emit(analogStick.name + ':move', currentValue);
}
};
// Public methods
//process all the analog events.
this.process = function(data) {
for (var i = 0; i < analogSticks.length; i++) {
processStick(analogSticks[i], data);
}
};
};
module.exports = Analogs;

104
dist/dualshock-controller/src/buttons.js vendored Normal file
View file

@ -0,0 +1,104 @@
'use strict';
// Module dependencies.
var config = require('./config'),
dsutilities = require('./utilities');
//Proccess button events.
var Buttons = function(controller) {
var buttons = config.getControllerConfig().buttons;
// convert strings to numbers, e.g. "0x01" to 0x01
// must be converted because JSON doesn't allow numbers with leading zeros
buttons.forEach(function(button) {
if (typeof button.buttonValue == "string") {
button.buttonValue = parseInt(button.buttonValue);
}
if (typeof button.mask == "string") {
button.mask = parseInt(button.mask);
} else if (!(button.mask instanceof Number)) {
button.mask = 0xFF;
}
//generate event name aliases:
button.eventPrefixes = dsutilities.generateEventPrefixAliases(button.name);
});
var buffer = {};
//Private methods
var emitEvent = function(button, eventText, data) {
button.eventPrefixes.forEach(function(eventPrefix) {
controller.emit(eventPrefix + eventText, data);
});
};
var processButton = function(button, data) {
//make sure the data contains a value for the specified block
//and bitwise operation for the button value
var block = data[button.buttonBlock] & button.mask;
var hit = (block & button.buttonValue) == button.buttonValue;
var value = 0;
var state = 0; // 0: up, 1: down, 2: hold
// special case for the dualshock 4's dpadUp button as it causes the
// lower 8 bits of it's block to be zeroed
if (!button.buttonValue) {
hit = !block;
}
// special case for dualshock 4's dpad - they are not bitmasked values as
// they cannot be pressed together - ie. up, left and upleft are three
// different values - upleft is not equal to up & left
if (button.buttonBlock == 5 && block < 0x08) {
hit = block == button.buttonValue;
}
if (hit) {
value = 1;
//if the button is in the released state.
if (!buffer[button.name]) {
state = 1;
buffer[button.name] = true;
emitEvent(button, ':press', button.name);
} else {
state = 2;
emitEvent(button, ':hold', button.name);
}
//send the analog data
if (button.analogPin && data[button.analogPin]) {
emitEvent(button, ':analog', data[button.analogPin]);
}
} else if (buffer[button.name]) {
//button was pressed and is not released
buffer[button.name] = false;
//button is no longer pressed, emit a analog 0 event.
if (button.analogPin) {
emitEvent(button, ':analog', 0);
}
//emit the released event.
emitEvent(button, ':release', button.name);
}
if (controller[button.name]) {
controller[button.name].value = value;
controller[button.name].state = state;
}
};
// Public methods
//process all the analog events.
this.process = function(data) {
for (var i = 0; i < buttons.length; i++) {
processButton(buttons[i], data);
}
};
};
module.exports = Buttons;

56
dist/dualshock-controller/src/config.js vendored Normal file
View file

@ -0,0 +1,56 @@
'use strict';
// Module dependencies.
// we will expose these objects via the manager.
var options,
controllerConfig;
//provides access to the current options and configs.
var config = {
setOptions: function(opts) {
//no options were passed
options = opts || {};
// Defaults:
var defaultValues = {
config: "dualShock3",
accelerometerSmoothing: true,
analogStickSmoothing: false,
logging: false,
forceNodeHid: false,
linuxJoystickId: 0
};
for (var name in defaultValues) {
if (defaultValues.hasOwnProperty(name)) {
var target = options[name];
var orig = defaultValues[name];
if (!target) {
options[name] = orig;
}
}
}
var controllerConfiguration;
//use passed config or load from built-in configs
if (typeof options.config === "object") {
controllerConfiguration = options.config;
} else {
controllerConfiguration = require('./../controllerConfigurations/' + options.config);
}
//set the current controllerConfiguration
config.setControllerConfig(controllerConfiguration);
},
getOptions: function() {
return options;
},
setControllerConfig: function(config) {
controllerConfig = config;
},
getControllerConfig: function() {
return controllerConfig;
}
};
module.exports = config;

View file

@ -0,0 +1,167 @@
// Module dependencies.
const util = require('util'),
dsutilities = require('./utilities'),
Emitter = require('events').EventEmitter,
Gyro = require('./gyro'),
Analogs = require('./analogs'),
Buttons = require('./buttons'),
Status = require('./status'),
HID = require('./HID'),
config = require('./config'),
TouchPad = require('./touchpad');
//generic controller object, it will need a controller Configuration with a buttons array passed into its connect function.
const Controller = function() {
'use strict';
Emitter.call(this);
const controllerConfig = config.getControllerConfig(),
options = config.getOptions(),
indexes = controllerConfig.output.indexes,
analogs = new Analogs(this),
buttons = new Buttons(this),
gyro = new Gyro(this),
status = new Status(this),
touchPad = new TouchPad(this);
let device = null;
[{
type: 'analogSticks',
properties: [{
name: 'x',
initialValue: 0
}, {
name: 'y',
initialValue: 0
}]
}, {
type: 'buttons',
properties: [{
name: 'state',
initialValue: 0
}, {
name: 'value',
initialValue: 0
}]
}, {
type: 'motionInputs',
properties: [{
name: 'value',
initialValue: 0
}, {
name: 'direction',
initialValue: 0
}]
}, {
type: 'status',
properties: [{
name: 'state',
initialValue: ''
}]
}].forEach(function(setup) {
const entities = controllerConfig[setup.type],
properties = setup.properties;
if (entities.length) {
entities.forEach(function(entity) {
this[entity.name] = properties.reduce(function(accum, property) {
return (accum[property.name] = property.initialValue, accum);
}, {});
}, this);
}
}, this);
//Private methods
//emit an error event or log it to the console.
const handleException = function(ex) {
//if exception was generated within our stream
if (this && this.emit) {
this.emit('error', ex);
} else {
dsutilities.warn(ex);
throw (ex);
}
};
//process data from HID connected device.
const processFrame = function(data) {
if (controllerConfig.motionInputs) {
gyro.process(data);
}
if (controllerConfig.analogSticks) {
analogs.process(data);
}
if (controllerConfig.buttons) {
buttons.process(data);
}
if (controllerConfig.status) {
status.process(data);
}
if (controllerConfig.touchPad) {
touchPad.process(data);
}
};
const isController = function(device) {
var vendor = (device.vendorId == controllerConfig.vendorId);
var product = (device.productId == controllerConfig.productId || device.productId == 2508);
return vendor && product;
};
// Public methods
this.connect = function() {
dsutilities.warn('connect method is deprecated, controller now connects upon declaration.');
};
this.disconnect = function() {
if (device && device.close) {
device.close();
}
this.emit('disconnecting');
dsutilities.warn('node dualshock disconnecting');
};
// Used to set controller rumble and light
this.setExtras = function(data) {
let buff = controllerConfig.output.defaultBuffer.slice();
Object.keys(data).forEach(k => {
buff[indexes[k]] = data[k];
});
device.write(buff);
};
//connect to the controller.
if (typeof options.device === 'undefined') {
dsutilities.warn('node dualshock connecting');
var devices = HID.devices();
const deviceMeta = devices
.filter(isController)[0];
if (deviceMeta) {
device = new HID.HID(deviceMeta.path);
console.log(device);
device.on('data', processFrame.bind(this));
device.on('error', handleException.bind(this));
} else {
handleException(new Error(`device with Vendor ID:${controllerConfig.vendorId} Product ID:${controllerConfig.productId} not found`));
}
} else {
// Allow user-specified device
device = options.device;
console.log("Custom device: ", device);
device.on('data', processFrame.bind(this));
device.on('error', handleException.bind(this));
}
//subscribe to the exit event:
process.on('exit', this.disconnect.bind(this));
};
//need to inherit from event emiter.
util.inherits(Controller, Emitter);
module.exports = Controller;

View file

@ -0,0 +1,22 @@
// Module dependencies.
var Controller = require('./controller'),
config = require('./config');
// This is the app entry point.
// options you can pass:
// {
// config : "File from controllerConfigurations" or a JS object containing configuration,
// accelerometerSmoothing : true/false, this will activate motion/acelerometer output smoothing. true by default.
// analogStickSmoothing : true/false, this will activate analog thumb stick smoothing
// }
var dualShock = function(options) {
'use strict';
//set the current options
config.setOptions(options);
//returns the controller.
return new Controller();
};
module.exports = dualShock;

72
dist/dualshock-controller/src/gyro.js vendored Normal file
View file

@ -0,0 +1,72 @@
'use strict';
// Module dependencies.
var dsutilities = require('./utilities'),
Smoothing = require('./smoothing'),
config = require('./config');
//Proccess button events.
var motionProcessor = function(controller) {
var varianceThreshhold = 1,
smoothInput = config.getOptions().accelerometerSmoothing,
outputSmoothing = new Smoothing(smoothInput),
motionInputs = config.getControllerConfig().motionInputs;
//generate event name aliases:
motionInputs.forEach(function(motionAxis) {
motionAxis.eventPrefixes = dsutilities.generateEventPrefixAliases(motionAxis.name);
});
//Private methods
//data corrections so that each dirrection has a 0 throug x value
var correctData = function(motionAxis, data) {
var value;
//ensuring that both directions start from 0 and move to -x or x;
if (data[motionAxis.directionPin] === 1) {
//we need the values to be from 0 to x.
value = 255 - data[motionAxis.valuePin];
} else if (data[motionAxis.directionPin] === 2) {
//going in the oposite direction, we need to values to be from 0 to -x;
value = data[motionAxis.valuePin] * -1;
}
//return an object with both value and dirrection.
return {
direction: data[motionAxis.directionPin],
value: value
};
};
//process the axis movement.
var processAxis = function(motionAxis, data) {
//every motion will have a dirrection and a value
var motionValue = correctData(motionAxis, data),
lastPosition = outputSmoothing.readLastPosition(motionAxis.name);
//check if the values are within variance
if (dsutilities.isWithinVariance(lastPosition, motionValue.value, varianceThreshhold)) {
motionValue.value = outputSmoothing.smooth(motionAxis.name, motionValue.value);
// Don't assign motionValue directly to controller[motionAxis.name],
// this will break the reference.
if (controller[motionAxis.name]) {
controller[motionAxis.name].value = motionValue.value;
controller[motionAxis.name].direction = motionValue.direction;
}
motionAxis.eventPrefixes.forEach(function(eventPrefix) {
controller.emit(eventPrefix + ':motion', motionValue);
});
}
};
// Public methods
//process all configured motion inputs.
this.process = function(data) {
for (var i = 0; i < motionInputs.length; i++) {
processAxis(motionInputs[i], data);
}
};
};
module.exports = motionProcessor;

View file

@ -0,0 +1,50 @@
// Module dependencies.
//smooths data with moving average
var outputSmoothing = function(smoothInput) {
'use strict';
var buffer = {},
maxFrames = 5;
// Public methods
this.readLastPosition = function(motionAxisName) {
var axisBuffer = buffer[motionAxisName];
return axisBuffer ? axisBuffer[axisBuffer.length - 1] : null;
};
this.addToBuffer = function(motionAxisName, value) {
if (buffer[motionAxisName]) {
//add the current value to the buffer
buffer[motionAxisName].push(value);
//remove the head of the buffer
if (buffer[motionAxisName].length > maxFrames) {
buffer[motionAxisName].shift();
}
} else {
//create an array with the value.
buffer[motionAxisName] = [value];
}
};
//smooth using a moving average.
this.smooth = function(motionAxisName, value) {
this.addToBuffer(motionAxisName, value);
var axisBuffer = buffer[motionAxisName],
sum = 0,
smoothedVal = value;
if (smoothInput) {
for (var i = 0; i < axisBuffer.length; i++) {
sum += axisBuffer[i];
}
smoothedVal = Math.floor(sum / axisBuffer.length);
}
return smoothedVal;
};
};
module.exports = outputSmoothing;

36
dist/dualshock-controller/src/status.js vendored Normal file
View file

@ -0,0 +1,36 @@
'use strict';
// Module dependencies.
var config = require('./config');
//Proccess button events.
var Status = function(controller) {
var buffer = {},
status = config.getControllerConfig().status;
var processControllerStatus = function(category, data) {
var state;
for (var i = 0; i < category.states.length; i++) {
if (data[category.pin] === category.states[i].value) {
state = category.states[i].state;
}
}
if (buffer[category.name] !== state) {
if (controller[category.name]) {
controller[category.name].state = state;
}
controller.emit(category.name + ':change', state);
}
buffer[category.name] = state;
};
this.process = function(data) {
for (var i = 0; i < status.length; i++) {
processControllerStatus(status[i], data);
}
};
};
module.exports = Status;

View file

@ -0,0 +1,52 @@
const config = require('./config');
function genpBufferFromConf(tpAxis) {
return {
name: tpAxis.name,
active: false,
data: {
x: 0,
y: 0
}
};
}
module.exports = function TouchPad(controller) {
const touchPad = config.getControllerConfig().touchPad;
let pBuffer = {};
function processIsActive(buffer, tpAxis) {
const active = buffer[tpAxis.activePin] < 128;
const axisBuffer = pBuffer[tpAxis.name];
const evt = active ? 'active' : 'inactive';
if (active !== axisBuffer.active) {
controller.emit(`touchpad:${tpAxis.name}:${evt}`);
}
axisBuffer.active = active;
}
function processData(buffer, tpAxis) {
const axisBuffer = pBuffer[tpAxis.name];
if (axisBuffer.active) {
axisBuffer.data.x = ((buffer[tpAxis.dataPinA] & 15) << 8 | buffer[tpAxis.dataPinB]);
axisBuffer.data.y = buffer[tpAxis.dataPinC] << 4 | ((buffer[tpAxis.dataPinA] & 240) >> 4);
controller.emit(`touchpad:${tpAxis.name}`, axisBuffer.data);
}
}
this.process = function process(buffer) {
for (let i = 0; i < touchPad.length; i++) {
//if we have not built a pBuffer profile for this axis lets build it.
if (!pBuffer[touchPad[i].name]) {
pBuffer[touchPad[i].name] = genpBufferFromConf(touchPad[i]);
}
processIsActive(buffer, touchPad[i]);
processData(buffer, touchPad[i]);
}
};
};

View file

@ -0,0 +1,34 @@
'use strict';
// Module dependencies.
var config = require('./config');
var unique = function unique(x) {
var result = [];
for (var i = 0; i < x.length; i++) {
if ((result.indexOf(x[i]) < 0)) {
result.push(x[i]);
}
}
return result;
};
//provide a few utility functions.
module.exports = {
//reduces noise from the controller
isWithinVariance: function(x, y, varianceThreshhold) {
return Math.abs(x - y) > varianceThreshhold;
},
warn: function(message) {
if (config.getOptions().logging) {
console.log(message);
}
},
generateEventPrefixAliases: function(eventPrefix) {
return unique([
eventPrefix,
eventPrefix.toLowerCase()
]);
}
};

View file

@ -0,0 +1,84 @@
// Module dependencies.
var Analogs = require('../src/analogs'),
assert = require('assert'),
sinon = require('sinon'),
EventEmitter = require('events').EventEmitter,
config = require('../src/config');
describe('the Analogs component', function() {
'use strict';
var mockConfig = [{
"name": "analog",
"x": 0,
"y": 1
}],
instance = [{
name: 'process'
}],
dataA = [50, 65],
dataB = [0, 0],
analogs,
emitter,
spy;
beforeEach(function() {
emitter = new EventEmitter();
config.setOptions({
analogStickSmoothing: false
});
config.setControllerConfig({
analogSticks: mockConfig
});
analogs = new Analogs(emitter);
spy = new sinon.spy();
});
describe('object instance', function() {
it('should have the following shape', function() {
//make sure we find these functions.
instance.forEach(function(method) {
assert.equal(typeof analogs[method.name], 'function');
});
});
});
describe('move events', function() {
it('should invoke the move event', function() {
emitter.on('analog:move', spy);
analogs.process(dataA);
assert.equal(spy.called, true);
});
it('should not invoke the move event', function() {
emitter.on('analog:move', spy);
analogs.process(dataB);
assert.equal(spy.called, false);
});
it('should invoke the move event with zero', function() {
analogs.process(dataA);
emitter.on('analog:move', spy);
analogs.process(dataB);
assert.equal(spy.called, true);
});
});
describe('return values', function() {
it('should return the analog values', function() {
emitter.on('analog:move', spy);
analogs.process(dataA);
var expectedValue = {
x: 50,
y: 65
},
spyArgument = spy.args[0][0];
assert.equal(expectedValue.x, spyArgument.x);
assert.equal(expectedValue.y, spyArgument.y);
assert.equal(spy.called, true);
});
});
});

View file

@ -0,0 +1,178 @@
// Module dependencies.
var Buttons = require('../src/buttons'),
assert = require('assert'),
sinon = require('sinon'),
EventEmitter = require('events').EventEmitter,
config = require('../src/config');
describe('the Buttons component', function() {
'use strict';
var mockConfig = [{
"name": "buttonName",
"buttonBlock": 0,
"buttonValue": "0x08",
"analogPin": 1
}, {
"name": "dpadUp",
"buttonBlock": 5,
"buttonValue": "0x00",
"mask": "0xF"
}, {
"name": "dpadDown",
"buttonBlock": 5,
"buttonValue": "0x01",
"mask": "0xF"
}],
instance = [{
name: 'process'
}],
dataA = [8, 170],
dataB = [0, 0],
buttons,
emitter,
spy,
spyLowerCaseEvents;
beforeEach(function() {
emitter = new EventEmitter();
config.setControllerConfig({
buttons: mockConfig
});
buttons = new Buttons(emitter);
spy = new sinon.spy();
spyLowerCaseEvents = new sinon.spy();
});
describe('object instance', function() {
it('should have the following shape', function() {
//make sure we find these functions.
instance.forEach(function(method) {
assert.equal(typeof buttons[method.name], 'function');
});
});
});
describe('press events', function() {
it('should envoke the buttonName:press', function() {
emitter.on('buttonName:press', spy);
emitter.on('buttonname:press', spyLowerCaseEvents);
buttons.process(dataA);
assert.equal(spy.called, true);
assert.equal(spyLowerCaseEvents.called, true);
});
it('should not envoke the buttonName:press', function() {
emitter.on('buttonName:release', spy);
emitter.on('buttonname:release', spyLowerCaseEvents);
buttons.process(dataB);
assert.equal(spy.called, false);
assert.equal(spyLowerCaseEvents.called, false);
});
});
describe('release events', function() {
it('should envoke the buttonName:release', function() {
emitter.on('buttonName:release', spy);
emitter.on('buttonname:release', spyLowerCaseEvents);
buttons.process(dataA);
buttons.process(dataB);
assert.equal(spy.called, true);
assert.equal(spyLowerCaseEvents.called, true);
});
it('should not envoke the buttonName:release', function() {
emitter.on('buttonName:release', spy);
emitter.on('buttonname:release', spyLowerCaseEvents);
buttons.process(dataA);
assert.equal(spy.called, false);
assert.equal(spyLowerCaseEvents.called, false);
});
});
describe('button hold', function() {
it('should raise the hold event', function() {
emitter.on('buttonName:hold', spy);
emitter.on('buttonname:hold', spyLowerCaseEvents);
buttons.process(dataA);
buttons.process(dataA);
assert.equal(spy.args[0][0], 'buttonName');
assert.equal(spy.called, true);
assert.equal(spyLowerCaseEvents.called, true);
});
it('should not raise the hold event', function() {
emitter.on('buttonName:hold', spy);
emitter.on('buttonname:hold', spyLowerCaseEvents);
buttons.process(dataB);
buttons.process(dataB);
assert.equal(spy.called, false);
assert.equal(spyLowerCaseEvents.called, false);
});
});
describe('button analog', function() {
it('should raise the analog event', function() {
emitter.on('buttonName:analog', spy);
emitter.on('buttonname:analog', spyLowerCaseEvents);
buttons.process(dataA);
assert.equal(spy.args[0][0], dataA[1]);
assert.equal(spy.called, true);
assert.equal(spyLowerCaseEvents.called, true);
});
it('should not raise the analog event', function() {
emitter.on('buttonName:analog', spy);
emitter.on('buttonname:analog', spyLowerCaseEvents);
buttons.process(dataB);
assert.equal(spy.called, false);
assert.equal(spyLowerCaseEvents.called, false);
});
});
describe('ps4 dpad up button', function() {
it('should emit the dpadUp:press event', function() {
emitter.on('dpadUp:press', spy);
emitter.on('dpadup:press', spyLowerCaseEvents);
buttons.process([0, 0, 0, 0, 0, 0]);
assert.equal(spy.called, true);
assert.equal(spyLowerCaseEvents.called, true);
});
it('should not emit the dpadDown:press event', function() {
emitter.on('dpadDown:press', spy);
emitter.on('dpaddown:press', spyLowerCaseEvents);
buttons.process([0, 0, 0, 0, 0, 0]);
assert.equal(spy.called, false);
assert.equal(spyLowerCaseEvents.called, false);
});
});
describe('ps4 dpad down button', function() {
it('should emit the dpadDown:press event', function() {
emitter.on('dpadDown:press', spy);
emitter.on('dpaddown:press', spyLowerCaseEvents);
buttons.process([0, 0, 0, 0, 0, parseInt("00001001", 2)]);
assert.equal(spy.called, true);
assert.equal(spyLowerCaseEvents.called, true);
});
it('should not emit the dpadUp:press event', function() {
emitter.on('dpadUp:press', spy);
emitter.on('dpadup:press', spyLowerCaseEvents);
buttons.process([0, 0, 0, 0, 0, parseInt("00001001", 2)]);
assert.equal(spy.called, false);
assert.equal(spyLowerCaseEvents.called, false);
});
});
});

View file

@ -0,0 +1,91 @@
'use strict';
var assert = require('assert');
describe('The Config component', function() {
var mockConfig = {
vendorId: 1556,
productId: 616,
output: []
},
mockOptions = {
config: 'dualShock3',
accelerometerSmoothing: true,
logging: false
},
instance = [{
name: 'setOptions'
}, {
name: 'getOptions'
}, {
name: 'setControllerConfig'
}, {
name: 'getControllerConfig'
}],
defaultOptionsInstance = [{
name: 'config'
}, {
name: 'accelerometerSmoothing'
}, {
name: 'analogStickSmoothing'
}],
configA,
configB;
beforeEach(function() {
configA = require('../src/config');
configB = require('../src/config');
});
describe('object instance', function() {
it('should have the following shape', function() {
instance.forEach(function(method) {
assert.equal(typeof configA[method.name], 'function');
});
});
});
describe('option methods', function() {
it('should be able to save options', function() {
configA.setOptions(mockOptions);
assert.equal(configA.getOptions(), mockOptions);
});
it('should provide a single object accross instances', function() {
configA.setOptions(mockOptions);
assert.equal(configA.getOptions(), configB.getOptions());
});
});
describe('controllerConfig methods', function() {
it('should be able to save controllerConfig settings', function() {
configA.setControllerConfig(mockConfig);
//change the object
mockConfig.vendorId = 22;
assert.equal(configA.getControllerConfig(), mockConfig);
});
it('should provide a single object accross instances', function() {
configA.setControllerConfig(mockConfig);
assert.equal(configA.getControllerConfig(), configB.getControllerConfig());
});
});
describe('default values', function() {
beforeEach(function() {
configA.setOptions();
});
it('should apply default values', function() {
var ops = configA.getOptions();
defaultOptionsInstance.forEach(function(property) {
assert.notEqual(ops[property.name], void 0);
});
});
it('should load default config', function() {
var controllerConfig = configA.getControllerConfig();
assert.notEqual(controllerConfig, null);
assert.notEqual(controllerConfig, void 0);
});
});
});

View file

@ -0,0 +1,158 @@
var assert = require('assert'),
Emitter = require('events').EventEmitter,
config = require('../src/config'),
mockery = require('mockery');
function Device() {
Emitter.call(this);
}
Device.prototype = Object.create(Emitter.prototype, {
constructor: {
value: Device
},
close: {
value: function() {}
}
});
var config = {
analogSticks: [{
name: 'foo',
x: 0,
y: 1
}],
buttons: [{
name: 'bar',
buttonBlock: 0,
buttonValue: 0x08,
analogPin: 1
}],
motionInputs: [{
name: 'baz',
directionPin: 0,
valuePin: 1
}],
status: [{
name: 'quz',
pin: 5,
states: [{
value: 0,
state: 'Charging'
}, {
value: 2,
state: 'Charging'
}, {
value: 3,
state: '40%'
}]
}],
output: []
};
var analogs = config.analogSticks;
var buttons = config.buttons;
var motions = config.motionInputs;
var status = config.status;
describe('the DualShock component', function() {
//enable mockery and mock node-hid:
mockery.enable();
var nodeHidMock = {
HID: function(vendor, productId) {
return {
on: function() {
//could use this at some point. nothing atm.
}
};
}
};
//register mock node-hid.
mockery.registerMock('node-hid', nodeHidMock);
//once mockery is up we can require the dualshock module.
var DualShock = require('./../src/dualshock.js'),
controller, device;
before(function() {
device = new Device();
controller = DualShock({
config: config,
device: device
});
});
//disable mockery so it does not interfere with other tests.
after(function() {
mockery.deregisterMock('node-hid');
mockery.disable();
});
beforeEach(function() {
controller.removeAllListeners(Object.keys(controller._events));
});
describe('analog properties', function() {
analogs.forEach(function(analog) {
it(analog.name, function() {
assert.equal(controller[analog.name].x, 0);
assert.equal(controller[analog.name].y, 0);
device.emit('data', [100, 100]);
assert.equal(controller[analog.name].x, 100);
assert.equal(controller[analog.name].y, 100);
});
});
});
describe('button properties', function() {
buttons.forEach(function(button) {
it(button.name, function() {
assert.equal(controller[button.name].value, 0);
assert.equal(controller[button.name].state, 0);
device.emit('data', [8, 170]);
assert.equal(controller[button.name].value, 1);
assert.equal(controller[button.name].state, 1);
device.emit('data', [8, 170]);
assert.equal(controller[button.name].value, 1);
assert.equal(controller[button.name].state, 2);
device.emit('data', [0, 0]);
assert.equal(controller[button.name].value, 0);
assert.equal(controller[button.name].state, 0);
});
});
});
describe('motion properties', function() {
motions.forEach(function(motion) {
it(motion.name, function() {
assert.equal(controller[motion.name].value, 0);
assert.equal(controller[motion.name].direction, 0);
device.emit('data', [1, 233]);
assert.equal(controller[motion.name].value, 22);
assert.equal(controller[motion.name].direction, 1);
});
});
});
describe('status properties', function() {
status.forEach(function(stat) {
it(stat.name, function() {
device.emit('data', [0, 0, 0, 0, 1, 3]);
assert.equal(controller[stat.name].state, '40%');
device.emit('data', [0, 0, 0, 0, 1, 2]);
assert.equal(controller[stat.name].state, 'Charging');
});
});
});
});

View file

@ -0,0 +1,78 @@
// Module dependencies.
var Gyro = require('../src/gyro'),
assert = require('assert'),
sinon = require('sinon'),
EventEmitter = require('events').EventEmitter,
config = require('../src/config');
describe('the Gyro component', function() {
'use strict';
var mockConfig = [{
name: "dirrection",
directionPin: 0,
valuePin: 1
}],
instance = [{
name: 'process'
}],
dataA = [1, 130],
dataB = [2, 130],
emitter,
spy,
gyro;
beforeEach(function() {
emitter = new EventEmitter();
spy = sinon.spy();
config.setOptions({
accelerometerSmoothing: false
});
config.setControllerConfig({
motionInputs: mockConfig
});
gyro = new Gyro(emitter);
});
describe('object instance', function() {
it('should have the following shape', function() {
//make sure we find these functions.
instance.forEach(function(method) {
assert.equal(typeof gyro[method.name], 'function');
});
});
});
describe('process()', function() {
it('should envoke the dirrection:motion event with positive values', function() {
emitter.on('dirrection:motion', spy);
gyro.process(dataA);
assert.equal(spy.called, true);
var expectedValue = {
direction: 1,
value: 125
},
spyArgument = spy.args[0][0];
assert.equal(spyArgument.direction, expectedValue.direction);
assert.equal(spyArgument.value, expectedValue.value);
});
it('should envoke the dirrection event with negative values.', function() {
emitter.on('dirrection:motion', spy);
gyro.process(dataB);
assert.equal(spy.called, true);
var expectedValue = {
direction: 2,
value: -130
},
spyArgument = spy.args[0][0];
assert.equal(spyArgument.direction, expectedValue.direction);
assert.equal(spyArgument.value, expectedValue.value);
});
});
});

View file

@ -0,0 +1,71 @@
// Module dependencies.
var Smoothing = require('../src/smoothing'),
assert = require('assert');
describe('the smoothing component', function() {
'use strict';
var smoothing,
nonSmoothing,
//an instance should have the following functions.
instance = [{
name: 'readLastPosition'
}, {
name: 'addToBuffer'
}, {
name: 'smooth'
}];
beforeEach(function() {
smoothing = new Smoothing(true);
nonSmoothing = new Smoothing(false);
for (var i = 0; i < 5; i++) {
nonSmoothing.addToBuffer('testNonSmoothing', i);
smoothing.addToBuffer('one', i);
smoothing.addToBuffer('two', i + 1);
smoothing.addToBuffer('testSmoothing', i);
}
});
describe('object instance', function() {
it('should have the following shape', function() {
//make sure we find these functions.
instance.forEach(function(method) {
assert.equal(typeof smoothing[method.name], 'function');
});
});
});
describe('addToBuffer()', function() {
it('should add values to the buffer', function() {
smoothing.addToBuffer('one', 6);
smoothing.addToBuffer('two', 7);
assert.equal(smoothing.readLastPosition('one'), 6);
assert.equal(smoothing.readLastPosition('two'), 7);
});
});
describe('readLastPosition()', function() {
it('should handle buffers for different objects', function() {
assert.equal(smoothing.readLastPosition('one'), 4);
assert.equal(smoothing.readLastPosition('two'), 5);
});
});
describe('smooth()', function() {
it('should return expected values when smoothing', function() {
//with the data set smoothing of 6 should be 3.
assert.equal(smoothing.smooth('testSmoothing', 6), 3);
//with the data set smoothing of 8 should be 4.
assert.equal(smoothing.smooth('testSmoothing', 8), 4);
});
it('should return expected values when not smoothing', function() {
//with smoothing turned off 9 should return 9
assert.equal(nonSmoothing.smooth('testNonSmoothing', 9), 9);
//with smoothing turned off 6 should return 6
assert.equal(nonSmoothing.smooth('testNonSmoothing', 6), 6);
});
});
});

View file

@ -0,0 +1,60 @@
// Module dependencies.
var Status = require('../src/status'),
assert = require('assert'),
sinon = require('sinon'),
EventEmitter = require('events').EventEmitter,
config = require('../src/config');
describe('the status component', function() {
var mockConfig = [{
"name": "chargingState",
"pin": 0,
"states": [{
"value": 0,
"state": "Charging"
}, {
"value": 2,
"state": "Charging"
}, {
"value": 3,
"state": "40%"
}]
}],
dataA = [0, 0],
dataB = [0, 3],
instance = [{
name: 'process'
}],
status,
emitter,
spy;
beforeEach(function() {
emitter = new EventEmitter();
spy = sinon.spy();
config.setControllerConfig({
status: mockConfig
});
status = new Status(emitter, mockConfig);
});
describe('object instance', function() {
it('should have the following shape', function() {
//make sure we find these functions.
instance.forEach(function(method) {
assert.equal(typeof status[method.name], 'function');
});
});
});
describe('process()', function() {
it('process should return an object with the expected values', function() {
emitter.on('chargingState:change', spy);
status.process(dataA);
var spyArgument = spy.args[0][0];
assert.equal(typeof spyArgument, 'string');
assert.equal(spyArgument, 'Charging');
spyArgument = null;
});
});
});

View file

@ -0,0 +1,40 @@
//Only to be used for PS4 (Dualshock 4) controllers
var dualShock = require('../../dist/dualshock-controller/src/dualshock');
try {
var controller = dualShock({
config: "dualshock4-generic-driver"
});
controller.on('error', err => console.log(err));
function setExtras(obj) {
controller.setExtras(obj);
}
var connected = false;
controller.on('connected', () => {
connected = true;
});
controller.on("disconnected", () => {
connected = false;
})
function isConnected() {
return connected;
}
function on(event, handler) {
controller.on(event, handler);
}
module.exports = {
on,
isConnected,
setExtras
}
} catch (e) {
console.error(e);
module.exports = {
isConnected: ()=>{return false}
}
}

View file

@ -2,6 +2,7 @@ const utils = require("../../utils");
const gui = require("./gui");
const Gamepad = require("../../gamepad/gamepad");
const gamepad = new Gamepad;
const controller = require("../../gamepad/ps4controller");
function gamepadConnected(){
console.log("Gamepad connected");
@ -18,6 +19,15 @@ function gamepadConnected(){
gui.renderUserCardList(cards);
var selected = 0;
if(controller.isConnected()){
controller.setExtras({
rumbleLeft: 0, // 0-255 (Rumble left intensity)
rumbleRight: 0, // 0-255 (Rumble right intensity)
red: 0, // 0-255 (Red intensity)
green: 75, // 0-255 (Blue intensity)
blue: 225, // 0-255 (Green intensity)
});
}
gamepad.on("press", "d_pad_right", () => {
if(selected < max){
selected++;

358
package-lock.json generated
View file

@ -93,8 +93,7 @@
"ansi-regex": {
"version": "2.1.1",
"resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-2.1.1.tgz",
"integrity": "sha1-w7M6te42DYbg5ijwRorn7yfWVN8=",
"dev": true
"integrity": "sha1-w7M6te42DYbg5ijwRorn7yfWVN8="
},
"ansi-styles": {
"version": "3.2.1",
@ -113,6 +112,54 @@
"picomatch": "^2.0.4"
}
},
"aproba": {
"version": "1.2.0",
"resolved": "https://registry.npmjs.org/aproba/-/aproba-1.2.0.tgz",
"integrity": "sha512-Y9J6ZjXtoYh8RnXVCMOU/ttDmk1aBjunq9vO0ta5x85WDQiQfUF9sIPBITdbiiIVcBo03Hi3jMxigBtsddlXRw=="
},
"are-we-there-yet": {
"version": "1.1.5",
"resolved": "https://registry.npmjs.org/are-we-there-yet/-/are-we-there-yet-1.1.5.tgz",
"integrity": "sha512-5hYdAkZlcG8tOLujVDTgCT+uPX0VnpAH28gWsLfzpXYm7wP6mp5Q/gYyR7YQ0cKVJcXJnl3j2kpBan13PtQf6w==",
"requires": {
"delegates": "^1.0.0",
"readable-stream": "^2.0.6"
},
"dependencies": {
"isarray": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz",
"integrity": "sha1-u5NdSFgsuhaMBoNJV6VKPgcSTxE="
},
"readable-stream": {
"version": "2.3.6",
"resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.6.tgz",
"integrity": "sha512-tQtKA9WIAhBF3+VLAseyMqZeBjW0AHJoxOtYqSUZNJxauErmLbVm2FW1y+J/YA9dUrAC39ITejlZWhVIwawkKw==",
"requires": {
"core-util-is": "~1.0.0",
"inherits": "~2.0.3",
"isarray": "~1.0.0",
"process-nextick-args": "~2.0.0",
"safe-buffer": "~5.1.1",
"string_decoder": "~1.1.1",
"util-deprecate": "~1.0.1"
}
},
"safe-buffer": {
"version": "5.1.2",
"resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz",
"integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g=="
},
"string_decoder": {
"version": "1.1.1",
"resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz",
"integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==",
"requires": {
"safe-buffer": "~5.1.0"
}
}
}
},
"array-back": {
"version": "1.0.4",
"resolved": "https://registry.npmjs.org/array-back/-/array-back-1.0.4.tgz",
@ -207,6 +254,42 @@
"resolved": "https://registry.npmjs.org/binary-extensions/-/binary-extensions-2.0.0.tgz",
"integrity": "sha512-Phlt0plgpIIBOGTT/ehfFnbNlfsDEiqmzE2KRXoX1bLIlir4X/MR+zSyBEkL05ffWgnRSf/DXv+WrUAVr93/ow=="
},
"bindings": {
"version": "1.5.0",
"resolved": "https://registry.npmjs.org/bindings/-/bindings-1.5.0.tgz",
"integrity": "sha512-p2q/t/mhvuOj/UeLlV6566GD/guowlr0hHxClI0W9m7MWYkL1F0hLo+0Aexs9HSPCtR1SXQ0TD3MMKrXZajbiQ==",
"requires": {
"file-uri-to-path": "1.0.0"
}
},
"bl": {
"version": "3.0.0",
"resolved": "https://registry.npmjs.org/bl/-/bl-3.0.0.tgz",
"integrity": "sha512-EUAyP5UHU5hxF8BPT0LKW8gjYLhq1DQIcneOX/pL/m2Alo+OYDQAJlHq+yseMP50Os2nHXOSic6Ss3vSQeyf4A==",
"requires": {
"readable-stream": "^3.0.1"
},
"dependencies": {
"readable-stream": {
"version": "3.4.0",
"resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.4.0.tgz",
"integrity": "sha512-jItXPLmrSR8jmTRmRWJXCnGJsfy85mB3Wd/uINMXA65yrnFo0cPClFIUWzo2najVNSl+mx7/4W8ttlLWJe99pQ==",
"requires": {
"inherits": "^2.0.3",
"string_decoder": "^1.1.1",
"util-deprecate": "^1.0.1"
}
},
"string_decoder": {
"version": "1.3.0",
"resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.3.0.tgz",
"integrity": "sha512-hkRX8U1WjJFd8LsDJ2yQ/wWWxaopEsABU1XfkM8A+j0+85JAGppt16cr1Whg6KIbb4okU6Mql6BOj+uup/wKeA==",
"requires": {
"safe-buffer": "~5.2.0"
}
}
}
},
"bluebird": {
"version": "3.7.2",
"resolved": "https://registry.npmjs.org/bluebird/-/bluebird-3.7.2.tgz",
@ -345,6 +428,11 @@
"readdirp": "~3.3.0"
}
},
"chownr": {
"version": "1.1.3",
"resolved": "https://registry.npmjs.org/chownr/-/chownr-1.1.3.tgz",
"integrity": "sha512-i70fVHhmV3DtTl6nqvZOnIjbY0Pe4kAUjwHj8z0zAdgBtYrJyYwLKCCuRBQ5ppkyL0AkN7HKRnETdmdp1zqNXw=="
},
"chromium-pickle-js": {
"version": "0.2.0",
"resolved": "https://registry.npmjs.org/chromium-pickle-js/-/chromium-pickle-js-0.2.0.tgz",
@ -363,8 +451,7 @@
"code-point-at": {
"version": "1.1.0",
"resolved": "https://registry.npmjs.org/code-point-at/-/code-point-at-1.1.0.tgz",
"integrity": "sha1-DQcLTQQ6W+ozovGkDi7bPZpMz3c=",
"dev": true
"integrity": "sha1-DQcLTQQ6W+ozovGkDi7bPZpMz3c="
},
"color-convert": {
"version": "1.9.3",
@ -499,6 +586,11 @@
"proto-list": "~1.2.1"
}
},
"console-control-strings": {
"version": "1.1.0",
"resolved": "https://registry.npmjs.org/console-control-strings/-/console-control-strings-1.1.0.tgz",
"integrity": "sha1-PXz0Rk22RG6mRL9LOVB/mFEAjo4="
},
"core-js": {
"version": "3.6.1",
"resolved": "https://registry.npmjs.org/core-js/-/core-js-3.6.1.tgz",
@ -509,8 +601,7 @@
"core-util-is": {
"version": "1.0.2",
"resolved": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.2.tgz",
"integrity": "sha1-tf1UIgqivFq1eqtxQMlAdUUDwac=",
"dev": true
"integrity": "sha1-tf1UIgqivFq1eqtxQMlAdUUDwac="
},
"cross-zip": {
"version": "2.1.6",
@ -616,6 +707,16 @@
"integrity": "sha1-3zrhmayt+31ECqrgsp4icrJOxhk=",
"dev": true
},
"delegates": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/delegates/-/delegates-1.0.0.tgz",
"integrity": "sha1-hMbhWbgZBP3KWaDvRM2HDTElD5o="
},
"detect-libc": {
"version": "1.0.3",
"resolved": "https://registry.npmjs.org/detect-libc/-/detect-libc-1.0.3.tgz",
"integrity": "sha1-+hN8S9aY7fVc1c0CrFWfkaTEups="
},
"detect-node": {
"version": "2.0.4",
"resolved": "https://registry.npmjs.org/detect-node/-/detect-node-2.0.4.tgz",
@ -802,7 +903,6 @@
"version": "1.4.4",
"resolved": "https://registry.npmjs.org/end-of-stream/-/end-of-stream-1.4.4.tgz",
"integrity": "sha512-+uw1inIHVPQoaVuHzRyXd21icM+cnt4CzD5rW+NC1wjOUSTOs+Te7FOv7AhN7vS9x/oIyhLP5PR1H+phQAHu5Q==",
"dev": true,
"requires": {
"once": "^1.4.0"
}
@ -834,6 +934,11 @@
"resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz",
"integrity": "sha1-G2HAViGQqN/2rjuyzwIAyhMLhtQ="
},
"expand-template": {
"version": "2.0.3",
"resolved": "https://registry.npmjs.org/expand-template/-/expand-template-2.0.3.tgz",
"integrity": "sha512-XYfuKMvj4O35f/pOXLObndIRvyQ+/+6AhODh+OKWj9S9498pHHn/IMszH+gt0fBCRWMNfk1ZSp5x3AifmnI2vg=="
},
"extend": {
"version": "3.0.2",
"resolved": "https://registry.npmjs.org/extend/-/extend-3.0.2.tgz",
@ -904,6 +1009,11 @@
"array-back": "^1.0.4"
}
},
"file-uri-to-path": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/file-uri-to-path/-/file-uri-to-path-1.0.0.tgz",
"integrity": "sha512-0Zt+s3L7Vf1biwWZ29aARiVYLx7iMGnEUl9x33fbB/j3jR81u/O2LbqK+Bm1CDSNDKVtJ/YjwY7TUd5SkeLQLw=="
},
"fill-range": {
"version": "7.0.1",
"resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.0.1.tgz",
@ -991,6 +1101,11 @@
"mime-types": "^2.1.12"
}
},
"fs-constants": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/fs-constants/-/fs-constants-1.0.0.tgz",
"integrity": "sha512-y6OAwoSIf7FyjMIv94u+b5rdheZEjzR63GTyZJm5qh4Bi+2YgwLCcI/fPFZkL5PSixOt6ZNKm+w+Hfp/Bciwow=="
},
"fs-extra": {
"version": "4.0.3",
"resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-4.0.3.tgz",
@ -1025,6 +1140,21 @@
"fs-extra": "^4.0.0"
}
},
"gauge": {
"version": "2.7.4",
"resolved": "https://registry.npmjs.org/gauge/-/gauge-2.7.4.tgz",
"integrity": "sha1-LANAXHU4w51+s3sxcCLjJfsBi/c=",
"requires": {
"aproba": "^1.0.3",
"console-control-strings": "^1.0.0",
"has-unicode": "^2.0.0",
"object-assign": "^4.1.0",
"signal-exit": "^3.0.0",
"string-width": "^1.0.1",
"strip-ansi": "^3.0.1",
"wide-align": "^1.1.0"
}
},
"get-package-info": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/get-package-info/-/get-package-info-1.0.0.tgz",
@ -1135,6 +1265,11 @@
"assert-plus": "^1.0.0"
}
},
"github-from-package": {
"version": "0.0.0",
"resolved": "https://registry.npmjs.org/github-from-package/-/github-from-package-0.0.0.tgz",
"integrity": "sha1-l/tdlr/eiXMxPyDoKI75oWf6ZM4="
},
"glob": {
"version": "7.1.6",
"resolved": "https://registry.npmjs.org/glob/-/glob-7.1.6.tgz",
@ -1251,6 +1386,11 @@
"resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz",
"integrity": "sha1-tdRU3CGZriJWmfNGfloH87lVuv0="
},
"has-unicode": {
"version": "2.0.1",
"resolved": "https://registry.npmjs.org/has-unicode/-/has-unicode-2.0.1.tgz",
"integrity": "sha1-4Ob+aijPUROIVeCG0Wkedx3iqLk="
},
"hosted-git-info": {
"version": "2.8.5",
"resolved": "https://registry.npmjs.org/hosted-git-info/-/hosted-git-info-2.8.5.tgz",
@ -1296,14 +1436,12 @@
"inherits": {
"version": "2.0.4",
"resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz",
"integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==",
"dev": true
"integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ=="
},
"ini": {
"version": "1.3.5",
"resolved": "https://registry.npmjs.org/ini/-/ini-1.3.5.tgz",
"integrity": "sha512-RZY5huIKCMRWDUqZlEi72f/lmXKMvuszcMBduliQ3nnWbx9X/ZBQO7DijMEYS9EhHBb2qacRUMtC7svLwe0lcw==",
"dev": true
"integrity": "sha512-RZY5huIKCMRWDUqZlEi72f/lmXKMvuszcMBduliQ3nnWbx9X/ZBQO7DijMEYS9EhHBb2qacRUMtC7svLwe0lcw=="
},
"is-arrayish": {
"version": "0.2.1",
@ -1337,7 +1475,6 @@
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-1.0.0.tgz",
"integrity": "sha1-754xOG8DGn8NZDr4L95QxFfvAMs=",
"dev": true,
"requires": {
"number-is-nan": "^1.0.0"
}
@ -1587,14 +1724,12 @@
"minimist": {
"version": "1.2.0",
"resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.0.tgz",
"integrity": "sha1-o1AIsg9BOD7sH7kU9M1d95omQoQ=",
"dev": true
"integrity": "sha1-o1AIsg9BOD7sH7kU9M1d95omQoQ="
},
"mkdirp": {
"version": "0.5.1",
"resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.1.tgz",
"integrity": "sha1-MAV0OOrGz3+MR2fzhkjWaX11yQM=",
"dev": true,
"requires": {
"minimist": "0.0.8"
},
@ -1602,8 +1737,7 @@
"minimist": {
"version": "0.0.8",
"resolved": "https://registry.npmjs.org/minimist/-/minimist-0.0.8.tgz",
"integrity": "sha1-hX/Kv8M5fSYluCKCYuhqp6ARsF0=",
"dev": true
"integrity": "sha1-hX/Kv8M5fSYluCKCYuhqp6ARsF0="
}
}
},
@ -1613,6 +1747,34 @@
"integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==",
"dev": true
},
"nan": {
"version": "2.14.0",
"resolved": "https://registry.npmjs.org/nan/-/nan-2.14.0.tgz",
"integrity": "sha512-INOFj37C7k3AfaNTtX8RhsTw7qRy7eLET14cROi9+5HAVbbHuIWUHEauBv5qT4Av2tWasiTY1Jw6puUNqRJXQg=="
},
"napi-build-utils": {
"version": "1.0.1",
"resolved": "https://registry.npmjs.org/napi-build-utils/-/napi-build-utils-1.0.1.tgz",
"integrity": "sha512-boQj1WFgQH3v4clhu3mTNfP+vOBxorDlE8EKiMjUlLG3C4qAESnn9AxIOkFgTR2c9LtzNjPrjS60cT27ZKBhaA=="
},
"node-abi": {
"version": "2.13.0",
"resolved": "https://registry.npmjs.org/node-abi/-/node-abi-2.13.0.tgz",
"integrity": "sha512-9HrZGFVTR5SOu3PZAnAY2hLO36aW1wmA+FDsVkr85BTST32TLCA1H/AEcatVRAsWLyXS3bqUDYCAjq5/QGuSTA==",
"requires": {
"semver": "^5.4.1"
}
},
"node-hid": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/node-hid/-/node-hid-1.0.0.tgz",
"integrity": "sha512-KOkrN0nn82ffj30iYAmzJmif6h0/5r3NlG68NJQf8hxYpy3pzkr7TjjOL4fgoB4Ehc4YIdinP1F6pGOJqdModg==",
"requires": {
"bindings": "^1.5.0",
"nan": "^2.14.0",
"prebuild-install": "^5.3.3"
}
},
"node-wifi": {
"version": "2.0.12",
"resolved": "https://registry.npmjs.org/node-wifi/-/node-wifi-2.0.12.tgz",
@ -1622,6 +1784,11 @@
"command-line-usage": "^5.0.5"
}
},
"noop-logger": {
"version": "0.1.1",
"resolved": "https://registry.npmjs.org/noop-logger/-/noop-logger-0.1.1.tgz",
"integrity": "sha1-lKKxYzxPExdVMAfYlm/Q6EG2pMI="
},
"normalize-package-data": {
"version": "2.5.0",
"resolved": "https://registry.npmjs.org/normalize-package-data/-/normalize-package-data-2.5.0.tgz",
@ -1665,6 +1832,17 @@
}
}
},
"npmlog": {
"version": "4.1.2",
"resolved": "https://registry.npmjs.org/npmlog/-/npmlog-4.1.2.tgz",
"integrity": "sha512-2uUqazuKlTaSI/dC8AzicUck7+IrEaOnN/e0jd3Xtt1KcGpwx30v50mL7oPyr/h9bL3E4aZccVwpwP+5W9Vjkg==",
"requires": {
"are-we-there-yet": "~1.1.2",
"console-control-strings": "~1.1.0",
"gauge": "~2.7.3",
"set-blocking": "~2.0.0"
}
},
"nugget": {
"version": "2.0.1",
"resolved": "https://registry.npmjs.org/nugget/-/nugget-2.0.1.tgz",
@ -1700,8 +1878,7 @@
"number-is-nan": {
"version": "1.0.1",
"resolved": "https://registry.npmjs.org/number-is-nan/-/number-is-nan-1.0.1.tgz",
"integrity": "sha1-CXtgK1NCKlIsGvuHkDGDNpQaAR0=",
"dev": true
"integrity": "sha1-CXtgK1NCKlIsGvuHkDGDNpQaAR0="
},
"oauth-sign": {
"version": "0.9.0",
@ -1712,8 +1889,7 @@
"object-assign": {
"version": "4.1.1",
"resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz",
"integrity": "sha1-IQmtx5ZYh8/AXLvUQsrIv7s2CGM=",
"dev": true
"integrity": "sha1-IQmtx5ZYh8/AXLvUQsrIv7s2CGM="
},
"object-keys": {
"version": "0.4.0",
@ -1725,7 +1901,6 @@
"version": "1.4.0",
"resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz",
"integrity": "sha1-WDsap3WWHUsROsF9nFC6753Xa9E=",
"dev": true,
"requires": {
"wrappy": "1"
}
@ -1856,6 +2031,28 @@
"xmldom": "0.1.x"
}
},
"prebuild-install": {
"version": "5.3.3",
"resolved": "https://registry.npmjs.org/prebuild-install/-/prebuild-install-5.3.3.tgz",
"integrity": "sha512-GV+nsUXuPW2p8Zy7SarF/2W/oiK8bFQgJcncoJ0d7kRpekEA0ftChjfEaF9/Y+QJEc/wFR7RAEa8lYByuUIe2g==",
"requires": {
"detect-libc": "^1.0.3",
"expand-template": "^2.0.3",
"github-from-package": "0.0.0",
"minimist": "^1.2.0",
"mkdirp": "^0.5.1",
"napi-build-utils": "^1.0.1",
"node-abi": "^2.7.0",
"noop-logger": "^0.1.1",
"npmlog": "^4.0.1",
"pump": "^3.0.0",
"rc": "^1.2.7",
"simple-get": "^3.0.3",
"tar-fs": "^2.0.0",
"tunnel-agent": "^0.6.0",
"which-pm-runs": "^1.0.0"
}
},
"prepend-http": {
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/prepend-http/-/prepend-http-2.0.0.tgz",
@ -1875,8 +2072,7 @@
"process-nextick-args": {
"version": "2.0.1",
"resolved": "https://registry.npmjs.org/process-nextick-args/-/process-nextick-args-2.0.1.tgz",
"integrity": "sha512-3ouUOpQhtgrbOa17J7+uxOTpITYWaGP7/AhoR3+A+/1e9skrzelGi/dXzEYyvbxubEF6Wn2ypscTKiKJFFn1ag==",
"dev": true
"integrity": "sha512-3ouUOpQhtgrbOa17J7+uxOTpITYWaGP7/AhoR3+A+/1e9skrzelGi/dXzEYyvbxubEF6Wn2ypscTKiKJFFn1ag=="
},
"progress-stream": {
"version": "1.2.0",
@ -1905,7 +2101,6 @@
"version": "3.0.0",
"resolved": "https://registry.npmjs.org/pump/-/pump-3.0.0.tgz",
"integrity": "sha512-LwZy+p3SFs1Pytd/jYct4wpv49HiYCqd9Rlc5ZVdk0V+8Yzv6jR5Blk3TRmPL1ft69TxP0IMZGJ+WPFU2BFhww==",
"dev": true,
"requires": {
"end-of-stream": "^1.1.0",
"once": "^1.3.1"
@ -1927,7 +2122,6 @@
"version": "1.2.8",
"resolved": "https://registry.npmjs.org/rc/-/rc-1.2.8.tgz",
"integrity": "sha512-y3bGgqKj3QBdxLbLkomlohkvsA8gdAiUQlSBJnBhfn+BPxg4bc62d8TcBW15wavDfgexCgccckhcZvywyQYPOw==",
"dev": true,
"requires": {
"deep-extend": "^0.6.0",
"ini": "~1.3.0",
@ -2079,8 +2273,7 @@
"safe-buffer": {
"version": "5.2.0",
"resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.0.tgz",
"integrity": "sha512-fZEwUGbVl7kouZs1jCdMLdt95hdIv0ZeHg6L7qPeciMZhZ+/gdesW4wgTARkrFWEpspjEATAzUGPG8N2jJiwbg==",
"dev": true
"integrity": "sha512-fZEwUGbVl7kouZs1jCdMLdt95hdIv0ZeHg6L7qPeciMZhZ+/gdesW4wgTARkrFWEpspjEATAzUGPG8N2jJiwbg=="
},
"safer-buffer": {
"version": "2.1.2",
@ -2100,8 +2293,7 @@
"semver": {
"version": "5.7.1",
"resolved": "https://registry.npmjs.org/semver/-/semver-5.7.1.tgz",
"integrity": "sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ==",
"dev": true
"integrity": "sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ=="
},
"semver-compare": {
"version": "1.0.0",
@ -2120,11 +2312,45 @@
"type-fest": "^0.8.0"
}
},
"set-blocking": {
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/set-blocking/-/set-blocking-2.0.0.tgz",
"integrity": "sha1-BF+XgtARrppoA93TgrJDkrPYkPc="
},
"signal-exit": {
"version": "3.0.2",
"resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.2.tgz",
"integrity": "sha1-tf3AjxKH6hF4Yo5BXiUTK3NkbG0=",
"dev": true
"integrity": "sha1-tf3AjxKH6hF4Yo5BXiUTK3NkbG0="
},
"simple-concat": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/simple-concat/-/simple-concat-1.0.0.tgz",
"integrity": "sha1-c0TLuLbib7J9ZrL8hvn21Zl1IcY="
},
"simple-get": {
"version": "3.1.0",
"resolved": "https://registry.npmjs.org/simple-get/-/simple-get-3.1.0.tgz",
"integrity": "sha512-bCR6cP+aTdScaQCnQKbPKtJOKDp/hj9EDLJo3Nw4y1QksqaovlW/bnptB6/c1e+qmNIDHRK+oXFDdEqBT8WzUA==",
"requires": {
"decompress-response": "^4.2.0",
"once": "^1.3.1",
"simple-concat": "^1.0.0"
},
"dependencies": {
"decompress-response": {
"version": "4.2.1",
"resolved": "https://registry.npmjs.org/decompress-response/-/decompress-response-4.2.1.tgz",
"integrity": "sha512-jOSne2qbyE+/r8G1VU+G/82LBs2Fs4LAsTiLSHOCOMZQl2OKZ6i8i4IyHemTe+/yIXOtTcRQMzPcgyhoFlqPkw==",
"requires": {
"mimic-response": "^2.0.0"
}
},
"mimic-response": {
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/mimic-response/-/mimic-response-2.0.0.tgz",
"integrity": "sha512-8ilDoEapqA4uQ3TwS0jakGONKXVJqpy+RpM+3b7pLdOjghCrEiGp9SRkFbUHAmZW9vdnrENWHjaweIoTIJExSQ=="
}
}
},
"single-line-log": {
"version": "1.1.2",
@ -2201,7 +2427,6 @@
"version": "1.0.2",
"resolved": "https://registry.npmjs.org/string-width/-/string-width-1.0.2.tgz",
"integrity": "sha1-EYvfW4zcUaKn5w0hHgfisLmxB9M=",
"dev": true,
"requires": {
"code-point-at": "^1.0.0",
"is-fullwidth-code-point": "^1.0.0",
@ -2218,7 +2443,6 @@
"version": "3.0.1",
"resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-3.0.1.tgz",
"integrity": "sha1-ajhfuIU9lS1f8F0Oiq+UJ43GPc8=",
"dev": true,
"requires": {
"ansi-regex": "^2.0.0"
}
@ -2244,8 +2468,7 @@
"strip-json-comments": {
"version": "2.0.1",
"resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-2.0.1.tgz",
"integrity": "sha1-PFMZQukIwml8DsNEhYwobHygpgo=",
"dev": true
"integrity": "sha1-PFMZQukIwml8DsNEhYwobHygpgo="
},
"sumchecker": {
"version": "2.0.2",
@ -2303,6 +2526,49 @@
}
}
},
"tar-fs": {
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/tar-fs/-/tar-fs-2.0.0.tgz",
"integrity": "sha512-vaY0obB6Om/fso8a8vakQBzwholQ7v5+uy+tF3Ozvxv1KNezmVQAiWtcNmMHFSFPqL3dJA8ha6gdtFbfX9mcxA==",
"requires": {
"chownr": "^1.1.1",
"mkdirp": "^0.5.1",
"pump": "^3.0.0",
"tar-stream": "^2.0.0"
}
},
"tar-stream": {
"version": "2.1.0",
"resolved": "https://registry.npmjs.org/tar-stream/-/tar-stream-2.1.0.tgz",
"integrity": "sha512-+DAn4Nb4+gz6WZigRzKEZl1QuJVOLtAwwF+WUxy1fJ6X63CaGaUAxJRD2KEn1OMfcbCjySTYpNC6WmfQoIEOdw==",
"requires": {
"bl": "^3.0.0",
"end-of-stream": "^1.4.1",
"fs-constants": "^1.0.0",
"inherits": "^2.0.3",
"readable-stream": "^3.1.1"
},
"dependencies": {
"readable-stream": {
"version": "3.4.0",
"resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.4.0.tgz",
"integrity": "sha512-jItXPLmrSR8jmTRmRWJXCnGJsfy85mB3Wd/uINMXA65yrnFo0cPClFIUWzo2najVNSl+mx7/4W8ttlLWJe99pQ==",
"requires": {
"inherits": "^2.0.3",
"string_decoder": "^1.1.1",
"util-deprecate": "^1.0.1"
}
},
"string_decoder": {
"version": "1.3.0",
"resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.3.0.tgz",
"integrity": "sha512-hkRX8U1WjJFd8LsDJ2yQ/wWWxaopEsABU1XfkM8A+j0+85JAGppt16cr1Whg6KIbb4okU6Mql6BOj+uup/wKeA==",
"requires": {
"safe-buffer": "~5.2.0"
}
}
}
},
"test-value": {
"version": "2.1.0",
"resolved": "https://registry.npmjs.org/test-value/-/test-value-2.1.0.tgz",
@ -2405,7 +2671,6 @@
"version": "0.6.0",
"resolved": "https://registry.npmjs.org/tunnel-agent/-/tunnel-agent-0.6.0.tgz",
"integrity": "sha1-J6XeoGs2sEoKmWZ3SykIaPD8QP0=",
"dev": true,
"requires": {
"safe-buffer": "^5.0.1"
}
@ -2467,8 +2732,7 @@
"util-deprecate": {
"version": "1.0.2",
"resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz",
"integrity": "sha1-RQ1Nyfpw3nMnYvvS1KKJgUGaDM8=",
"dev": true
"integrity": "sha1-RQ1Nyfpw3nMnYvvS1KKJgUGaDM8="
},
"uuid": {
"version": "3.3.3",
@ -2497,6 +2761,19 @@
"extsprintf": "^1.2.0"
}
},
"which-pm-runs": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/which-pm-runs/-/which-pm-runs-1.0.0.tgz",
"integrity": "sha1-Zws6+8VS4LVd9rd4DKdGFfI60cs="
},
"wide-align": {
"version": "1.1.3",
"resolved": "https://registry.npmjs.org/wide-align/-/wide-align-1.1.3.tgz",
"integrity": "sha512-QGkOQc8XL6Bt5PwnsExKBPuMKBxnGxWWW3fU55Xt4feHozMUhdUMaBCk290qpm/wG5u/RSKzwdAC4i51YigihA==",
"requires": {
"string-width": "^1.0.2 || 2"
}
},
"wordwrapjs": {
"version": "3.0.0",
"resolved": "https://registry.npmjs.org/wordwrapjs/-/wordwrapjs-3.0.0.tgz",
@ -2509,8 +2786,7 @@
"wrappy": {
"version": "1.0.2",
"resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz",
"integrity": "sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8=",
"dev": true
"integrity": "sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8="
},
"xmlbuilder": {
"version": "9.0.7",

View file

@ -17,6 +17,7 @@
},
"dependencies": {
"electron-reload": "^1.5.0",
"node-hid": "^1.0.0",
"node-wifi": "^2.0.5"
}
}