diff --git a/controller.js b/controller.js new file mode 100644 index 0000000..456c0cb --- /dev/null +++ b/controller.js @@ -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)); diff --git a/dist/dualshock-controller/.editorconfig b/dist/dualshock-controller/.editorconfig new file mode 100644 index 0000000..359c836 --- /dev/null +++ b/dist/dualshock-controller/.editorconfig @@ -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 diff --git a/dist/dualshock-controller/.gitignore b/dist/dualshock-controller/.gitignore new file mode 100644 index 0000000..f568ada --- /dev/null +++ b/dist/dualshock-controller/.gitignore @@ -0,0 +1,6 @@ +node_modules +*.log +coverage +*.iml +*.idea +.DS_Store diff --git a/dist/dualshock-controller/.travis.yml b/dist/dualshock-controller/.travis.yml new file mode 100644 index 0000000..3765381 --- /dev/null +++ b/dist/dualshock-controller/.travis.yml @@ -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 diff --git a/dist/dualshock-controller/README.md b/dist/dualshock-controller/README.md new file mode 100644 index 0000000..3c4f2c6 --- /dev/null +++ b/dist/dualshock-controller/README.md @@ -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)); + +~~~~ + +## 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) + +#### 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 +``` + +#### 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 +``` + +#### 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. diff --git a/dist/dualshock-controller/controllerConfigurations/README.md b/dist/dualshock-controller/controllerConfigurations/README.md new file mode 100644 index 0000000..195cc27 --- /dev/null +++ b/dist/dualshock-controller/controllerConfigurations/README.md @@ -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. + }, +] diff --git a/dist/dualshock-controller/controllerConfigurations/dualShock3.json b/dist/dualshock-controller/controllerConfigurations/dualShock3.json new file mode 100644 index 0000000..3d10283 --- /dev/null +++ b/dist/dualshock-controller/controllerConfigurations/dualShock3.json @@ -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 + } + } +} diff --git a/dist/dualshock-controller/controllerConfigurations/dualShock4-alternate-driver.json b/dist/dualshock-controller/controllerConfigurations/dualShock4-alternate-driver.json new file mode 100644 index 0000000..bdc76e9 --- /dev/null +++ b/dist/dualshock-controller/controllerConfigurations/dualShock4-alternate-driver.json @@ -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 + }] +} diff --git a/dist/dualshock-controller/controllerConfigurations/dualShock4-generic-driver.json b/dist/dualshock-controller/controllerConfigurations/dualShock4-generic-driver.json new file mode 100644 index 0000000..5cdfda6 --- /dev/null +++ b/dist/dualshock-controller/controllerConfigurations/dualShock4-generic-driver.json @@ -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 + }] +} diff --git a/dist/dualshock-controller/controllerConfigurations/dualShock4.json b/dist/dualshock-controller/controllerConfigurations/dualShock4.json new file mode 100644 index 0000000..a3afc16 --- /dev/null +++ b/dist/dualshock-controller/controllerConfigurations/dualShock4.json @@ -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 + }] +} diff --git a/dist/dualshock-controller/examples/README.md b/dist/dualshock-controller/examples/README.md new file mode 100644 index 0000000..2f28345 --- /dev/null +++ b/dist/dualshock-controller/examples/README.md @@ -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). diff --git a/dist/dualshock-controller/examples/consolePrintControllerEvents.js b/dist/dualshock-controller/examples/consolePrintControllerEvents.js new file mode 100644 index 0000000..45e3322 --- /dev/null +++ b/dist/dualshock-controller/examples/consolePrintControllerEvents.js @@ -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; diff --git a/dist/dualshock-controller/examples/consolePrintDualShock3.js b/dist/dualshock-controller/examples/consolePrintDualShock3.js new file mode 100644 index 0000000..078859a --- /dev/null +++ b/dist/dualshock-controller/examples/consolePrintDualShock3.js @@ -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 + }); +}); diff --git a/dist/dualshock-controller/examples/consolePrintDualShock4.js b/dist/dualshock-controller/examples/consolePrintDualShock4.js new file mode 100644 index 0000000..78ea7c4 --- /dev/null +++ b/dist/dualshock-controller/examples/consolePrintDualShock4.js @@ -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 + }); +}); diff --git a/dist/dualshock-controller/examples/deviceDiscoveryHelp.js b/dist/dualshock-controller/examples/deviceDiscoveryHelp.js new file mode 100644 index 0000000..e4d73a8 --- /dev/null +++ b/dist/dualshock-controller/examples/deviceDiscoveryHelp.js @@ -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]); + } + } +}); diff --git a/dist/dualshock-controller/examples/deviceDiscoveryHelpLinux.js b/dist/dualshock-controller/examples/deviceDiscoveryHelpLinux.js new file mode 100644 index 0000000..bb143d9 --- /dev/null +++ b/dist/dualshock-controller/examples/deviceDiscoveryHelpLinux.js @@ -0,0 +1,6 @@ +var joystick = require('joystick'), + controller = new joystick(0, 256, 500); + +controller.on('button', console.log); + +controller.on('axis', console.log); diff --git a/dist/dualshock-controller/gruntfile.js b/dist/dualshock-controller/gruntfile.js new file mode 100644 index 0000000..60d5100 --- /dev/null +++ b/dist/dualshock-controller/gruntfile.js @@ -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']); +}; diff --git a/dist/dualshock-controller/package.json b/dist/dualshock-controller/package.json new file mode 100644 index 0000000..867b06d --- /dev/null +++ b/dist/dualshock-controller/package.json @@ -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" + ] +} diff --git a/dist/dualshock-controller/src/HID.node b/dist/dualshock-controller/src/HID.node new file mode 100644 index 0000000..1501c4f Binary files /dev/null and b/dist/dualshock-controller/src/HID.node differ diff --git a/dist/dualshock-controller/src/analogs.js b/dist/dualshock-controller/src/analogs.js new file mode 100644 index 0000000..19b001e --- /dev/null +++ b/dist/dualshock-controller/src/analogs.js @@ -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; diff --git a/dist/dualshock-controller/src/buttons.js b/dist/dualshock-controller/src/buttons.js new file mode 100644 index 0000000..516938a --- /dev/null +++ b/dist/dualshock-controller/src/buttons.js @@ -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; diff --git a/dist/dualshock-controller/src/config.js b/dist/dualshock-controller/src/config.js new file mode 100644 index 0000000..1901dfe --- /dev/null +++ b/dist/dualshock-controller/src/config.js @@ -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; diff --git a/dist/dualshock-controller/src/controller.js b/dist/dualshock-controller/src/controller.js new file mode 100644 index 0000000..f6760c3 --- /dev/null +++ b/dist/dualshock-controller/src/controller.js @@ -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; diff --git a/dist/dualshock-controller/src/dualshock.js b/dist/dualshock-controller/src/dualshock.js new file mode 100644 index 0000000..48b9b22 --- /dev/null +++ b/dist/dualshock-controller/src/dualshock.js @@ -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; diff --git a/dist/dualshock-controller/src/gyro.js b/dist/dualshock-controller/src/gyro.js new file mode 100644 index 0000000..9635c46 --- /dev/null +++ b/dist/dualshock-controller/src/gyro.js @@ -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; diff --git a/dist/dualshock-controller/src/smoothing.js b/dist/dualshock-controller/src/smoothing.js new file mode 100644 index 0000000..5a20160 --- /dev/null +++ b/dist/dualshock-controller/src/smoothing.js @@ -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; diff --git a/dist/dualshock-controller/src/status.js b/dist/dualshock-controller/src/status.js new file mode 100644 index 0000000..016e9da --- /dev/null +++ b/dist/dualshock-controller/src/status.js @@ -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; diff --git a/dist/dualshock-controller/src/touchpad.js b/dist/dualshock-controller/src/touchpad.js new file mode 100644 index 0000000..af7674f --- /dev/null +++ b/dist/dualshock-controller/src/touchpad.js @@ -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]); + } + }; +}; diff --git a/dist/dualshock-controller/src/utilities.js b/dist/dualshock-controller/src/utilities.js new file mode 100644 index 0000000..089d063 --- /dev/null +++ b/dist/dualshock-controller/src/utilities.js @@ -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() + ]); + } +}; diff --git a/dist/dualshock-controller/test/analog.tests.js b/dist/dualshock-controller/test/analog.tests.js new file mode 100644 index 0000000..3291715 --- /dev/null +++ b/dist/dualshock-controller/test/analog.tests.js @@ -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); + }); + }); + +}); diff --git a/dist/dualshock-controller/test/buttons.tests.js b/dist/dualshock-controller/test/buttons.tests.js new file mode 100644 index 0000000..f7a8688 --- /dev/null +++ b/dist/dualshock-controller/test/buttons.tests.js @@ -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); + }); + }); + +}); diff --git a/dist/dualshock-controller/test/config.tests.js b/dist/dualshock-controller/test/config.tests.js new file mode 100644 index 0000000..8ca5e64 --- /dev/null +++ b/dist/dualshock-controller/test/config.tests.js @@ -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); + }); + }); +}); diff --git a/dist/dualshock-controller/test/dualshock.tests.js b/dist/dualshock-controller/test/dualshock.tests.js new file mode 100644 index 0000000..17e85bd --- /dev/null +++ b/dist/dualshock-controller/test/dualshock.tests.js @@ -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'); + }); + }); + }); +}); diff --git a/dist/dualshock-controller/test/gyro.tests.js b/dist/dualshock-controller/test/gyro.tests.js new file mode 100644 index 0000000..a3037a8 --- /dev/null +++ b/dist/dualshock-controller/test/gyro.tests.js @@ -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); + }); + }); + +}); diff --git a/dist/dualshock-controller/test/smoothing.tests.js b/dist/dualshock-controller/test/smoothing.tests.js new file mode 100644 index 0000000..2046e28 --- /dev/null +++ b/dist/dualshock-controller/test/smoothing.tests.js @@ -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); + }); + }); +}); diff --git a/dist/dualshock-controller/test/status.tests.js b/dist/dualshock-controller/test/status.tests.js new file mode 100644 index 0000000..a394439 --- /dev/null +++ b/dist/dualshock-controller/test/status.tests.js @@ -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; + }); + }); +}); diff --git a/html/gamepad/ps4controller.js b/html/gamepad/ps4controller.js new file mode 100644 index 0000000..6c53faf --- /dev/null +++ b/html/gamepad/ps4controller.js @@ -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} + } +} diff --git a/html/views/menu/connected.js b/html/views/menu/connected.js index f1d8f5a..8ccf261 100644 --- a/html/views/menu/connected.js +++ b/html/views/menu/connected.js @@ -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++; diff --git a/package-lock.json b/package-lock.json index 48f66ea..5421aec 100644 --- a/package-lock.json +++ b/package-lock.json @@ -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", diff --git a/package.json b/package.json index d1a703c..3059a7e 100644 --- a/package.json +++ b/package.json @@ -17,6 +17,7 @@ }, "dependencies": { "electron-reload": "^1.5.0", + "node-hid": "^1.0.0", "node-wifi": "^2.0.5" } }