From 5cb3ac57156b38601c882a5a755741e02bc0db7a Mon Sep 17 00:00:00 2001 From: Adam Bulmer Date: Fri, 22 Jan 2016 19:03:36 +0000 Subject: [PATCH 01/10] Express action --- commands/express.js | 33 +++++++++++++++++++++++++++++++++ index.js | 5 +++++ libs/express.js | 15 +++++++++++++++ package.json | 2 ++ 4 files changed, 55 insertions(+) create mode 100644 commands/express.js create mode 100644 libs/express.js diff --git a/commands/express.js b/commands/express.js new file mode 100644 index 0000000..df4e078 --- /dev/null +++ b/commands/express.js @@ -0,0 +1,33 @@ +var bb8 = require('../libs/bb8-instance')(), + config = require('../libs/bb8-instance').config,, + expressInstance = require('../libs/express'); + +module.exports = function() { + + if(bb8) { + + bb8.connect(function() { + + console.log('Connected to ' + config.BB8_LOCAL_NAME); + + expressInstance(function (req, res) { + + var requestBody = req.body; + + if(requestBody.action && requestBody.value) { + + if(typeof(requestBody.value) === 'string') { + bb8[requestBody.action](requestBody.value); + } else if(typeof(requestBody.value) === 'object') { + bb8[requestBody.action].apply(this, requestBody.value); + } + + res.send('Command received'); + + } else { + res.send('Command is invalid'); + } + }); + }); + } +}; diff --git a/index.js b/index.js index 467ab39..cd716b1 100644 --- a/index.js +++ b/index.js @@ -36,6 +36,11 @@ program .option('-t, --access-token ', 'API Key') .action(require('./commands/github')); +program + .command('express') + .description('Command to setup express server') + .action(require('./commands/express')); + try { program.parse(process.argv); } catch (e) { diff --git a/libs/express.js b/libs/express.js new file mode 100644 index 0000000..0a51e6d --- /dev/null +++ b/libs/express.js @@ -0,0 +1,15 @@ +var express = require('express'), + app = express(), + bodyParser = require('body-parser'); + +module.exports = function(callback) { + + app.use(bodyParser.json()); + + app.post('/', callback); + + app.listen(3000, function () { + console.log( 'Server listening on port 3000' ); + }); + +} diff --git a/package.json b/package.json index 25f5fd7..08cb05a 100644 --- a/package.json +++ b/package.json @@ -19,7 +19,9 @@ }, "homepage": "https://github.com/mintuz/bb8-commander#readme", "dependencies": { + "body-parser": "^1.14.2", "commander": "^2.9.0", + "express": "^4.13.4", "github": "^0.2.4", "home-config": "^0.1.0", "lodash": "^4.0.0", From ec8dc0d21a3d370103b71d5c2044355511ea71f8 Mon Sep 17 00:00:00 2001 From: Adam Bulmer Date: Sat, 23 Jan 2016 10:18:40 +0000 Subject: [PATCH 02/10] Syntax error fi --- commands/express.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/commands/express.js b/commands/express.js index df4e078..bd00377 100644 --- a/commands/express.js +++ b/commands/express.js @@ -1,5 +1,5 @@ var bb8 = require('../libs/bb8-instance')(), - config = require('../libs/bb8-instance').config,, + config = require('../libs/bb8-instance').config, expressInstance = require('../libs/express'); module.exports = function() { From d2b3b9ccf3bbcc89ab6db4ab973f45e88601f120 Mon Sep 17 00:00:00 2001 From: Adam Bulmer Date: Sat, 23 Jan 2016 10:36:58 +0000 Subject: [PATCH 03/10] Express Server updated to return data from bot --- commands/express.js | 26 ++++++++++++++++++++++---- 1 file changed, 22 insertions(+), 4 deletions(-) diff --git a/commands/express.js b/commands/express.js index bd00377..50cb48f 100644 --- a/commands/express.js +++ b/commands/express.js @@ -2,6 +2,16 @@ var bb8 = require('../libs/bb8-instance')(), config = require('../libs/bb8-instance').config, expressInstance = require('../libs/express'); +var callbackFactory = function(res){ + return function(err, data){ + if(!err) { + res.send({data: data}); + } else { + res.send({error: err}); + } + }; +}; + module.exports = function() { if(bb8) { @@ -14,15 +24,23 @@ module.exports = function() { var requestBody = req.body; - if(requestBody.action && requestBody.value) { + if(requestBody.action && requestBody.mode === 'sphero') { if(typeof(requestBody.value) === 'string') { - bb8[requestBody.action](requestBody.value); + + bb8[requestBody.action](requestBody.value, callbackFactory(res)); + } else if(typeof(requestBody.value) === 'object') { + + requestBody.value.push(callbackFactory(res)); + bb8[requestBody.action].apply(this, requestBody.value); - } - res.send('Command received'); + } else { + + bb8[requestBody.action](callbackFactory(res)); + + } } else { res.send('Command is invalid'); From 2ce352c4657693f10f477f629bc2428680ddb8e3 Mon Sep 17 00:00:00 2001 From: Adam Bulmer Date: Sun, 24 Jan 2016 09:45:56 +0000 Subject: [PATCH 04/10] Refactor to use execute command function --- commands/express.js | 40 ++++++++++++++++------------------------ index.js | 19 ++++++------------- libs/execute-command.js | 9 +++++++++ 3 files changed, 31 insertions(+), 37 deletions(-) create mode 100644 libs/execute-command.js diff --git a/commands/express.js b/commands/express.js index 50cb48f..31bd652 100644 --- a/commands/express.js +++ b/commands/express.js @@ -12,40 +12,32 @@ var callbackFactory = function(res){ }; }; -module.exports = function() { +module.exports = function(bb8, options) { - if(bb8) { + expressInstance(function (req, res) { - bb8.connect(function() { + var requestBody = req.body; - console.log('Connected to ' + config.BB8_LOCAL_NAME); + if(requestBody.action && requestBody.mode === 'sphero') { - expressInstance(function (req, res) { + if(typeof(requestBody.value) === 'string') { - var requestBody = req.body; + bb8[requestBody.action](requestBody.value, callbackFactory(res)); - if(requestBody.action && requestBody.mode === 'sphero') { + } else if(typeof(requestBody.value) === 'object') { - if(typeof(requestBody.value) === 'string') { + requestBody.value.push(callbackFactory(res)); - bb8[requestBody.action](requestBody.value, callbackFactory(res)); + bb8[requestBody.action].apply(this, requestBody.value); - } else if(typeof(requestBody.value) === 'object') { + } else { - requestBody.value.push(callbackFactory(res)); + bb8[requestBody.action](callbackFactory(res)); - bb8[requestBody.action].apply(this, requestBody.value); + } - } else { - - bb8[requestBody.action](callbackFactory(res)); - - } - - } else { - res.send('Command is invalid'); - } - }); - }); - } + } else { + res.send('Command is invalid'); + } + }); }; diff --git a/index.js b/index.js index 2dda72e..3f89b95 100644 --- a/index.js +++ b/index.js @@ -1,15 +1,6 @@ -var program = require('commander'); -var packageFile = require('./package.json'); -var bb8 = require('./libs/bb8-instance')(); -var config = require('./libs/bb8-instance').config; - -var executeCommand = function (command, options) { - if (bb8) { - bb8.connect(function () { - require(command)(bb8, options) - }); - } -}; +var program = require('commander'), + packageFile = require('./package.json'), + executeCommand = require('./libs/execute-command'); program.version(packageFile.version); @@ -76,7 +67,9 @@ program program .command('express') .description('Command to setup express server') - .action(require('./commands/express')); + .action(function () { + executeCommand('./commands/express'); + }); try { program.parse(process.argv); diff --git a/libs/execute-command.js b/libs/execute-command.js new file mode 100644 index 0000000..baae63f --- /dev/null +++ b/libs/execute-command.js @@ -0,0 +1,9 @@ +var bb8 = require('./bb8-instance')(); + +module.exports = function (command, options) { + if (bb8) { + bb8.connect(function () { + require(command)(bb8, options) + }); + } +}; \ No newline at end of file From 90c9ce4352b1e13d9440bff7260465969685303c Mon Sep 17 00:00:00 2001 From: Adam Bulmer Date: Sun, 24 Jan 2016 10:09:55 +0000 Subject: [PATCH 05/10] Express custom commands now supported --- commands/express.js | 60 ++++++++++++++++++++++++++++++++--------- index.js | 12 ++++----- libs/execute-command.js | 35 +++++++++++++++++++----- package.json | 1 + 4 files changed, 83 insertions(+), 25 deletions(-) diff --git a/commands/express.js b/commands/express.js index 31bd652..9392d77 100644 --- a/commands/express.js +++ b/commands/express.js @@ -1,6 +1,7 @@ var bb8 = require('../libs/bb8-instance')(), config = require('../libs/bb8-instance').config, - expressInstance = require('../libs/express'); + expressInstance = require('../libs/express'), + executeCustomCommand = require('../libs/execute-command'); var callbackFactory = function(res){ return function(err, data){ @@ -12,32 +13,65 @@ var callbackFactory = function(res){ }; }; -module.exports = function(bb8, options) { +var spheroCommandExecuter = function(bb8, requestBody, res) { - expressInstance(function (req, res) { + if(typeof(requestBody.value) === 'string') { - var requestBody = req.body; + bb8[requestBody.action](requestBody.value, callbackFactory(res)); - if(requestBody.action && requestBody.mode === 'sphero') { + } else if(typeof(requestBody.value) === 'object') { + + requestBody.value.push(callbackFactory(res)); + + bb8[requestBody.action].apply(this, requestBody.value); + + } else { + + bb8[requestBody.action](callbackFactory(res)); + + } + +}; - if(typeof(requestBody.value) === 'string') { +var customCommandExecuter = function(bb8, requestBody, res){ + + if(typeof(requestBody.value) === 'string') { + + executeCustomCommand.alreadyConnectedSingleValue(bb8, requestBody.action, requestBody.value); - bb8[requestBody.action](requestBody.value, callbackFactory(res)); + } else if(typeof(requestBody.value) === 'object') { + + executeCustomCommand.alreadyConnectedMultipleValues(bb8, requestBody.action, requestBody.value); + + } else { - } else if(typeof(requestBody.value) === 'object') { + executeCustomCommand.alreadyConnected(bb8, requestBody.action); - requestBody.value.push(callbackFactory(res)); + } - bb8[requestBody.action].apply(this, requestBody.value); + res.send('Command Executed - ' + requestBody.action); - } else { +}; + +module.exports = function(bb8, options) { - bb8[requestBody.action](callbackFactory(res)); + expressInstance(function (req, res) { - } + var requestBody = req.body; + if(requestBody.action && requestBody.mode === 'sphero') { + + spheroCommandExecuter(bb8, req.body, res); + + } else if(requestBody.action && requestBody.mode === 'custom') { + + customCommandExecuter(bb8, req.body, res); + } else { + res.send('Command is invalid'); + } + }); }; diff --git a/index.js b/index.js index 3f89b95..5f4c870 100644 --- a/index.js +++ b/index.js @@ -24,7 +24,7 @@ program .command('disco') .description('Command to make your BB8 Unit A disco ball') .action(function () { - executeCommand('./commands/disco'); + executeCommand('disco'); }); program @@ -34,20 +34,20 @@ program .option('-cc, --country ', 'Country name such as uk') .option('-t, --access-token ', 'API Key') .action(function(options) { - executeCommand('./commands/weather', options); + executeCommand('weather', options); }); program .command('github') .description('Command to get notifications of new issues and PRs') .option('-t, --access-token ', 'API Key') - .action(require('./commands/github')); + .action(require('github')); program .command('roll') .description('BB8 will roll!') .action(function () { - executeCommand('./commands/roll'); + executeCommand('roll'); }); @@ -61,14 +61,14 @@ program .option('--access-token-key ', 'Twitter api access token key') .option('--access-token-secret ', 'Twitter api access token secret') .action(function (options) { - executeCommand('./commands/tweet', options) + executeCommand('tweet', options) }); program .command('express') .description('Command to setup express server') .action(function () { - executeCommand('./commands/express'); + executeCommand('express'); }); try { diff --git a/libs/execute-command.js b/libs/execute-command.js index baae63f..6a7a59d 100644 --- a/libs/execute-command.js +++ b/libs/execute-command.js @@ -1,9 +1,32 @@ -var bb8 = require('./bb8-instance')(); +var bb8 = require('./bb8-instance')(), + appRootPath = require('app-root-path'), + _ = require('lodash'); module.exports = function (command, options) { - if (bb8) { - bb8.connect(function () { - require(command)(bb8, options) - }); - } + if (bb8) { + bb8.connect(function () { + require(appRootPath + '/commands/' + command)(bb8, options); + }); + } +}; + +module.exports.alreadyConnected = function(bb8, command) { + if (bb8) { + require(appRootPath + '/commands/' + command)(bb8); + } +} + +module.exports.alreadyConnectedSingleValue = function(bb8, command, options) { + if (bb8) { + require(appRootPath + '/commands/' + command)(bb8, options); + } +}; + +module.exports.alreadyConnectedMultipleValues = function(bb8, command, options) { + + if (bb8) { + var parameters = _.union([bb8], options); + require(appRootPath + '/commands/' + command).apply(this, parameters); + } + }; \ No newline at end of file diff --git a/package.json b/package.json index 4582da1..0ce6773 100644 --- a/package.json +++ b/package.json @@ -19,6 +19,7 @@ }, "homepage": "https://github.com/mintuz/bb8-commander#readme", "dependencies": { + "app-root-path": "^1.0.0", "body-parser": "^1.14.2", "commander": "^2.9.0", "express": "^4.13.4", From f5c896642be5544c0ebf006f0e43319fd7220b7a Mon Sep 17 00:00:00 2001 From: Adam Bulmer Date: Sun, 24 Jan 2016 10:12:48 +0000 Subject: [PATCH 06/10] Able to pass the port in --- commands/express.js | 4 ++-- index.js | 5 +++-- libs/express.js | 8 +++++--- 3 files changed, 10 insertions(+), 7 deletions(-) diff --git a/commands/express.js b/commands/express.js index 9392d77..26eb733 100644 --- a/commands/express.js +++ b/commands/express.js @@ -72,6 +72,6 @@ module.exports = function(bb8, options) { res.send('Command is invalid'); } - - }); + + }, options); }; diff --git a/index.js b/index.js index 5f4c870..7b5f75f 100644 --- a/index.js +++ b/index.js @@ -67,8 +67,9 @@ program program .command('express') .description('Command to setup express server') - .action(function () { - executeCommand('express'); + .option('-p, --port ', 'Port to run express on. Defaults to 3000') + .action(function (options) { + executeCommand('express', options); }); try { diff --git a/libs/express.js b/libs/express.js index 0a51e6d..6b51779 100644 --- a/libs/express.js +++ b/libs/express.js @@ -2,14 +2,16 @@ var express = require('express'), app = express(), bodyParser = require('body-parser'); -module.exports = function(callback) { +module.exports = function(callback, options) { + + var port = options.port || 3000; app.use(bodyParser.json()); app.post('/', callback); - app.listen(3000, function () { - console.log( 'Server listening on port 3000' ); + app.listen(port, function () { + console.log( 'Server listening on port ' + port ); }); } From f936b0e1a2a33a15c7cb2e51c3d360ac9de2dd2a Mon Sep 17 00:00:00 2001 From: Adam Bulmer Date: Sun, 24 Jan 2016 10:20:04 +0000 Subject: [PATCH 07/10] able to handle objects and arrays --- commands/express.js | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/commands/express.js b/commands/express.js index 26eb733..dcfc435 100644 --- a/commands/express.js +++ b/commands/express.js @@ -1,7 +1,8 @@ var bb8 = require('../libs/bb8-instance')(), config = require('../libs/bb8-instance').config, expressInstance = require('../libs/express'), - executeCustomCommand = require('../libs/execute-command'); + executeCustomCommand = require('../libs/execute-command'), + _ = require('lodash'); var callbackFactory = function(res){ return function(err, data){ @@ -34,12 +35,12 @@ var spheroCommandExecuter = function(bb8, requestBody, res) { }; var customCommandExecuter = function(bb8, requestBody, res){ - - if(typeof(requestBody.value) === 'string') { + + if(_.isString(requestBody.value) || _.isObject(requestBody.value)) { executeCustomCommand.alreadyConnectedSingleValue(bb8, requestBody.action, requestBody.value); - } else if(typeof(requestBody.value) === 'object') { + } else if(_.isArray(requestBody.value)) { executeCustomCommand.alreadyConnectedMultipleValues(bb8, requestBody.action, requestBody.value); From f91b5f4bbaddc9325e87f036c9a329460dd54f57 Mon Sep 17 00:00:00 2001 From: Adam Bulmer Date: Sun, 24 Jan 2016 10:57:32 +0000 Subject: [PATCH 08/10] Readme updated --- README.md | 82 +++++++++++++++++++++++++++++++++++++++++++++ commands/express.js | 6 ++-- commands/tweet.js | 2 +- 3 files changed, 86 insertions(+), 4 deletions(-) diff --git a/README.md b/README.md index f5fc517..a4efe87 100644 --- a/README.md +++ b/README.md @@ -20,6 +20,88 @@ Not yet on npm so you'll have to do it the good'ol fasioned way with a cheeky gi * `node index.js disco` - Command to turn your BB8 Unit into a shining disco ball in the night * `node index.js weather --city="manchester" --country="uk" --api-key="ABCD"` - Command to turn your BB8 Unit into your very own weather reporter, uses OpenWeather so be sure to get your own API key * `node index.js tweet --hash-tag="bb8" --delay=5000` - Command to search twitter and run the first hashtag it finds as a command. Eg a tweet "#disco #bb8" would run the `disco` command --consumer-key xxx --consumer-secret xxx --access-token-key xxx --access-token-secret xxx +* `node index.js express --port=4000` - Command to run an express server which has a single POST endpoint which you can send a JSON object to. See below for more details. + +#### Express Server + +Having the ability to run an Express server to issue commands to the BB8 unit opens up a bunch of possibilities. One of the main benefits of having an Express server is that you can integrate into [IFTTT](https://ifttt.com/) and at that point, you have entered the Internet of things. + +To get started is really easy, all you need to do is run `node index.js express --port=4000` adn once your BB8 is connected, an Express server will be started. + +You can then send commands directly to it via a POST request. It supports any SpheroSDK command as well as custom commands we have created. See below for some examples. + +##### Native Commands + +With native commands, the response body will include information the BB8 exposes once that command has been executed. Read the Sphero documentation on what data it returns. http://sdk.sphero.com/community-apis/javascript-sdk/ + +##### Running the `color` command + +Post Request - localhost:3000/ + +Request Body + +``` +{ + "mode":"sphero", + "command":"color", + "value": "yellow" +} +``` + +##### Running the `roll` command + +Post Request - localhost:3000/ + +Request Body + +``` +{ + "mode":"sphero", + "command":"roll", + "value": [130, 50] +} +``` + +With this request, we are passing an array and that's because the roll command in Sphero SDK requires multiple parameters. This is just a simple way to pass those values to that command. + +##### Custom Commands + +##### Running the `disco` command + +Post Request - localhost:3000/ + +Request Body + +``` +{ + "mode":"custom", + "command":"disco" +} +``` + +##### Running the `tweet` command + +POST Request - localhost:3000/ + +Request Body + +``` +{ + "mode":"custom", + "command":"tweet", + "value": { + "delay": 30, + "consumerKey": "YOUR_CONSUMER_KEY", + "consumerSecret": "YOUR_CONSUMER_SECRET", + "accessTokenKey": "YOUR_ACCESS_TOKEN_KEY", + "accessTokenSecret": "YOUR_ACCESS_TOKEN_SECRET" + } +} +``` + +Obviously you wouldn’t pass your OAuth information like this (BB8 Commander supports environment variables for secure data) but the important thing to note here is, anything that can be passed to the CLI tool can also be passed into the express server endpoint. + +A suite difference between native commands and custom commands is that native commands that require multiple parameters will be passed as an array whilst custom commands will be objects. The reason for this is custom commands are key value pairs due to them sharing the same code as the CLI tool. # Contributors * [@mintuz](http://twitter.com/mintuz) diff --git a/commands/express.js b/commands/express.js index dcfc435..54b0e76 100644 --- a/commands/express.js +++ b/commands/express.js @@ -16,11 +16,11 @@ var callbackFactory = function(res){ var spheroCommandExecuter = function(bb8, requestBody, res) { - if(typeof(requestBody.value) === 'string') { + if(_.isString(requestBody.value)) { bb8[requestBody.action](requestBody.value, callbackFactory(res)); - } else if(typeof(requestBody.value) === 'object') { + } else if(_.isArray(requestBody.value)) { requestBody.value.push(callbackFactory(res)); @@ -46,7 +46,7 @@ var customCommandExecuter = function(bb8, requestBody, res){ } else { - executeCustomCommand.alreadyConnected(bb8, requestBody.action); + executeCustomCommand.alreadyConnectedSingleValue(bb8, requestBody.action, {}); } diff --git a/commands/tweet.js b/commands/tweet.js index cd5003d..cf05a22 100644 --- a/commands/tweet.js +++ b/commands/tweet.js @@ -22,7 +22,7 @@ function getLatestStatus(tweets) { } var executeTwitterCommand = function (bb8, options) { - + var twitter = TwitterClient(options), hashTag = options.hashTag || 'bb8code'; From 63521b8520984b4bf83986b60afb4217f4db1c8d Mon Sep 17 00:00:00 2001 From: Adam Bulmer Date: Sun, 24 Jan 2016 10:59:07 +0000 Subject: [PATCH 09/10] README updated --- README.md | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/README.md b/README.md index a4efe87..53d533e 100644 --- a/README.md +++ b/README.md @@ -22,7 +22,7 @@ Not yet on npm so you'll have to do it the good'ol fasioned way with a cheeky gi * `node index.js tweet --hash-tag="bb8" --delay=5000` - Command to search twitter and run the first hashtag it finds as a command. Eg a tweet "#disco #bb8" would run the `disco` command --consumer-key xxx --consumer-secret xxx --access-token-key xxx --access-token-secret xxx * `node index.js express --port=4000` - Command to run an express server which has a single POST endpoint which you can send a JSON object to. See below for more details. -#### Express Server +### Express Server Having the ability to run an Express server to issue commands to the BB8 unit opens up a bunch of possibilities. One of the main benefits of having an Express server is that you can integrate into [IFTTT](https://ifttt.com/) and at that point, you have entered the Internet of things. @@ -30,11 +30,11 @@ To get started is really easy, all you need to do is run `node index.js express You can then send commands directly to it via a POST request. It supports any SpheroSDK command as well as custom commands we have created. See below for some examples. -##### Native Commands +### Native Commands With native commands, the response body will include information the BB8 exposes once that command has been executed. Read the Sphero documentation on what data it returns. http://sdk.sphero.com/community-apis/javascript-sdk/ -##### Running the `color` command +#### Running the `color` command Post Request - localhost:3000/ @@ -48,7 +48,7 @@ Request Body } ``` -##### Running the `roll` command +#### Running the `roll` command Post Request - localhost:3000/ @@ -64,9 +64,9 @@ Request Body With this request, we are passing an array and that's because the roll command in Sphero SDK requires multiple parameters. This is just a simple way to pass those values to that command. -##### Custom Commands +### Custom Commands -##### Running the `disco` command +#### Running the `disco` command Post Request - localhost:3000/ @@ -79,7 +79,7 @@ Request Body } ``` -##### Running the `tweet` command +#### Running the `tweet` command POST Request - localhost:3000/ From cf0b9c9cc5600fea5c1fa630d5f5160c46bf4585 Mon Sep 17 00:00:00 2001 From: Adam Bulmer Date: Sun, 24 Jan 2016 11:00:27 +0000 Subject: [PATCH 10/10] Update to the README file --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 53d533e..5140997 100644 --- a/README.md +++ b/README.md @@ -1,5 +1,5 @@ # BB8 Commander -A Node CLI Tool for Sphero BB8 Robot. +A Node CLI Tool for Sphero BB8 Robot using the [Sphero Javascript SDK](http://sdk.sphero.com/community-apis/javascript-sdk/) ![BB8 Rolling like he's owning](http://i.imgur.com/00sZIf3.gif)