From f06c3f735f8a967e50cb2dbefba2e7448d71e366 Mon Sep 17 00:00:00 2001 From: Jerome Bouvard Date: Tue, 17 Mar 2015 16:19:16 +0100 Subject: [PATCH 01/40] Add Upload to Garden Function --- flower-power-cloud.js | 74 +++++++++++++++++++++++++++++++++++++++---- 1 file changed, 67 insertions(+), 7 deletions(-) diff --git a/flower-power-cloud.js b/flower-power-cloud.js index 381011c..b8c22fc 100644 --- a/flower-power-cloud.js +++ b/flower-power-cloud.js @@ -7,6 +7,7 @@ var https = require('https') , querystring = require('querystring') , url = require('url') , util = require('util') + , request = require('request') ; @@ -108,8 +109,8 @@ CloudAPI.prototype._refresh = function(self, callback) { CloudAPI.prototype.getGarden = function(callback) { var self = this; - return self.invoke('GET', '/sensor_data/v2/sync?include_s3_urls=1', function(err, code, results) { - var count, i, location, locations, sensor, sensors; + return self.invoke('GET', '/sensor_data/v3/sync?include_s3_urls=1', function(err, code, results) { + var count, i, location, locations, sensor, sensors, user_config_version; if (!!err) return callback(err); @@ -118,12 +119,12 @@ CloudAPI.prototype.getGarden = function(callback) { if (!!err) self.logger.error('invoke', { event: 'sync', diagnostic: err.message }); else locations[id].samples = results.samples; - if (--count === 0) callback(null, locations, sensors); + if (--count === 0) callback(null, locations, sensors, user_config_version); }; }; count = 0; - + user_config_version = results.user_config_version; sensors = {}; for (i = 0; i < results.sensors.length; i++) { @@ -142,7 +143,7 @@ CloudAPI.prototype.getGarden = function(callback) { } count++; - self.roundtrip('GET', '/sensor_data/v1/garden_locations_status', function(err, results) { + self.roundtrip('GET', '/sensor_data/v4/garden_locations_status', function(err, results) { var i, id; if (!!err) self.logger.error('invoke', { event: 'garden_locations_status', diagnostic: err.message }); @@ -151,16 +152,75 @@ CloudAPI.prototype.getGarden = function(callback) { location = results.locations[i]; id = location.location_identifier; delete(location.location_identifier); - + for (i = 0; i < results.sensors.length; i++) { + sensor = results.sensors[i]; + } locations[id].status = location; } } - if (--count === 0) callback(null, locations, sensors); + if (--count === 0) callback(null, locations, sensors, user_config_version); }); }); }; +CloudAPI.prototype.uploadGarden = function (sensor_serial, user_config_version, sUpTime, history, currentID, sessionPeriod, sessionStartIndex, callback) { + +var self = this; +var json1 = { + client_datetime_utc : new Date(), + user_config_version : user_config_version, + tmz_offset : (new Date()).getTimezoneOffset(), + + session_histories : [{ + sensor_serial : sensor_serial, + session_id : currentID, + sample_measure_period : sessionPeriod, + sensor_startup_timestamp_utc : sUpTime, + session_start_index : sessionStartIndex + }], + + uploads : [{ + sensor_serial : sensor_serial, + upload_timestamp_utc : new Date(), + buffer_base64 : history, + app_version : "", + sensor_fw_version : "", + sensor_hw_identifier : "", + }] + }; + + request({ + proxy: undefined, + method: 'PUT', + uri: 'https://apiflowerpower.parrot.com/sensor_data/v5/sample', + headers: {Authorization : "Bearer " + self.oauth.access_token}, + body: (json1 !== null)? JSON.stringify(json1) : undefined + }, function (error, response, body) { + if (error !== null) { + callback(error, null); + } else if (response.statusCode !== 200) { + var htmlError = self.manageHtmlErrors(response); + if (typeof (callback) === 'function') { + callback(htmlError, body); + } + } else { + body = JSON.parse(body); + if((body.hasOwnProperty("errors") && (body.errors.length > 0))){ + callback(body.errors[0], null); + } + else { + if (self.formatter instanceof Function){ + body = self.formatter(body); + } + if (callback instanceof Function) { + callback(null, body); + } + } + } + }); +}; + CloudAPI.prototype.roundtrip = function(method, path, json, callback) { var self = this; From eaa598a789d56a2a762675f9c75176d7242a220e Mon Sep 17 00:00:00 2001 From: Amaury Saugrain Date: Thu, 30 Jul 2015 11:23:22 +0200 Subject: [PATCH 02/40] Changes in the getGarden function to match the requirements of the Raspberry/flowerPower program. Added to tabs required by this program and deleted the get samples part due to the use of location.last_sample_utc which is contained in the result of the get garden_locations_satuses, called after. --- flower-power-cloud.js | 27 +++++++++++---------------- 1 file changed, 11 insertions(+), 16 deletions(-) diff --git a/flower-power-cloud.js b/flower-power-cloud.js index b8c22fc..ec76768 100644 --- a/flower-power-cloud.js +++ b/flower-power-cloud.js @@ -108,6 +108,8 @@ CloudAPI.prototype._refresh = function(self, callback) { CloudAPI.prototype.getGarden = function(callback) { var self = this; + var tabSensorSerial = []; + var tabHistoryIndex = []; return self.invoke('GET', '/sensor_data/v3/sync?include_s3_urls=1', function(err, code, results) { var count, i, location, locations, sensor, sensors, user_config_version; @@ -119,7 +121,7 @@ CloudAPI.prototype.getGarden = function(callback) { if (!!err) self.logger.error('invoke', { event: 'sync', diagnostic: err.message }); else locations[id].samples = results.samples; - if (--count === 0) callback(null, locations, sensors, user_config_version); + if (--count === 0) callback(null, locations, sensors, tabSensorSerial, tabHistoryIndex, user_config_version); }; }; @@ -129,19 +131,10 @@ CloudAPI.prototype.getGarden = function(callback) { sensors = {}; for (i = 0; i < results.sensors.length; i++) { sensor = results.sensors[i]; + tabSensorSerial.push(sensor.sensor_serial); sensors[sensor.sensor_serial] = sensor; } - locations = {}; - for (i = 0; i < results.locations.length; i++) { - location = results.locations[i]; - locations[location.location_identifier] = location; - - count++; - self.roundtrip('GET', '/sensor_data/v2/sample/location/' + location.location_identifier - + '?from_datetime_utc=' + location.last_sample_utc, f(location.location_identifier)); - } - count++; self.roundtrip('GET', '/sensor_data/v4/garden_locations_status', function(err, results) { var i, id; @@ -152,14 +145,16 @@ CloudAPI.prototype.getGarden = function(callback) { location = results.locations[i]; id = location.location_identifier; delete(location.location_identifier); - for (i = 0; i < results.sensors.length; i++) { - sensor = results.sensors[i]; - } - locations[id].status = location; + + for (i = 0; i < results.sensors.length; i++) { + sensor = results.sensors[i]; + tabHistoryIndex.push(sensor.current_history_index); + } + } } - if (--count === 0) callback(null, locations, sensors, user_config_version); + if (--count === 0) callback(null, sensors, tabSensorSerial, tabHistoryIndex, user_config_version); }); }); }; From 804fe387201476785e46fb1ecda2fd45e937c86c Mon Sep 17 00:00:00 2001 From: bsautron Date: Tue, 8 Dec 2015 15:28:46 +0100 Subject: [PATCH 03/40] New version: multiple callback fixed and more... --- .gitignore | 2 - ApiError.js | 28 ++++ FlowerPowerCloud.js | 153 +++++++++++++++++++++ README.md | 57 ++++---- flower-power-cloud.js | 307 ------------------------------------------ package.json | 48 ++++--- test.js | 33 +++-- 7 files changed, 252 insertions(+), 376 deletions(-) create mode 100644 ApiError.js create mode 100644 FlowerPowerCloud.js delete mode 100644 flower-power-cloud.js diff --git a/.gitignore b/.gitignore index 52019cc..a72b52e 100644 --- a/.gitignore +++ b/.gitignore @@ -13,5 +13,3 @@ results npm-debug.log node_modules - -test2.js diff --git a/ApiError.js b/ApiError.js new file mode 100644 index 0000000..4ca48e4 --- /dev/null +++ b/ApiError.js @@ -0,0 +1,28 @@ +function ApiError(code, body) { + this.code = code; + this.errors = []; + + if (typeof body.errors != 'undefined' && body.errors.length > 0) { + this.errors = body.errors; + } + else this.errors.push(body); +} + +ApiError.prototype = new Error; + +ApiError.prototype.toString = function() { + var str = "CODE: " + this.code; + + for (var i = 0; i < this.errors.length; i++) { + str += "\n"; + if (typeof this.errors[i].error != '!undefined' && typeof this.errors[i].error_description != 'undefined') { + str += this.errors[i].error + ": " + this.errors[i].error_description; + } + else { + str += this.errors[i].error_code + ": " + this.errors[i].error_message; + } + } + return (str); +} + +module.exports = ApiError; diff --git a/FlowerPowerCloud.js b/FlowerPowerCloud.js new file mode 100644 index 0000000..6be5908 --- /dev/null +++ b/FlowerPowerCloud.js @@ -0,0 +1,153 @@ +var ApiError = require('./ApiError'); +var request = require('request'); +var qs = require('querystring'); +var clc = require('cli-color'); +var schedule = require('node-schedule'); + +var DEBUG = false; + +function FlowerPowerCloud() { + this._token = {}; + this._isLogged = false; + this.credentials = {}; + + var self = this; + var api = { + 'getGarden': {method: 'GET/json', path: '/sensor_data/v4/garden_locations_status', auth: true}, + 'getProfile': {method: 'GET/json', path: '/user/v4/profile', auth: true}, + 'sendSamples': {method: 'PUT/json', path: '/sensor_data/v5/sample', auth: true} + }; + + for (var item in api) { + self.makeReqFunction(item, api[item]); + } + return this; +}; + +FlowerPowerCloud.url = 'https://apiflowerpower.parrot.com'; + +FlowerPowerCloud.prototype.makeReqFunction = function(name, req) { + var self = this; + + FlowerPowerCloud.prototype[name] = function(data, callback) { + self.invoke(req, data, callback); + }; +}; + +FlowerPowerCloud.prototype.makeHeader = function(req, data) { + var options = {headers: {}}; + var verb = req.method.split('/')[0]; + var type = req.method.split('/')[1]; + + switch (type) { + case 'urlencoded': + options.body = qs.stringify(data); + options.headers['Content-Type'] = 'application/x-www-form-urlencoded'; + break; + case 'json': + options.body = JSON.stringify(data); + options.headers['Content-Type'] = 'application/json'; + break; + default: + options.body = data; + options.headers['Content-Type'] = 'text/plain'; + break; + } + + options.url = FlowerPowerCloud.url + req.path; + options.method = verb; + options.headers['Authorization'] = (req.auth) ? "Bearer " + this._token.access_token : ""; + + return options; +}; + +FlowerPowerCloud.prototype.makeUrl = function(req, data) { + var self = this; + + if (data) { + for (var item in data.url) { + req.path = req.path.replace(':' + item, data.url[item]); + } + delete data.url; + } + if (DEBUG) self.loggerReq(req, data); + return req; +}; + +FlowerPowerCloud.prototype.loggerReq = function(req, data) { + console.log(clc.yellow(req.method), clc.green(req.path)); + for (var key in data) { + console.log(clc.xterm(45)(clc.underline(key + ":"), data[key])); + } +}; + +FlowerPowerCloud.prototype.invoke = function(req, data, callback) { + var options = {}; + var self = this; + + if (typeof data == 'function') { + callback = data; + data = null; + } + req = self.makeUrl(req, data); + options = self.makeHeader(req, data); + + if (DEBUG) console.log(options); + request(options, function(err, res, body) { + if (err) callback(err); + else if (res.statusCode != 200 || (typeof body.errors != 'undefined' && body.errors.length > 0)) { var errorContent = null; + + if (typeof body == 'object') errorContent = JSON.parse(body); + else errorContent = body; + return callback(new ApiError(res.statusCode, errorContent)); + } + else if (callback) { + return callback(null, JSON.parse(body)); + } + else throw "Give me a callback"; + }); +}; + +FlowerPowerCloud.prototype.login = function(data, callback) { + var req = {method: 'POST/urlencoded', path: '/user/v2/authenticate'}; + var self = this; + + self.credentials = data; + data['grant_type'] = 'password'; + self.invoke(req, data, function(err, res) { + if (err) callback(err); + else self.getToken(res, callback); + }); +}; + +FlowerPowerCloud.prototype.getToken = function(token, callback) { + var self = this; + + self._token = token; + self._isLogged = true; + var job = new schedule.Job(function() { + self.refresh(token); + }); + job.schedule(new Date(Date.now() + (token['expires_in'] - 1440) * 1000)); + if (typeof callback != 'undefined') callback(null, token); +} + +FlowerPowerCloud.prototype.refresh = function(token) { + var req = {method: 'POST/urlencoded', path: '/user/v2/authenticate'}; + var self = this; + + var data = { + 'client_id': self.credentials['client_id'], + 'client_secret': self.credentials['client_secret'], + 'refresh_token': token.refresh_token, + 'grant_type': 'refresh_token' + }; + + self.invoke(req, data, function(err, res) { + if (err) callback(err); + else self.getToken(res); + }); +}; + + +module.exports = FlowerPowerCloud; diff --git a/README.md b/README.md index 7955546..493e568 100644 --- a/README.md +++ b/README.md @@ -15,44 +15,41 @@ You will need OAuth tokens and a Flower Power account: launch the [iOS](https://itunes.apple.com/us/app/apple-store/id712479884), and follow the directions to create an account. (Apparently there isn't an Android app yet). - -Install -------- - - npm install flower-power-cloud - API --- ### Load - - var CloudAPI = require('flower-power-cloud'); +```js +var FlowerPowerCloud = require('./FlowerPowerCloud'); +var api = new FlowerPowerCloud(); +``` ### Login to cloud - - var clientID = '...' - , clientSecret = '...' - , userName = '...' - , passPhrase = '...' - , api - ; - - api = new CloudAPI.CloudAPI({ clientID : clientID - , clientSecret : clientSecret }).login(userName, passPhrase, function(err) { - if (!!err) return console.log('login error: ' + err.message); - - // otherwise, good to go! - }).on('error', function(err) { - console.log('background error: ' + err.message); - }); +```js +var credential = { + 'username' : "...", + 'password' : "...", + 'client_id' : "...", + 'client_secret' : "...", +}; + +api.login(credential, function(err, res) { + if (err) console.log(err); + else { + // Head in the clouds :) + } +}); +``` ### Get garden information - - flower-power-cloud.getGarden(function(err, plants, sensors) { - if (!!err) return console.log('getGarden: ' + err.message); - - // inspect plants{} and sensors{} - } +```js +api.getGarden(function(err, garden) { + if (err) console.log(err); + else { + // Beautiful flowers! + } +}); +``` Finally ------- diff --git a/flower-power-cloud.js b/flower-power-cloud.js deleted file mode 100644 index ec76768..0000000 --- a/flower-power-cloud.js +++ /dev/null @@ -1,307 +0,0 @@ -// flower-power-cloud.js -// cf., https://github.com/parrot-flower-power/parrot-flower-power-api-example - - -var https = require('https') - , events = require('events') - , querystring = require('querystring') - , url = require('url') - , util = require('util') - , request = require('request') - ; - - -var DEFAULT_LOGGER = { error : function(msg, props) { console.log(msg); if (!!props) console.log(props); } - , warning : function(msg, props) { console.log(msg); if (!!props) console.log(props); } - , notice : function(msg, props) { console.log(msg); if (!!props) console.log(props); } - , info : function(msg, props) { console.log(msg); if (!!props) console.log(props); } - , debug : function(msg, props) { console.log(msg); if (!!props) console.log(props); } - }; - - -var CloudAPI = function(options) { - var k; - - var self = this; - - if (!(self instanceof CloudAPI)) return new CloudAPI(options); - - self.options = options; - - self.logger = self.options.logger || {}; - for (k in DEFAULT_LOGGER) { - if ((DEFAULT_LOGGER.hasOwnProperty(k)) && (typeof self.logger[k] === 'undefined')) self.logger[k] = DEFAULT_LOGGER[k]; - } - - self.oauth = {}; -}; -util.inherits(CloudAPI, events.EventEmitter); - - -CloudAPI.prototype.login = function(username, passphrase, callback) { - var json; - - var self = this; - - if (typeof callback !== 'function') throw new Error('callback is mandatory for login'); - - json = { username : username - , client_secret : self.options.clientSecret - , password : passphrase - , client_id : self.options.clientID - , grant_type : 'password' - }; - self.invoke('POST', '/user/v1/authenticate', json, function(err, code, results) { - if (!!err) callback(err); - - if (code !== 200) return callback(new Error('invalid credentials: code=' + code + ' results=' + JSON.stringify(results))); - - self.oauth = results; - -/* - if (!!self.timer) { - clearTimeout(self.timer); - delete(self.timer); - } - if (!!results.expires_in) self.timer = setTimeout(function() { self._refresh(self); }, (results.expires_in - 120) * 1000); - */ - - callback(null); - }); - - return self; -}; - -CloudAPI.prototype._refresh = function(self, callback) { - var json; - - delete(self.timer); - - if (!callback) { - callback = function(err) { - if (!!err) return self.logger.error('refresh', { exception: err }); - - self.logger.info('refresh', { status: 'success' }); - }; - } - - json = { client_id : self.options.clientID - , client_secret : self.options.clientSecret - , refresh_token : self.oauth.refresh_token - , grant_type : 'refresh_token' - }; - self.invoke('POST', '/user/v1/authenticate', json, function(err, code, results) { - if (!!err) callback(err); - - if (code !== 200) return callback(new Error('invalid credentials: code=' + code + 'results=' + JSON.stringify(results))); - - self.oauth = results; - - if (!!results.expires_in) self.timer = setTimeout(function() { self._refresh(self); }, (results.expires_in - 120) * 1000); - - callback(null); - }); - - return self; -}; - - -CloudAPI.prototype.getGarden = function(callback) { - var self = this; - var tabSensorSerial = []; - var tabHistoryIndex = []; - - return self.invoke('GET', '/sensor_data/v3/sync?include_s3_urls=1', function(err, code, results) { - var count, i, location, locations, sensor, sensors, user_config_version; - - if (!!err) return callback(err); - - var f = function(id) { - return function(err, results) { - if (!!err) self.logger.error('invoke', { event: 'sync', diagnostic: err.message }); - else locations[id].samples = results.samples; - - if (--count === 0) callback(null, locations, sensors, tabSensorSerial, tabHistoryIndex, user_config_version); - }; - }; - - count = 0; - user_config_version = results.user_config_version; - - sensors = {}; - for (i = 0; i < results.sensors.length; i++) { - sensor = results.sensors[i]; - tabSensorSerial.push(sensor.sensor_serial); - sensors[sensor.sensor_serial] = sensor; - } - - count++; - self.roundtrip('GET', '/sensor_data/v4/garden_locations_status', function(err, results) { - var i, id; - - if (!!err) self.logger.error('invoke', { event: 'garden_locations_status', diagnostic: err.message }); - else { - for (i = 0; i < results.locations.length; i++) { - location = results.locations[i]; - id = location.location_identifier; - delete(location.location_identifier); - - for (i = 0; i < results.sensors.length; i++) { - sensor = results.sensors[i]; - tabHistoryIndex.push(sensor.current_history_index); - } - - } - } - - if (--count === 0) callback(null, sensors, tabSensorSerial, tabHistoryIndex, user_config_version); - }); - }); -}; - -CloudAPI.prototype.uploadGarden = function (sensor_serial, user_config_version, sUpTime, history, currentID, sessionPeriod, sessionStartIndex, callback) { - -var self = this; -var json1 = { - client_datetime_utc : new Date(), - user_config_version : user_config_version, - tmz_offset : (new Date()).getTimezoneOffset(), - - session_histories : [{ - sensor_serial : sensor_serial, - session_id : currentID, - sample_measure_period : sessionPeriod, - sensor_startup_timestamp_utc : sUpTime, - session_start_index : sessionStartIndex - }], - - uploads : [{ - sensor_serial : sensor_serial, - upload_timestamp_utc : new Date(), - buffer_base64 : history, - app_version : "", - sensor_fw_version : "", - sensor_hw_identifier : "", - }] - }; - - request({ - proxy: undefined, - method: 'PUT', - uri: 'https://apiflowerpower.parrot.com/sensor_data/v5/sample', - headers: {Authorization : "Bearer " + self.oauth.access_token}, - body: (json1 !== null)? JSON.stringify(json1) : undefined - }, function (error, response, body) { - if (error !== null) { - callback(error, null); - } else if (response.statusCode !== 200) { - var htmlError = self.manageHtmlErrors(response); - if (typeof (callback) === 'function') { - callback(htmlError, body); - } - } else { - body = JSON.parse(body); - if((body.hasOwnProperty("errors") && (body.errors.length > 0))){ - callback(body.errors[0], null); - } - else { - if (self.formatter instanceof Function){ - body = self.formatter(body); - } - if (callback instanceof Function) { - callback(null, body); - } - } - } - }); -}; - - -CloudAPI.prototype.roundtrip = function(method, path, json, callback) { - var self = this; - - if ((!callback) && (typeof json === 'function')) { - callback = json; - json = null; - } - - return self.invoke(method, path, json, function(err, code, results) { - var errors; - - if (!!err) return callback(err); - - errors = (!!results.errors) && util.isArray(results.errors) && (results.errors.length > 0) && results.errors; - if (!!errors) { - return callback(new Error('invalid response: ' + JSON.stringify(!!errors ? errors : results))); - } - - callback(null, results); - }); -}; - -CloudAPI.prototype.invoke = function(method, path, json, callback) { - var options; - - var self = this; - - if ((!callback) && (typeof json === 'function')) { - callback = json; - json = null; - } - if (!callback) { - callback = function(err, results) { - if (!!err) self.logger.error('invoke', { exception: err }); else self.logger.info(path, { results: results }); - }; - } - - options = url.parse('https://apiflowerpower.parrot.com' + path); - options.agent = false; - options.method = method; - options.rejectUnauthorized = false; // self-signed certificate? - options.headers = {}; - if ((!!self.oauth.access_token) && ((!json) || (!json.grant_type))) { - options.headers.Authorization = 'Bearer ' + self.oauth.access_token; - } - if (!!json) { - options.headers['Content-Type'] = 'application/x-www-form-urlencoded'; - json = querystring.stringify(json); - options.headers['Content-Length'] = Buffer.byteLength(json); - } - - https.request(options, function(response) { - var body = ''; - - response.on('data', function(chunk) { - body += chunk.toString(); - }).on('end', function() { - var expected = { GET : [ 200 ] - , PUT : [ 200 ] - , POST : [ 200, 201, 202 ] - , DELETE : [ 200 ] - }[method]; - - var results = {}; - - try { results = JSON.parse(body); } catch(ex) { - self.logger.error(path, { event: 'json', diagnostic: ex.message, body: body }); - return callback(ex, response.statusCode); - } - - if (expected.indexOf(response.statusCode) === -1) { - self.logger.error(path, { event: 'https', code: response.statusCode, body: body }); - return callback(new Error('HTTP response ' + response.statusCode), response.statusCode, results); - } - - callback(null, response.statusCode, results); - }).on('close', function() { - callback(new Error('premature end-of-file')); - }).setEncoding('utf8'); - }).on('error', function(err) { - callback(err); - }).end(json); - - return self; -}; - - -exports.CloudAPI = CloudAPI; diff --git a/package.json b/package.json index 2e118ad..ea1b209 100644 --- a/package.json +++ b/package.json @@ -1,22 +1,30 @@ { - "name" : "flower-power-cloud" -, "version" : "0.2.1" -, "description" : "A node.js module to interface with the cloud service for the Parrot Flower Power" -, "author" : { - "name" : "Marshall Rose" - , "email" : "mrose17@gmail.com" - } -, "repository" : { - "type" : "git" - , "url" : "https://github.com/TheThingSystem/node-flower-power-cloud.git" - } -, "bugs" : { - "url" : "https://github.com/TheThingSystem/node-flower-power-cloud/issues" - } -, "keywords" : [ "Parrot", "Flower Power" ] -, "main" : "./flower-power-cloud.js" -, "engines" : { - "node" : ">=0.8" - } -, "readmeFilename" : "README.md" + "name": "flower-power-cloud", + "version": "1.0,0", + "description": "A node.js module to interface with the cloud service for the Parrot Flower Power", + "author": { + "name": "Bruno Sautron", + "email": "sautron.brunojj@gmail.com" + }, + "repository": { + "type": "git", + "url": "https://github.com/Parrot-Developers/node-flower-power-cloud.git" + }, + "bugs": { + "url": "https://github.com/Parrot-Developers/node-flower-power-cloud/issues" + }, + "keywords": [ + "Parrot", + "Flower Power" + ], + "main": "./FlowerPowerCloud.js", + "engines": { + "node": ">=0.8" + }, + "readmeFilename": "README.md", + "dependencies": { + "cli-color": "^1.1.0", + "node-schedule": "^0.6.0", + "request": "^2.67.0" + } } diff --git a/test.js b/test.js index 3c9a4a2..1cc60e8 100644 --- a/test.js +++ b/test.js @@ -1,21 +1,20 @@ -var CloudAPI = require('./flower-power-cloud'); +var FlowerPowerCloud = require('./FlowerPowerCloud'); +var async = require('async'); -var clientID = '...' - , clientSecret = '...' - , userName = '...' - , passPhrase = '...' - , api - ; +var api = new FlowerPowerCloud(); -api = new CloudAPI.CloudAPI({ clientID: clientID, clientSecret: clientSecret }).login(userName, passPhrase, function(err) { - if (!!err) return console.log('login error: ' + err.message); +var credential = { + 'username' : "...", + 'password' : "...", + 'client_id' : "...", + 'client_secret' : "...", +}; - api.getGarden(function(err, plants, sensors) { - if (!!err) return console.log('getGarden: ' + err.message); - - console.log('plants:'); console.log(plants); - console.log('sensors:'); console.log(sensors); - }); -}).on('error', function(err) { - console.log('background error: ' + err.message); +api.login(credential, function(err, res) { + if (err) console.log(err); + else { + api.getGarden(function(err, res) { + console.log(res); + }); + } }); From 1a3821d62afff0512c8c594a730ac30247d48181 Mon Sep 17 00:00:00 2001 From: bsautron Date: Tue, 8 Dec 2015 15:33:24 +0100 Subject: [PATCH 04/40] version 1.0.0 --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index ea1b209..9a627cb 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "flower-power-cloud", - "version": "1.0,0", + "version": "1.0.0", "description": "A node.js module to interface with the cloud service for the Parrot Flower Power", "author": { "name": "Bruno Sautron", From bc38c6d4dd9279d798581b0fd32270f6c489cb04 Mon Sep 17 00:00:00 2001 From: bsautron Date: Thu, 17 Dec 2015 10:05:31 +0100 Subject: [PATCH 05/40] Concat json and new path to get image --- FlowerPowerCloud.js | 61 +++++++++++++++++++++++++++++++++++++++++---- package.json | 2 +- 2 files changed, 57 insertions(+), 6 deletions(-) diff --git a/FlowerPowerCloud.js b/FlowerPowerCloud.js index 6be5908..9ec8a05 100644 --- a/FlowerPowerCloud.js +++ b/FlowerPowerCloud.js @@ -1,4 +1,5 @@ var ApiError = require('./ApiError'); +var async = require('async'); var request = require('request'); var qs = require('querystring'); var clc = require('cli-color'); @@ -13,9 +14,10 @@ function FlowerPowerCloud() { var self = this; var api = { - 'getGarden': {method: 'GET/json', path: '/sensor_data/v4/garden_locations_status', auth: true}, + 'getSyncGarden': {method: 'GET/json', path: '/sensor_data/v4/garden_locations_status', auth: true}, 'getProfile': {method: 'GET/json', path: '/user/v4/profile', auth: true}, - 'sendSamples': {method: 'PUT/json', path: '/sensor_data/v5/sample', auth: true} + 'sendSamples': {method: 'PUT/json', path: '/sensor_data/v5/sample', auth: true}, + 'getSyncData': {method: 'GET/json', path: '/sensor_data/v3/sync', auth: true} }; for (var item in api) { @@ -95,14 +97,32 @@ FlowerPowerCloud.prototype.invoke = function(req, data, callback) { if (DEBUG) console.log(options); request(options, function(err, res, body) { if (err) callback(err); - else if (res.statusCode != 200 || (typeof body.errors != 'undefined' && body.errors.length > 0)) { var errorContent = null; - + else if (res.statusCode != 200 || (typeof body.errors != 'undefined' && body.errors.length > 0)) { + var errorContent = null; if (typeof body == 'object') errorContent = JSON.parse(body); else errorContent = body; return callback(new ApiError(res.statusCode, errorContent)); } else if (callback) { - return callback(null, JSON.parse(body)); + var results = JSON.parse(body); + + if (typeof results.sensors != 'undefined') { + var sensors = {}; + for (var i = 0; i < results.sensors.length; i++) { + sensors[results.sensors[i].sensor_serial] = results.sensors[i]; + } + results.sensors = sensors; + } + if (typeof results.locations != 'undefined') { + var locations = {}; + for (var i = 0; i < results.locations.length; i++) { + locations[results.locations[i].sensor_serial] = results.locations[i]; + } + results.locations = locations; + } + results.sensors = self.concatJson(results.sensors, results.locations); + delete results.locations; + return callback(null, results); } else throw "Give me a callback"; }); @@ -149,5 +169,36 @@ FlowerPowerCloud.prototype.refresh = function(token) { }); }; +FlowerPowerCloud.prototype.getGarden = function(callback) { + var self = this; + + async.parallel({ + syncGarden: function(callback) { + self.getSyncGarden(callback); + }, + syncData: function(callback) { + self.getSyncData(callback); + } + }, function(err, res) { + if (err) callback(err); + else callback(null, self.concatJson(res.syncData, res.syncGarden)); + }); +}; + +FlowerPowerCloud.prototype.concatJson = function(json1, json2) { + var dest = json1; + var self = this; + + for (var key in json2) { + if (typeof json1[key] == 'object' && typeof json2[key] == 'object') { + dest[key] = self.concatJson(json1[key], json2[key]); + } + else { + dest[key] = json2[key]; + } + } + return dest; +} + module.exports = FlowerPowerCloud; diff --git a/package.json b/package.json index 9a627cb..61efb8c 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "flower-power-cloud", - "version": "1.0.0", + "version": "1.1.0", "description": "A node.js module to interface with the cloud service for the Parrot Flower Power", "author": { "name": "Bruno Sautron", From 6127e882456f34f0794eb9415670cc93912553c2 Mon Sep 17 00:00:00 2001 From: bsautron Date: Fri, 18 Dec 2015 12:26:15 +0100 Subject: [PATCH 06/40] New name: flower-power-api --- README.md | 2 +- package.json | 59 +++++++++++++++++++++++++++------------------------- 2 files changed, 32 insertions(+), 29 deletions(-) diff --git a/README.md b/README.md index 493e568..33b8b82 100644 --- a/README.md +++ b/README.md @@ -1,4 +1,4 @@ -node-flower-power-cloud +flower-power-api ======================= A node.js module to interface with the [cloud service](https://github.com/parrot-flower-power/parrot-flower-power-api-example) diff --git a/package.json b/package.json index 61efb8c..eee63d9 100644 --- a/package.json +++ b/package.json @@ -1,30 +1,33 @@ { - "name": "flower-power-cloud", - "version": "1.1.0", - "description": "A node.js module to interface with the cloud service for the Parrot Flower Power", - "author": { - "name": "Bruno Sautron", - "email": "sautron.brunojj@gmail.com" - }, - "repository": { - "type": "git", - "url": "https://github.com/Parrot-Developers/node-flower-power-cloud.git" - }, - "bugs": { - "url": "https://github.com/Parrot-Developers/node-flower-power-cloud/issues" - }, - "keywords": [ - "Parrot", - "Flower Power" - ], - "main": "./FlowerPowerCloud.js", - "engines": { - "node": ">=0.8" - }, - "readmeFilename": "README.md", - "dependencies": { - "cli-color": "^1.1.0", - "node-schedule": "^0.6.0", - "request": "^2.67.0" - } + "name": "flower-power-api", + "version": "1.1.0", + "description": "A node.js module which use the Parrot Flower Power API", + "author": "Bruno Sautron ", + "repository": { + "type": "git", + "url": "git+https://github.com/Parrot-Developers/node-flower-power-cloud.git" + }, + "bugs": { + "url": "https://github.com/Parrot-Developers/node-flower-power-cloud/issues" + }, + "keywords": [ + "Parrot", + "Flower Power" + ], + "main": "./FlowerPowerCloud.js", + "engines": { + "node": ">=0.8" + }, + "dependencies": { + "node-schedule": "^0.6.0", + "request": "^2.67.0" + }, + "homepage": "https://github.com/Parrot-Developers/node-flower-power-cloud#readme", + "scripts": { + "test": "echo \"Error: no test specified\" && exit 1" + }, + "license": "ISC", + "devDependencies": { + "cli-color": "^1.1.0" + } } From 4e5f13888472740297e1c53fc45b6abd41c4f897 Mon Sep 17 00:00:00 2001 From: Bruno Date: Fri, 18 Dec 2015 16:39:44 +0100 Subject: [PATCH 07/40] Update README.md --- README.md | 41 ++++++++++++++++++++++++++++++++--------- 1 file changed, 32 insertions(+), 9 deletions(-) diff --git a/README.md b/README.md index 33b8b82..5973f00 100644 --- a/README.md +++ b/README.md @@ -18,10 +18,15 @@ launch the [iOS](https://itunes.apple.com/us/app/apple-store/id712479884), and f API --- +### Install +```bash +$ npm install flower-power-api +``` + ### Load ```js -var FlowerPowerCloud = require('./FlowerPowerCloud'); -var api = new FlowerPowerCloud(); +var FlowerPowerApi = require('flower-power-api'); +var api = new FlowerPowerApi(); ``` ### Login to cloud @@ -41,14 +46,32 @@ api.login(credential, function(err, res) { }); ``` -### Get garden information +### Get garden configuration ```js -api.getGarden(function(err, garden) { - if (err) console.log(err); - else { - // Beautiful flowers! - } -}); +api.getGarden(function(error, garden)); +``` + +### Communicate with Cloud +Every method have the sema pattern: +```js +api.methodName([data,] callback); + +typeof data == object // json +callback == function(error, results); + +// Call them +api.getGarden(callback); +api.getSyncGarden(callback); +api.getSyncData(callback); +api.sendSamples(data, callback); + +// More details into ./FlowerPowerCloud.js +var api = { + 'getSyncGarden': {method: 'GET/json', path: '/sensor_data/v4/garden_locations_status', auth: true}, + 'getProfile': {method: 'GET/json', path: '/user/v4/profile', auth: true}, + 'sendSamples': {method: 'PUT/json', path: '/sensor_data/v5/sample', auth: true}, + 'getSyncData': {method: 'GET/json', path: '/sensor_data/v3/sync', auth: true} +}; ``` Finally From 0036bdda49bb49a7dd36536207cb910c472b578a Mon Sep 17 00:00:00 2001 From: bsautron Date: Fri, 18 Dec 2015 16:51:43 +0100 Subject: [PATCH 08/40] 1.1.1 --- package.json | 62 ++++++++++++++++++++++++++-------------------------- 1 file changed, 31 insertions(+), 31 deletions(-) diff --git a/package.json b/package.json index eee63d9..6ba277d 100644 --- a/package.json +++ b/package.json @@ -1,33 +1,33 @@ { - "name": "flower-power-api", - "version": "1.1.0", - "description": "A node.js module which use the Parrot Flower Power API", - "author": "Bruno Sautron ", - "repository": { - "type": "git", - "url": "git+https://github.com/Parrot-Developers/node-flower-power-cloud.git" - }, - "bugs": { - "url": "https://github.com/Parrot-Developers/node-flower-power-cloud/issues" - }, - "keywords": [ - "Parrot", - "Flower Power" - ], - "main": "./FlowerPowerCloud.js", - "engines": { - "node": ">=0.8" - }, - "dependencies": { - "node-schedule": "^0.6.0", - "request": "^2.67.0" - }, - "homepage": "https://github.com/Parrot-Developers/node-flower-power-cloud#readme", - "scripts": { - "test": "echo \"Error: no test specified\" && exit 1" - }, - "license": "ISC", - "devDependencies": { - "cli-color": "^1.1.0" - } + "name": "flower-power-api", + "version": "1.1.1", + "description": "A node.js module which use the Parrot Flower Power API", + "author": "Bruno Sautron ", + "repository": { + "type": "git", + "url": "git+https://github.com/Parrot-Developers/node-flower-power-cloud.git" + }, + "bugs": { + "url": "https://github.com/Parrot-Developers/node-flower-power-cloud/issues" + }, + "keywords": [ + "Parrot", + "Flower Power" + ], + "main": "./FlowerPowerCloud.js", + "engines": { + "node": ">=0.8" + }, + "dependencies": { + "node-schedule": "^0.6.0", + "request": "^2.67.0" + }, + "homepage": "https://github.com/Parrot-Developers/node-flower-power-cloud#readme", + "scripts": { + "test": "echo \"Error: no test specified\" && exit 1" + }, + "license": "ISC", + "devDependencies": { + "cli-color": "^1.1.0" + } } From afd1fc48d789c76550aa3ad95ab4027151568094 Mon Sep 17 00:00:00 2001 From: bsautron Date: Fri, 18 Dec 2015 16:52:16 +0100 Subject: [PATCH 09/40] v1.1.2 --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 6ba277d..d303a99 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "flower-power-api", - "version": "1.1.1", + "version": "1.1.2", "description": "A node.js module which use the Parrot Flower Power API", "author": "Bruno Sautron ", "repository": { From 018b1acf199f35f4df5653c7719e2e5974cdc99b Mon Sep 17 00:00:00 2001 From: bsautron Date: Fri, 18 Dec 2015 16:52:31 +0100 Subject: [PATCH 10/40] 1.1.3 --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index d303a99..28fe4eb 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "flower-power-api", - "version": "1.1.2", + "version": "1.1.3", "description": "A node.js module which use the Parrot Flower Power API", "author": "Bruno Sautron ", "repository": { From e88c05d7c05c44236291c5167fda4cb984a19ffb Mon Sep 17 00:00:00 2001 From: bsautron Date: Fri, 18 Dec 2015 16:55:17 +0100 Subject: [PATCH 11/40] 1.1.4 --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 28fe4eb..c30818a 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "flower-power-api", - "version": "1.1.3", + "version": "1.1.4", "description": "A node.js module which use the Parrot Flower Power API", "author": "Bruno Sautron ", "repository": { From 39bad31f268c684e16cfb00c2f17e19db9e02acf Mon Sep 17 00:00:00 2001 From: bsautron Date: Fri, 18 Dec 2015 16:58:01 +0100 Subject: [PATCH 12/40] 1.1.5 --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index c30818a..702ddb6 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "flower-power-api", - "version": "1.1.4", + "version": "1.1.5", "description": "A node.js module which use the Parrot Flower Power API", "author": "Bruno Sautron ", "repository": { From e6e213d512d15e062dc570b49224be7337c0d6bc Mon Sep 17 00:00:00 2001 From: bsautron Date: Fri, 18 Dec 2015 17:52:17 +0100 Subject: [PATCH 13/40] async required --- package.json | 1 + 1 file changed, 1 insertion(+) diff --git a/package.json b/package.json index 702ddb6..a7e83bc 100644 --- a/package.json +++ b/package.json @@ -19,6 +19,7 @@ "node": ">=0.8" }, "dependencies": { + "async": "^1.5.0", "node-schedule": "^0.6.0", "request": "^2.67.0" }, From 4b42234a0c99b013a44ad47d5af2764240d96105 Mon Sep 17 00:00:00 2001 From: bsautron Date: Fri, 18 Dec 2015 17:52:39 +0100 Subject: [PATCH 14/40] 1.1.6 --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index a7e83bc..c0b1931 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "flower-power-api", - "version": "1.1.5", + "version": "1.1.6", "description": "A node.js module which use the Parrot Flower Power API", "author": "Bruno Sautron ", "repository": { From dac8aeda43e7f77c60efe3aadec58f519dd5fa5d Mon Sep 17 00:00:00 2001 From: bsautron Date: Tue, 29 Dec 2015 16:14:55 +0100 Subject: [PATCH 15/40] Eject null and undefined sensors --- FlowerPowerCloud.js | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/FlowerPowerCloud.js b/FlowerPowerCloud.js index 9ec8a05..cdf0936 100644 --- a/FlowerPowerCloud.js +++ b/FlowerPowerCloud.js @@ -109,14 +109,18 @@ FlowerPowerCloud.prototype.invoke = function(req, data, callback) { if (typeof results.sensors != 'undefined') { var sensors = {}; for (var i = 0; i < results.sensors.length; i++) { - sensors[results.sensors[i].sensor_serial] = results.sensors[i]; + if (typeof results.sensors[i].sensor_serial != 'undefined' && results.sensors[i].sensor_serial) { + sensors[results.sensors[i].sensor_serial] = results.sensors[i]; + } } results.sensors = sensors; } if (typeof results.locations != 'undefined') { var locations = {}; for (var i = 0; i < results.locations.length; i++) { - locations[results.locations[i].sensor_serial] = results.locations[i]; + if (typeof results.locations[i].sensor_serial != 'undefined' && results.locations[i].sensor_serial) { + locations[results.locations[i].sensor_serial] = results.locations[i]; + } } results.locations = locations; } From 60a11982a2f8612a8b079c61418a0a02ab016f47 Mon Sep 17 00:00:00 2001 From: bsautron Date: Tue, 29 Dec 2015 16:15:05 +0100 Subject: [PATCH 16/40] 1.1.7 --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index c0b1931..c0d3104 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "flower-power-api", - "version": "1.1.6", + "version": "1.1.7", "description": "A node.js module which use the Parrot Flower Power API", "author": "Bruno Sautron ", "repository": { From 56cd1479993ad042e5167c57a5a4ffc5f8c9af7c Mon Sep 17 00:00:00 2001 From: bsautron Date: Wed, 30 Dec 2015 14:42:13 +0100 Subject: [PATCH 17/40] Best parse error --- ApiError.js | 13 +++++++++---- FlowerPowerCloud.js | 12 +++++------- test.js | 2 +- 3 files changed, 15 insertions(+), 12 deletions(-) diff --git a/ApiError.js b/ApiError.js index 4ca48e4..cae4169 100644 --- a/ApiError.js +++ b/ApiError.js @@ -2,10 +2,11 @@ function ApiError(code, body) { this.code = code; this.errors = []; - if (typeof body.errors != 'undefined' && body.errors.length > 0) { + if (body.errors && body.errors.length > 0) { this.errors = body.errors; } else this.errors.push(body); + return (this); } ApiError.prototype = new Error; @@ -15,12 +16,16 @@ ApiError.prototype.toString = function() { for (var i = 0; i < this.errors.length; i++) { str += "\n"; - if (typeof this.errors[i].error != '!undefined' && typeof this.errors[i].error_description != 'undefined') { + + if (this.errors[i].error && this.errors[i].error_description) { str += this.errors[i].error + ": " + this.errors[i].error_description; - } - else { + } else if (this.errors[i].error_code && this.errors[i].error_message) { str += this.errors[i].error_code + ": " + this.errors[i].error_message; + } else { + var key = Object.keys(this.errors[i])[0]; + str += key + ": " + this.errors[i][key]; } + } return (str); } diff --git a/FlowerPowerCloud.js b/FlowerPowerCloud.js index cdf0936..84bb458 100644 --- a/FlowerPowerCloud.js +++ b/FlowerPowerCloud.js @@ -96,15 +96,13 @@ FlowerPowerCloud.prototype.invoke = function(req, data, callback) { if (DEBUG) console.log(options); request(options, function(err, res, body) { - if (err) callback(err); - else if (res.statusCode != 200 || (typeof body.errors != 'undefined' && body.errors.length > 0)) { - var errorContent = null; - if (typeof body == 'object') errorContent = JSON.parse(body); - else errorContent = body; - return callback(new ApiError(res.statusCode, errorContent)); + if (typeof body == 'string') body = JSON.parse(body); + if (err) callback(err, null); + else if (res.statusCode != 200 || (body.errors && body.errors.length > 0)) { + return callback(new ApiError(res.statusCode, body), null); } else if (callback) { - var results = JSON.parse(body); + var results = body; if (typeof results.sensors != 'undefined') { var sensors = {}; diff --git a/test.js b/test.js index 1cc60e8..4f29f1b 100644 --- a/test.js +++ b/test.js @@ -11,7 +11,7 @@ var credential = { }; api.login(credential, function(err, res) { - if (err) console.log(err); + if (err) console.log(err.toString()); else { api.getGarden(function(err, res) { console.log(res); From 162f678633294e3ebca33b7cacb74c41c9f42997 Mon Sep 17 00:00:00 2001 From: bsautron Date: Wed, 30 Dec 2015 14:42:27 +0100 Subject: [PATCH 18/40] 1.1.8 --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index c0d3104..69e949c 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "flower-power-api", - "version": "1.1.7", + "version": "1.1.8", "description": "A node.js module which use the Parrot Flower Power API", "author": "Bruno Sautron ", "repository": { From b81d50f36e2012a6069b95cfd41d4a13ebcced8a Mon Sep 17 00:00:00 2001 From: bsautron Date: Wed, 30 Dec 2015 19:32:36 +0100 Subject: [PATCH 19/40] Set auto refresh token --- FlowerPowerCloud.js | 21 ++++++++++++++------- test.js | 5 +++-- 2 files changed, 17 insertions(+), 9 deletions(-) diff --git a/FlowerPowerCloud.js b/FlowerPowerCloud.js index 84bb458..5bca47c 100644 --- a/FlowerPowerCloud.js +++ b/FlowerPowerCloud.js @@ -11,6 +11,7 @@ function FlowerPowerCloud() { this._token = {}; this._isLogged = false; this.credentials = {}; + this.autoRefresh = false; var self = this; var api = { @@ -134,23 +135,29 @@ FlowerPowerCloud.prototype.login = function(data, callback) { var req = {method: 'POST/urlencoded', path: '/user/v2/authenticate'}; var self = this; + if (data['auto-refresh']) { + self.autoRefresh = data['auto-refresh']; + delete data['auto-refresh']; + } self.credentials = data; data['grant_type'] = 'password'; self.invoke(req, data, function(err, res) { if (err) callback(err); - else self.getToken(res, callback); + else self.setToken(res, callback); }); }; -FlowerPowerCloud.prototype.getToken = function(token, callback) { +FlowerPowerCloud.prototype.setToken = function(token, callback) { var self = this; self._token = token; self._isLogged = true; - var job = new schedule.Job(function() { - self.refresh(token); - }); - job.schedule(new Date(Date.now() + (token['expires_in'] - 1440) * 1000)); + if (self.autoRefresh) { + var job = new schedule.Job(function() { + self.refresh(token); + }); + job.schedule(new Date(Date.now() + (token['expires_in'] - 1440) * 1000)); + } if (typeof callback != 'undefined') callback(null, token); } @@ -167,7 +174,7 @@ FlowerPowerCloud.prototype.refresh = function(token) { self.invoke(req, data, function(err, res) { if (err) callback(err); - else self.getToken(res); + else self.setToken(res); }); }; diff --git a/test.js b/test.js index 4f29f1b..f71178f 100644 --- a/test.js +++ b/test.js @@ -3,14 +3,15 @@ var async = require('async'); var api = new FlowerPowerCloud(); -var credential = { +var credentials = { 'username' : "...", 'password' : "...", 'client_id' : "...", 'client_secret' : "...", + 'auto-refresh' : true }; -api.login(credential, function(err, res) { +api.login(credentials, function(err, res) { if (err) console.log(err.toString()); else { api.getGarden(function(err, res) { From 8283ebe76090124ed0406d981221ab84b49e5e02 Mon Sep 17 00:00:00 2001 From: bsautron Date: Wed, 30 Dec 2015 19:32:47 +0100 Subject: [PATCH 20/40] 1.1.9 --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 69e949c..e58d454 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "flower-power-api", - "version": "1.1.8", + "version": "1.1.9", "description": "A node.js module which use the Parrot Flower Power API", "author": "Bruno Sautron ", "repository": { From 2a7191aee672469bb2375260b69226b48b59c8ae Mon Sep 17 00:00:00 2001 From: Bruno Date: Thu, 31 Dec 2015 11:11:01 +0100 Subject: [PATCH 21/40] Update README.md --- README.md | 27 +++++++++++---------------- 1 file changed, 11 insertions(+), 16 deletions(-) diff --git a/README.md b/README.md index 5973f00..a676e42 100644 --- a/README.md +++ b/README.md @@ -1,22 +1,18 @@ -flower-power-api -======================= +# flower-power-api + +[![NPM](https://nodei.co/npm/flower-power-api.png)](https://nodei.co/npm/flower-power-api/) A node.js module to interface with the [cloud service](https://github.com/parrot-flower-power/parrot-flower-power-api-example) for the Parrot [Flower Power](http://www.parrot.com/flowerpower/). -Before Starting ---------------- -You will need OAuth tokens and a Flower Power account: - -- To get the OAuth tokens, use this [form](https://apiflowerpower.parrot.com/api_access/signup) +## Get your access API +* `username` `password` + * Make sure you have an account created by your smartphone. You should be see your garden: [myflowerpower.parrot.com](https://myflowerpower.parrot.com). +* `client_id` `client_secret` + * [Sign up to API here](https://apiflowerpower.parrot.com/api_access/signup), and got by **email** your *Access ID* (`client_id`) and your *Access secret* (`client_secret`). -- To get a Flower power account, -launch the [iOS](https://itunes.apple.com/us/app/apple-store/id712479884), and follow the directions to create an account. -(Apparently there isn't an Android app yet). - -API ---- +## API ### Install ```bash @@ -36,6 +32,7 @@ var credential = { 'password' : "...", 'client_id' : "...", 'client_secret' : "...", + 'auto-refresh' : false }; api.login(credential, function(err, res) { @@ -74,7 +71,5 @@ var api = { }; ``` -Finally -------- - +## Finally Enjoy! From 3cb7dc2b05a2a3913fa0cfee3ab5ce5d299b99fe Mon Sep 17 00:00:00 2001 From: bsautron Date: Thu, 31 Dec 2015 17:10:10 +0100 Subject: [PATCH 22/40] Dep: cli-color --- package.json | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) diff --git a/package.json b/package.json index e58d454..07022ba 100644 --- a/package.json +++ b/package.json @@ -21,14 +21,12 @@ "dependencies": { "async": "^1.5.0", "node-schedule": "^0.6.0", - "request": "^2.67.0" + "request": "^2.67.0", + "cli-color": "^1.1.0" }, "homepage": "https://github.com/Parrot-Developers/node-flower-power-cloud#readme", "scripts": { "test": "echo \"Error: no test specified\" && exit 1" }, - "license": "ISC", - "devDependencies": { - "cli-color": "^1.1.0" - } + "license": "ISC" } From e0322cdd75d0bcde8e274dbcacc5ae3496b908c7 Mon Sep 17 00:00:00 2001 From: bsautron Date: Thu, 31 Dec 2015 17:10:15 +0100 Subject: [PATCH 23/40] 1.1.10 --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 07022ba..32a5af7 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "flower-power-api", - "version": "1.1.9", + "version": "1.1.10", "description": "A node.js module which use the Parrot Flower Power API", "author": "Bruno Sautron ", "repository": { From 02b2d9af46c0e1dcdc48c59cac21cb41ced14f56 Mon Sep 17 00:00:00 2001 From: bsautron Date: Thu, 31 Dec 2015 17:15:02 +0100 Subject: [PATCH 24/40] Dont need cli-color --- FlowerPowerCloud.js | 7 +++---- package.json | 3 +-- 2 files changed, 4 insertions(+), 6 deletions(-) diff --git a/FlowerPowerCloud.js b/FlowerPowerCloud.js index 5bca47c..659f3ae 100644 --- a/FlowerPowerCloud.js +++ b/FlowerPowerCloud.js @@ -2,10 +2,9 @@ var ApiError = require('./ApiError'); var async = require('async'); var request = require('request'); var qs = require('querystring'); -var clc = require('cli-color'); var schedule = require('node-schedule'); -var DEBUG = false; +var DEBUG = true; function FlowerPowerCloud() { this._token = {}; @@ -78,9 +77,9 @@ FlowerPowerCloud.prototype.makeUrl = function(req, data) { }; FlowerPowerCloud.prototype.loggerReq = function(req, data) { - console.log(clc.yellow(req.method), clc.green(req.path)); + console.log(req.method, req.path); for (var key in data) { - console.log(clc.xterm(45)(clc.underline(key + ":"), data[key])); + console.log(key + ":", data[key])); } }; diff --git a/package.json b/package.json index 32a5af7..294daf4 100644 --- a/package.json +++ b/package.json @@ -21,8 +21,7 @@ "dependencies": { "async": "^1.5.0", "node-schedule": "^0.6.0", - "request": "^2.67.0", - "cli-color": "^1.1.0" + "request": "^2.67.0" }, "homepage": "https://github.com/Parrot-Developers/node-flower-power-cloud#readme", "scripts": { From 7f818517f13e7ce6f1401f36b2525bc84777a188 Mon Sep 17 00:00:00 2001 From: bsautron Date: Thu, 31 Dec 2015 17:15:19 +0100 Subject: [PATCH 25/40] 1.2.0 --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 294daf4..f93c17b 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "flower-power-api", - "version": "1.1.10", + "version": "1.2.0", "description": "A node.js module which use the Parrot Flower Power API", "author": "Bruno Sautron ", "repository": { From 64a8489757021f2dfca8fb54f36c7966ac623a85 Mon Sep 17 00:00:00 2001 From: bsautron Date: Thu, 31 Dec 2015 17:17:33 +0100 Subject: [PATCH 26/40] A lot of parenthese --- FlowerPowerCloud.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/FlowerPowerCloud.js b/FlowerPowerCloud.js index 659f3ae..b722791 100644 --- a/FlowerPowerCloud.js +++ b/FlowerPowerCloud.js @@ -4,7 +4,7 @@ var request = require('request'); var qs = require('querystring'); var schedule = require('node-schedule'); -var DEBUG = true; +var DEBUG = false; function FlowerPowerCloud() { this._token = {}; @@ -79,7 +79,7 @@ FlowerPowerCloud.prototype.makeUrl = function(req, data) { FlowerPowerCloud.prototype.loggerReq = function(req, data) { console.log(req.method, req.path); for (var key in data) { - console.log(key + ":", data[key])); + console.log(key + ":", data[key]); } }; From cdc2068bb2aa994be6e6cecfa59378c06436db69 Mon Sep 17 00:00:00 2001 From: bsautron Date: Thu, 31 Dec 2015 17:17:38 +0100 Subject: [PATCH 27/40] 1.2.1 --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index f93c17b..4d12ae7 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "flower-power-api", - "version": "1.2.0", + "version": "1.2.1", "description": "A node.js module which use the Parrot Flower Power API", "author": "Bruno Sautron ", "repository": { From 3dce62d0fbe08d7224bd9da820e92e2b8797ecfb Mon Sep 17 00:00:00 2001 From: bsautron Date: Wed, 6 Jan 2016 11:46:27 +0100 Subject: [PATCH 28/40] Get sample by location --- FlowerPowerCloud.js | 30 ++++++++++++++++-------------- 1 file changed, 16 insertions(+), 14 deletions(-) diff --git a/FlowerPowerCloud.js b/FlowerPowerCloud.js index b722791..3340cd9 100644 --- a/FlowerPowerCloud.js +++ b/FlowerPowerCloud.js @@ -4,7 +4,7 @@ var request = require('request'); var qs = require('querystring'); var schedule = require('node-schedule'); -var DEBUG = false; +const DEBUG = true; function FlowerPowerCloud() { this._token = {}; @@ -17,7 +17,8 @@ function FlowerPowerCloud() { 'getSyncGarden': {method: 'GET/json', path: '/sensor_data/v4/garden_locations_status', auth: true}, 'getProfile': {method: 'GET/json', path: '/user/v4/profile', auth: true}, 'sendSamples': {method: 'PUT/json', path: '/sensor_data/v5/sample', auth: true}, - 'getSyncData': {method: 'GET/json', path: '/sensor_data/v3/sync', auth: true} + 'getSyncData': {method: 'GET/json', path: '/sensor_data/v3/sync', auth: true}, + 'getLocationSamples': {method: 'GET/json', path: '/sensor_data/v2/sample/location/:location_identifier', auth: true} }; for (var item in api) { @@ -104,26 +105,28 @@ FlowerPowerCloud.prototype.invoke = function(req, data, callback) { else if (callback) { var results = body; - if (typeof results.sensors != 'undefined') { + if (results.sensors) { var sensors = {}; - for (var i = 0; i < results.sensors.length; i++) { - if (typeof results.sensors[i].sensor_serial != 'undefined' && results.sensors[i].sensor_serial) { - sensors[results.sensors[i].sensor_serial] = results.sensors[i]; + for (var sensor of results.sensors) { + if (sensor.sensor_serial) { + sensors[sensor.sensor_serial] = sensor; } } results.sensors = sensors; } - if (typeof results.locations != 'undefined') { + if (results.locations) { var locations = {}; - for (var i = 0; i < results.locations.length; i++) { - if (typeof results.locations[i].sensor_serial != 'undefined' && results.locations[i].sensor_serial) { - locations[results.locations[i].sensor_serial] = results.locations[i]; + for (var location of results.locations) { + if (location.sensor_serial) { + locations[location.sensor_serial] = location; } } results.locations = locations; } - results.sensors = self.concatJson(results.sensors, results.locations); - delete results.locations; + if (results.sensors && results.locations) { + results.sensors = self.concatJson(results.sensors, results.locations); + delete results.locations; + } return callback(null, results); } else throw "Give me a callback"; @@ -157,7 +160,7 @@ FlowerPowerCloud.prototype.setToken = function(token, callback) { }); job.schedule(new Date(Date.now() + (token['expires_in'] - 1440) * 1000)); } - if (typeof callback != 'undefined') callback(null, token); + if (typeof callback == 'function') callback(null, token); } FlowerPowerCloud.prototype.refresh = function(token) { @@ -208,5 +211,4 @@ FlowerPowerCloud.prototype.concatJson = function(json1, json2) { return dest; } - module.exports = FlowerPowerCloud; From be5c231337bef6fcc2229a9a6f076862b1e598ae Mon Sep 17 00:00:00 2001 From: bsautron Date: Wed, 6 Jan 2016 12:13:30 +0100 Subject: [PATCH 29/40] Update readme.md --- README.md | 22 ++++++++++++++++++++-- 1 file changed, 20 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index a676e42..0f8d42c 100644 --- a/README.md +++ b/README.md @@ -53,7 +53,7 @@ Every method have the sema pattern: ```js api.methodName([data,] callback); -typeof data == object // json +typeof data == 'object' // json callback == function(error, results); // Call them @@ -67,9 +67,27 @@ var api = { 'getSyncGarden': {method: 'GET/json', path: '/sensor_data/v4/garden_locations_status', auth: true}, 'getProfile': {method: 'GET/json', path: '/user/v4/profile', auth: true}, 'sendSamples': {method: 'PUT/json', path: '/sensor_data/v5/sample', auth: true}, - 'getSyncData': {method: 'GET/json', path: '/sensor_data/v3/sync', auth: true} + 'getSyncData': {method: 'GET/json', path: '/sensor_data/v3/sync', auth: true}, + 'getLocationSamples': {method: 'GET/json', path: '/sensor_data/v2/sample/location/:location_identifier', auth: true} }; ``` +#### Param in url +```js +// Api which need parameters into url +'anExample': {method: 'GET/json', path: '/:this/is/an/:example'} + +api.anExample({ + url: { + this: 'flower', + example: 'organ' + }, + param1: '...', + param2: '...' +}, callback); + +// Become +'/flower/is/an/organ' +``` ## Finally Enjoy! From a77340bf0c539afe2e7d8180ee026538ac15ca67 Mon Sep 17 00:00:00 2001 From: bsautron Date: Wed, 6 Jan 2016 12:13:40 +0100 Subject: [PATCH 30/40] 1.3.0 --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 4d12ae7..d4d3c4a 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "flower-power-api", - "version": "1.2.1", + "version": "1.3.0", "description": "A node.js module which use the Parrot Flower Power API", "author": "Bruno Sautron ", "repository": { From 15a131f4830258c521dd666bfa674fa1c3e3ceda Mon Sep 17 00:00:00 2001 From: bsautron Date: Thu, 7 Jan 2016 16:34:31 +0100 Subject: [PATCH 31/40] New functions and doc --- ApiError.js | 16 ++++++------ FlowerPowerCloud.js | 60 +++++++++++++++++++++++++++++++++++---------- README.md | 42 +++++++++++++++++++++---------- 3 files changed, 85 insertions(+), 33 deletions(-) diff --git a/ApiError.js b/ApiError.js index cae4169..00ad608 100644 --- a/ApiError.js +++ b/ApiError.js @@ -14,16 +14,18 @@ ApiError.prototype = new Error; ApiError.prototype.toString = function() { var str = "CODE: " + this.code; - for (var i = 0; i < this.errors.length; i++) { + for (var error of this.errors) { str += "\n"; - if (this.errors[i].error && this.errors[i].error_description) { - str += this.errors[i].error + ": " + this.errors[i].error_description; - } else if (this.errors[i].error_code && this.errors[i].error_message) { - str += this.errors[i].error_code + ": " + this.errors[i].error_message; + if (error.error && error.error_description) { + str += error.error + ": " + error.error_description; + } else if (error.error_code && error.error_message) { + str += error.error_code + ": " + error.error_message; + } else if (typeof error == 'string') { + str += error; } else { - var key = Object.keys(this.errors[i])[0]; - str += key + ": " + this.errors[i][key]; + var key = Object.keys(error)[0]; + str += key + ": " + error[key]; } } diff --git a/FlowerPowerCloud.js b/FlowerPowerCloud.js index 3340cd9..267fe4e 100644 --- a/FlowerPowerCloud.js +++ b/FlowerPowerCloud.js @@ -14,11 +14,20 @@ function FlowerPowerCloud() { var self = this; var api = { - 'getSyncGarden': {method: 'GET/json', path: '/sensor_data/v4/garden_locations_status', auth: true}, + // Profile 'getProfile': {method: 'GET/json', path: '/user/v4/profile', auth: true}, + 'getUserVersions': {method: 'GET/json', path: '/user/v1/versions', auth: true}, + + // Garden + 'getSyncGarden': {method: 'GET/json', path: '/sensor_data/v4/garden_locations_status', auth: true}, 'sendSamples': {method: 'PUT/json', path: '/sensor_data/v5/sample', auth: true}, 'getSyncData': {method: 'GET/json', path: '/sensor_data/v3/sync', auth: true}, - 'getLocationSamples': {method: 'GET/json', path: '/sensor_data/v2/sample/location/:location_identifier', auth: true} + 'getFirmwareUpdate': {method: 'GET/json', path: '/sensor_data/v1/firmware_update', auth: true}, + 'getLocationSamples': {method: 'GET/json', path: '/sensor_data/v2/sample/location/:location_identifier', auth: true}, + 'getStatistics': {method: 'GET/json', path: '/sensor_data/v1/statistics/:location_identifier', auth: true}, + + // Images + 'getImageLocation': {method: 'GET/json', path: '/image/v3/location/user_images/:location_identifier', auth: true}, }; for (var item in api) { @@ -44,17 +53,17 @@ FlowerPowerCloud.prototype.makeHeader = function(req, data) { switch (type) { case 'urlencoded': - options.body = qs.stringify(data); - options.headers['Content-Type'] = 'application/x-www-form-urlencoded'; - break; + options.body = qs.stringify(data); + options.headers['Content-Type'] = 'application/x-www-form-urlencoded'; + break; case 'json': - options.body = JSON.stringify(data); - options.headers['Content-Type'] = 'application/json'; - break; + options.body = JSON.stringify(data); + options.headers['Content-Type'] = 'application/json'; + break; default: - options.body = data; - options.headers['Content-Type'] = 'text/plain'; - break; + options.body = data; + options.headers['Content-Type'] = 'text/plain'; + break; } options.url = FlowerPowerCloud.url + req.path; @@ -97,7 +106,11 @@ FlowerPowerCloud.prototype.invoke = function(req, data, callback) { if (DEBUG) console.log(options); request(options, function(err, res, body) { - if (typeof body == 'string') body = JSON.parse(body); + if (typeof body == 'string') { + try { + body = JSON.parse(body); + } catch (e) {}; + } if (err) callback(err, null); else if (res.statusCode != 200 || (body.errors && body.errors.length > 0)) { return callback(new ApiError(res.statusCode, body), null); @@ -137,7 +150,7 @@ FlowerPowerCloud.prototype.login = function(data, callback) { var req = {method: 'POST/urlencoded', path: '/user/v2/authenticate'}; var self = this; - if (data['auto-refresh']) { + if (typeof data['auto-refresh'] != 'undefined') { self.autoRefresh = data['auto-refresh']; delete data['auto-refresh']; } @@ -211,4 +224,25 @@ FlowerPowerCloud.prototype.concatJson = function(json1, json2) { return dest; } +// 'url1': { +// 'getProfile', +// 'getUserVersions', +// 'getSyncGarden', +// 'sendSamples', +// 'getSyncData', +// 'getFirmwareUpdate', +// 'getLocationSamples', +// 'getStatistics', +// }, +// 'url2': { +// 'searchName', +// 'suggest', +// 'suggestLocation' +// }, +// 'url3': { +// 'infoPlant', +// 'plantdataVersion', +// 'listPopularityPlant', +// } + module.exports = FlowerPowerCloud; diff --git a/README.md b/README.md index 0f8d42c..a102187 100644 --- a/README.md +++ b/README.md @@ -43,7 +43,7 @@ api.login(credential, function(err, res) { }); ``` -### Get garden configuration +### Get garden configuration (and specials methods) ```js api.getGarden(function(error, garden)); ``` @@ -51,25 +51,41 @@ api.getGarden(function(error, garden)); ### Communicate with Cloud Every method have the sema pattern: ```js +// For example: +'methodName': {method: 'GET/json', path: '/im/a/flower/', auth: true} +// Call like this: api.methodName([data,] callback); -typeof data == 'object' // json -callback == function(error, results); - -// Call them -api.getGarden(callback); -api.getSyncGarden(callback); -api.getSyncData(callback); -api.sendSamples(data, callback); - -// More details into ./FlowerPowerCloud.js +// 'data' is optional, 'callback' is required +data = { + url: {} + param1, + param2, + ... +} +callback = function(error, results); +``` +```js +// Find all methods in ./FlowerPowerCloud.js var api = { - 'getSyncGarden': {method: 'GET/json', path: '/sensor_data/v4/garden_locations_status', auth: true}, + // Profile + 'login': {method: 'POST/urlencoded', path: '/user/v2/authenticate', auth: false}, + 'refresh': {method: 'POST/urlencoded', path: '/user/v2/authenticate', auth: false}, 'getProfile': {method: 'GET/json', path: '/user/v4/profile', auth: true}, + 'getUserVersions': {method: 'GET/json', path: '/user/v1/versions', auth: true}, + + // Garden + 'getSyncGarden': {method: 'GET/json', path: '/sensor_data/v4/garden_locations_status', auth: true}, 'sendSamples': {method: 'PUT/json', path: '/sensor_data/v5/sample', auth: true}, 'getSyncData': {method: 'GET/json', path: '/sensor_data/v3/sync', auth: true}, - 'getLocationSamples': {method: 'GET/json', path: '/sensor_data/v2/sample/location/:location_identifier', auth: true} + 'getFirmwareUpdate': {method: 'GET/json', path: '/sensor_data/v1/firmware_update', auth: true}, + 'getLocationSamples': {method: 'GET/json', path: '/sensor_data/v2/sample/location/:location_identifier', auth: true}, + 'getStatistics': {method: 'GET/json', path: '/sensor_data/v1/statistics/:location_identifier', auth: true}, + + // Images + 'getImageLocation': {method: 'GET/json', path: '/image/v3/location/user_images/:location_identifier', auth: true}, }; + ``` #### Param in url ```js From 7ef8bed916cb0fcf6233905e6be1a809a36f46e1 Mon Sep 17 00:00:00 2001 From: bsautron Date: Thu, 7 Jan 2016 16:34:38 +0100 Subject: [PATCH 32/40] 1.4.0 --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index d4d3c4a..bbabc31 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "flower-power-api", - "version": "1.3.0", + "version": "1.4.0", "description": "A node.js module which use the Parrot Flower Power API", "author": "Bruno Sautron ", "repository": { From cf3d6dd8144ec29edca025cddfd23fc856ed7aab Mon Sep 17 00:00:00 2001 From: bsautron Date: Wed, 13 Jan 2016 17:43:12 +0100 Subject: [PATCH 33/40] Error herited not instancified - api.verify() --- ApiError.js | 5 +++-- FlowerPowerCloud.js | 1 + 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/ApiError.js b/ApiError.js index 00ad608..a007d92 100644 --- a/ApiError.js +++ b/ApiError.js @@ -9,7 +9,8 @@ function ApiError(code, body) { return (this); } -ApiError.prototype = new Error; +ApiError.prototype = Object.create(Error.prototype); +ApiError.prototype.constructor = ApiError; ApiError.prototype.toString = function() { var str = "CODE: " + this.code; @@ -27,7 +28,7 @@ ApiError.prototype.toString = function() { var key = Object.keys(error)[0]; str += key + ": " + error[key]; } - + } return (str); } diff --git a/FlowerPowerCloud.js b/FlowerPowerCloud.js index 267fe4e..33f212f 100644 --- a/FlowerPowerCloud.js +++ b/FlowerPowerCloud.js @@ -17,6 +17,7 @@ function FlowerPowerCloud() { // Profile 'getProfile': {method: 'GET/json', path: '/user/v4/profile', auth: true}, 'getUserVersions': {method: 'GET/json', path: '/user/v1/versions', auth: true}, + 'verify': {method: 'GET/json', path: '/user/v1/verify', auth: true}, // Garden 'getSyncGarden': {method: 'GET/json', path: '/sensor_data/v4/garden_locations_status', auth: true}, From c6e0ef3eb39d5de68992199d45a04ce4618a06b7 Mon Sep 17 00:00:00 2001 From: bsautron Date: Wed, 13 Jan 2016 17:44:46 +0100 Subject: [PATCH 34/40] 1.4.1 --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index bbabc31..364f3a2 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "flower-power-api", - "version": "1.4.0", + "version": "1.4.1", "description": "A node.js module which use the Parrot Flower Power API", "author": "Bruno Sautron ", "repository": { From d21d7e8837420be9bbcb765e08b2519453c3956e Mon Sep 17 00:00:00 2001 From: bsautron Date: Sun, 17 Jan 2016 01:32:00 +0100 Subject: [PATCH 35/40] callback(err) if the data is not a json --- FlowerPowerCloud.js | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/FlowerPowerCloud.js b/FlowerPowerCloud.js index 33f212f..65b2bba 100644 --- a/FlowerPowerCloud.js +++ b/FlowerPowerCloud.js @@ -4,7 +4,7 @@ var request = require('request'); var qs = require('querystring'); var schedule = require('node-schedule'); -const DEBUG = true; +const DEBUG = false; function FlowerPowerCloud() { this._token = {}; @@ -102,6 +102,9 @@ FlowerPowerCloud.prototype.invoke = function(req, data, callback) { callback = data; data = null; } + if (data && typeof data !== 'object') { + return callback(new Error('Data is not a json')); + } req = self.makeUrl(req, data); options = self.makeHeader(req, data); From 6f798c06b0283c93c51d9e3783d1019f4af991fb Mon Sep 17 00:00:00 2001 From: bsautron Date: Sun, 17 Jan 2016 01:32:10 +0100 Subject: [PATCH 36/40] 1.4.2 --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 364f3a2..83dd394 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "flower-power-api", - "version": "1.4.1", + "version": "1.4.2", "description": "A node.js module which use the Parrot Flower Power API", "author": "Bruno Sautron ", "repository": { From 565189f2a55234bb16a185ff89f197823c3c4c2f Mon Sep 17 00:00:00 2001 From: muuuh Date: Sat, 8 Oct 2016 12:10:09 +0200 Subject: [PATCH 37/40] change url of server --- FlowerPowerCloud.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/FlowerPowerCloud.js b/FlowerPowerCloud.js index 65b2bba..df91205 100644 --- a/FlowerPowerCloud.js +++ b/FlowerPowerCloud.js @@ -37,7 +37,7 @@ function FlowerPowerCloud() { return this; }; -FlowerPowerCloud.url = 'https://apiflowerpower.parrot.com'; +FlowerPowerCloud.url = 'https://api-flower-power-pot.parrot.com'; FlowerPowerCloud.prototype.makeReqFunction = function(name, req) { var self = this; From 21e0d3fdfb23aa2c59ad079a19fed90ddcd16e7e Mon Sep 17 00:00:00 2001 From: bsautron Date: Fri, 9 Dec 2016 01:23:30 +0100 Subject: [PATCH 38/40] 2.0.0 --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 83dd394..3ae0024 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "flower-power-api", - "version": "1.4.2", + "version": "2.0.0", "description": "A node.js module which use the Parrot Flower Power API", "author": "Bruno Sautron ", "repository": { From e90599add23a9064bb5c4b74da5335afb60c7dc3 Mon Sep 17 00:00:00 2001 From: bsautron Date: Sun, 11 Dec 2016 05:56:39 +0100 Subject: [PATCH 39/40] getGarden end send samples --- FlowerPowerCloud.js | 52 ++++++++++++--------------------------------- 1 file changed, 14 insertions(+), 38 deletions(-) diff --git a/FlowerPowerCloud.js b/FlowerPowerCloud.js index df91205..77840c1 100644 --- a/FlowerPowerCloud.js +++ b/FlowerPowerCloud.js @@ -21,7 +21,9 @@ function FlowerPowerCloud() { // Garden 'getSyncGarden': {method: 'GET/json', path: '/sensor_data/v4/garden_locations_status', auth: true}, - 'sendSamples': {method: 'PUT/json', path: '/sensor_data/v5/sample', auth: true}, + 'getConfiguration': {method: 'GET/json', path: '/garden/v2/configuration', auth: true}, + 'getGarden': {method: 'GET/json', path: '/garden/v1/status', auth: true}, + 'sendSamples': {method: 'PUT/json', path: '/sensor_data/v8/sample', auth: true}, 'getSyncData': {method: 'GET/json', path: '/sensor_data/v3/sync', auth: true}, 'getFirmwareUpdate': {method: 'GET/json', path: '/sensor_data/v1/firmware_update', auth: true}, 'getLocationSamples': {method: 'GET/json', path: '/sensor_data/v2/sample/location/:location_identifier', auth: true}, @@ -122,28 +124,6 @@ FlowerPowerCloud.prototype.invoke = function(req, data, callback) { else if (callback) { var results = body; - if (results.sensors) { - var sensors = {}; - for (var sensor of results.sensors) { - if (sensor.sensor_serial) { - sensors[sensor.sensor_serial] = sensor; - } - } - results.sensors = sensors; - } - if (results.locations) { - var locations = {}; - for (var location of results.locations) { - if (location.sensor_serial) { - locations[location.sensor_serial] = location; - } - } - results.locations = locations; - } - if (results.sensors && results.locations) { - results.sensors = self.concatJson(results.sensors, results.locations); - delete results.locations; - } return callback(null, results); } else throw "Give me a callback"; @@ -197,21 +177,17 @@ FlowerPowerCloud.prototype.refresh = function(token) { }); }; -FlowerPowerCloud.prototype.getGarden = function(callback) { - var self = this; - - async.parallel({ - syncGarden: function(callback) { - self.getSyncGarden(callback); - }, - syncData: function(callback) { - self.getSyncData(callback); - } - }, function(err, res) { - if (err) callback(err); - else callback(null, self.concatJson(res.syncData, res.syncGarden)); - }); -}; +// FlowerPowerCloud.prototype.getGarden = function(callback) { +// var self = this; +// +// async.parallel({ +// syncGarden: self.getSyncGarden, +// syncData: self.getSyncData, +// }, function(err, res) { +// if (err) callback(err); +// else callback(null, self.concatJson(res.syncData, res.syncGarden)); +// }); +// }; FlowerPowerCloud.prototype.concatJson = function(json1, json2) { var dest = json1; From ff6b1d7d9366361d76c16ddd04d3701278de9d8a Mon Sep 17 00:00:00 2001 From: bsautron Date: Sun, 11 Dec 2016 05:56:48 +0100 Subject: [PATCH 40/40] 2.0.1 --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 3ae0024..4c8b561 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "flower-power-api", - "version": "2.0.0", + "version": "2.0.1", "description": "A node.js module which use the Parrot Flower Power API", "author": "Bruno Sautron ", "repository": {