From 1c216309a375a47c22bd6f4bf1fc60f539e5d4c1 Mon Sep 17 00:00:00 2001 From: Wolfgang Sommerer Date: Sun, 5 Mar 2017 18:58:06 +0100 Subject: [PATCH 1/5] streamlined erro handling and added timeout option Error handling has been streamlined so that callbacks are invoked in error cases in every method. A request timeout option has been added for backend requests. A timeout passed to hte constructor is used as the global default while timeouts set in method options are only used in the method call- --- netatmo.js | 89 ++++++++++++++++++++++++++++++++++++++++++++++-------- 1 file changed, 76 insertions(+), 13 deletions(-) diff --git a/netatmo.js b/netatmo.js index f8831c0..1813135 100644 --- a/netatmo.js +++ b/netatmo.js @@ -11,6 +11,7 @@ var client_id; var client_secret; var scope; var access_token; +var timeout; /** * @constructor @@ -93,6 +94,7 @@ netatmo.prototype.authenticate = function (args, callback) { password = args.password; client_id = args.client_id; client_secret = args.client_secret; + timeout = args.timeout; scope = args.scope || 'read_station read_thermostat write_thermostat read_camera read_homecoach'; var form = { @@ -110,9 +112,14 @@ netatmo.prototype.authenticate = function (args, callback) { url: url, method: "POST", form: form, + timeout: timeout }, function (err, response, body) { if (err || response.statusCode != 200) { - return this.handleRequestError(err, response, body, "Authenticate error", true); + var error = this.handleRequestError(err, response, body, "Authenticate error"); + if (callback) { + callback(error); + } + return; } body = JSON.parse(body); @@ -155,9 +162,14 @@ netatmo.prototype.authenticate_refresh = function (refresh_token) { url: url, method: "POST", form: form, + timeout: timeout }, function (err, response, body) { if (err || response.statusCode != 200) { - return this.handleRequestError(err, response, body, "Authenticate refresh error"); + var error = this.handleRequestError(err, response, body, "Authenticate refresh error"); + if (callback) { + callback(error); + } + return; } body = JSON.parse(body); @@ -212,9 +224,14 @@ netatmo.prototype.getStationsData = function (options, callback) { url: url, method: "POST", form: form, + timeout: options && options.timeout || timeout }, function (err, response, body) { if (err || response.statusCode != 200) { - return this.handleRequestError(err, response, body, "getStationsDataError error"); + var error = this.handleRequestError(err, response, body, "getStationsData error"); + if (callback) { + callback(error); + } + return; } body = JSON.parse(body); @@ -261,9 +278,14 @@ netatmo.prototype.getThermostatsData = function (options, callback) { request({ url: url, method: "GET", + timeout: options && options.timeout || timeout }, function (err, response, body) { if (err || response.statusCode != 200) { - return this.handleRequestError(err, response, body, "getThermostatsDataError error"); + var error = this.handleRequestError(err, response, body, "getThermostatsData error"); + if (callback) { + callback(error); + } + return; } body = JSON.parse(body); @@ -378,6 +400,7 @@ netatmo.prototype.getMeasure = function (options, callback) { url: url, method: "POST", form: form, + timeout: options && options.timeout || timeout }, function (err, response, body) { if (err || response.statusCode != 200) { var error = this.handleRequestError(err, response, body, "getMeasure error"); @@ -457,9 +480,14 @@ netatmo.prototype.setSyncSchedule = function (options, callback) { url: url, method: "POST", form: form, + timeout: options && options.timeout || timeout }, function (err, response, body) { if (err || response.statusCode != 200) { - return this.handleRequestError(err, response, body, "setSyncSchedule error"); + var error = this.handleRequestError(err, response, body, "setSyncSchedule error"); + if (callback) { + callback(error); + } + return; } body = JSON.parse(body); @@ -536,9 +564,14 @@ netatmo.prototype.setThermpoint = function (options, callback) { url: url, method: "POST", form: form, + timeout: options && options.timeout || timeout }, function (err, response, body) { if (err || response.statusCode != 200) { - return this.handleRequestError(err, response, body, "setThermpoint error"); + var error = this.handleRequestError(err, response, body, "setThermpoint error"); + if (callback) { + callback(error); + } + return; } body = JSON.parse(body); @@ -594,9 +627,14 @@ netatmo.prototype.getHomeData = function (options, callback) { url: url, method: "POST", form: form, + timeout: options && options.timeout || timeout }, function (err, response, body) { if (err || response.statusCode != 200) { - return this.handleRequestError(err, response, body, "getHomeData error"); + var error = this.handleRequestError(err, response, body, "getHomeData error"); + if (callback) { + callback(error); + } + return; } body = JSON.parse(body); @@ -659,9 +697,14 @@ netatmo.prototype.getNextEvents = function (options, callback) { url: url, method: "POST", form: form, + timeout: options && options.timeout || timeout }, function (err, response, body) { if (err || response.statusCode != 200) { - return this.handleRequestError(err, response, body, "getNextEvents error"); + var error = this.handleRequestError(err, response, body, "getNextEvents error"); + if (callback) { + callback(error); + } + return; } body = JSON.parse(body); @@ -724,9 +767,14 @@ netatmo.prototype.getLastEventOf = function (options, callback) { url: url, method: "POST", form: form, + timeout: options && options.timeout || timeout }, function (err, response, body) { if (err || response.statusCode != 200) { - return this.handleRequestError(err, response, body, "getLastEventOf error"); + var error = this.handleRequestError(err, response, body, "getLastEventOf error"); + if (callback) { + callback(error); + } + return; } body = JSON.parse(body); @@ -785,9 +833,14 @@ netatmo.prototype.getEventsUntil = function (options, callback) { url: url, method: "POST", form: form, + timeout: options && options.timeout || timeout }, function (err, response, body) { if (err || response.statusCode != 200) { - return this.handleRequestError(err, response, body, "getEventsUntil error"); + var error = this.handleRequestError(err, response, body, "getEventsUntil error"); + if (callback) { + callback(error); + } + return; } body = JSON.parse(body); @@ -847,10 +900,15 @@ netatmo.prototype.getCameraPicture = function (options, callback) { method: "GET", qs: qs, encoding: null, - contentType: 'image/jpg' + contentType: 'image/jpg', + timeout: options && options.timeout || timeout }, function (err, response, body) { if (err || response.statusCode != 200) { - return this.handleRequestError(err, response, body, "getCameraPicture error"); + var error = this.handleRequestError(err, response, body, "getCameraPicture error"); + if (callback) { + callback(error); + } + return; } this.emit('get-camerapicture', err, body); @@ -893,9 +951,14 @@ netatmo.prototype.getHealthyHomeCoachData = function (options, callback) { request({ url: url, method: "GET", + timeout: options && options.timeout || timeout }, function (err, response, body) { if (err || response.statusCode != 200) { - return this.handleRequestError(err, response, body, "getHealthyHomeCoachData error"); + var error = this.handleRequestError(err, response, body, "getHealthyHomeCoachData error"); + if (callback) { + callback(error); + } + return; } body = JSON.parse(body); From 44327d9db526e275214de3ce0b39dde93cd3c56d Mon Sep 17 00:00:00 2001 From: sowo Date: Mon, 6 Mar 2017 12:36:55 +0100 Subject: [PATCH 2/5] fixed authenticate_refresh authenticate_refresh is invoked without callback. Using standard error handling doesn't work in this case. --- netatmo.js | 3 --- 1 file changed, 3 deletions(-) diff --git a/netatmo.js b/netatmo.js index 1813135..bf00413 100644 --- a/netatmo.js +++ b/netatmo.js @@ -166,9 +166,6 @@ netatmo.prototype.authenticate_refresh = function (refresh_token) { }, function (err, response, body) { if (err || response.statusCode != 200) { var error = this.handleRequestError(err, response, body, "Authenticate refresh error"); - if (callback) { - callback(error); - } return; } From 0eaa888789fee7045d59ea1816cef6d53589b007 Mon Sep 17 00:00:00 2001 From: Wolfgang Sommerer Date: Mon, 6 Mar 2017 21:18:56 +0100 Subject: [PATCH 3/5] methods now return error after invoking callback --- netatmo.js | 1955 ++++++++++++++++++++++++++-------------------------- 1 file changed, 977 insertions(+), 978 deletions(-) diff --git a/netatmo.js b/netatmo.js index bf00413..d787969 100644 --- a/netatmo.js +++ b/netatmo.js @@ -1,978 +1,977 @@ -var util = require('util'); -var EventEmitter = require("events").EventEmitter; -var request = require('request'); -var moment = require('moment'); - -const BASE_URL = 'https://api.netatmo.net'; - -var username; -var password; -var client_id; -var client_secret; -var scope; -var access_token; -var timeout; - -/** - * @constructor - * @param args - */ -var netatmo = function (args) { - EventEmitter.call(this); - this.authenticate(args); -}; - -util.inherits(netatmo, EventEmitter); - -/** - * handleRequestError - * @param err - * @param response - * @param body - * @param message - * @param critical - * @returns {Error} - */ -netatmo.prototype.handleRequestError = function (err, response, body, message, critical) { - var errorMessage = ""; - if (body && response.headers['content-type'] === 'application/json') { - errorMessage = JSON.parse(body); - errorMessage = errorMessage && (errorMessage.error.message || errorMessage.error); - } else if (typeof response !== 'undefined') { - errorMessage = "Status code" + response.statusCode; - } - else { - errorMessage = "No response"; - } - var error = new Error(message + ": " + errorMessage); - if (critical) { - this.emit("error", error); - } else { - this.emit("warning", error); - } - return error; -}; - -/** - * https://dev.netatmo.com/dev/resources/technical/guides/authentication - * @param args - * @param callback - * @returns {netatmo} - */ -netatmo.prototype.authenticate = function (args, callback) { - if (!args) { - this.emit("error", new Error("Authenticate 'args' not set.")); - return this; - } - - if (args.access_token) { - access_token = args.access_token; - return this; - } - - if (!args.client_id) { - this.emit("error", new Error("Authenticate 'client_id' not set.")); - return this; - } - - if (!args.client_secret) { - this.emit("error", new Error("Authenticate 'client_secret' not set.")); - return this; - } - - if (!args.username) { - this.emit("error", new Error("Authenticate 'username' not set.")); - return this; - } - - if (!args.password) { - this.emit("error", new Error("Authenticate 'password' not set.")); - return this; - } - - username = args.username; - password = args.password; - client_id = args.client_id; - client_secret = args.client_secret; - timeout = args.timeout; - scope = args.scope || 'read_station read_thermostat write_thermostat read_camera read_homecoach'; - - var form = { - client_id: client_id, - client_secret: client_secret, - username: username, - password: password, - scope: scope, - grant_type: 'password', - }; - - var url = util.format('%s/oauth2/token', BASE_URL); - - request({ - url: url, - method: "POST", - form: form, - timeout: timeout - }, function (err, response, body) { - if (err || response.statusCode != 200) { - var error = this.handleRequestError(err, response, body, "Authenticate error"); - if (callback) { - callback(error); - } - return; - } - - body = JSON.parse(body); - - access_token = body.access_token; - - if (body.expires_in) { - setTimeout(this.authenticate_refresh.bind(this), body.expires_in * 1000, body.refresh_token); - } - - this.emit('authenticated'); - - if (callback) { - return callback(); - } - - return this; - }.bind(this)); - - return this; -}; - -/** - * https://dev.netatmo.com/dev/resources/technical/guides/authentication/refreshingatoken - * @param refresh_token - * @returns {netatmo} - */ -netatmo.prototype.authenticate_refresh = function (refresh_token) { - - var form = { - grant_type: 'refresh_token', - refresh_token: refresh_token, - client_id: client_id, - client_secret: client_secret, - }; - - var url = util.format('%s/oauth2/token', BASE_URL); - - request({ - url: url, - method: "POST", - form: form, - timeout: timeout - }, function (err, response, body) { - if (err || response.statusCode != 200) { - var error = this.handleRequestError(err, response, body, "Authenticate refresh error"); - return; - } - - body = JSON.parse(body); - - access_token = body.access_token; - - if (body.expires_in) { - setTimeout(this.authenticate_refresh.bind(this), body.expires_in * 1000, body.refresh_token); - } - - return this; - }.bind(this)); - - return this; -}; - -/** - * https://dev.netatmo.com/dev/resources/technical/reference/weatherstation/getstationsdata - * @param options - * @param callback - * @returns {*} - */ -netatmo.prototype.getStationsData = function (options, callback) { - // Wait until authenticated. - if (!access_token) { - return this.on('authenticated', function () { - this.getStationsData(options, callback); - }); - } - - if (options != null && callback == null) { - callback = options; - options = null; - } - - var url = util.format('%s/api/getstationsdata', BASE_URL); - - var form = { - access_token: access_token, - }; - - if (options) { - if (options.device_id) { - form.device_id = options.device_id; - } - if (options.get_favorites) { - form.get_favorites = options.get_favorites; - } - } - - request({ - url: url, - method: "POST", - form: form, - timeout: options && options.timeout || timeout - }, function (err, response, body) { - if (err || response.statusCode != 200) { - var error = this.handleRequestError(err, response, body, "getStationsData error"); - if (callback) { - callback(error); - } - return; - } - - body = JSON.parse(body); - - var devices = body.body.devices; - - this.emit('get-stationsdata', err, devices); - - if (callback) { - return callback(err, devices); - } - - return this; - - }.bind(this)); - - return this; -}; - -/** - * https://dev.netatmo.com/dev/resources/technical/reference/thermostat/getthermostatsdata - * @param options - * @param callback - * @returns {*} - */ -netatmo.prototype.getThermostatsData = function (options, callback) { - // Wait until authenticated. - if (!access_token) { - return this.on('authenticated', function () { - this.getThermostatsData(options, callback); - }); - } - - if (options != null && callback == null) { - callback = options; - options = null; - } - - var url = util.format('%s/api/getthermostatsdata?access_token=%s', BASE_URL, access_token); - if (options != null) { - url = util.format(url + '&device_id=%s', options.device_id); - } - - request({ - url: url, - method: "GET", - timeout: options && options.timeout || timeout - }, function (err, response, body) { - if (err || response.statusCode != 200) { - var error = this.handleRequestError(err, response, body, "getThermostatsData error"); - if (callback) { - callback(error); - } - return; - } - - body = JSON.parse(body); - - var devices = body.body.devices; - - this.emit('get-thermostatsdata', err, devices); - - if (callback) { - return callback(err, devices); - } - - return this; - - }.bind(this)); - - return this; -}; - -/** - * https://dev.netatmo.com/dev/resources/technical/reference/common/getmeasure - * @param options - * @param callback - * @returns {*} - */ -netatmo.prototype.getMeasure = function (options, callback) { - // Wait until authenticated. - if (!access_token) { - return this.on('authenticated', function () { - this.getMeasure(options, callback); - }); - } - - if (!options) { - this.emit("error", new Error("getMeasure 'options' not set.")); - return this; - } - - if (!options.device_id) { - this.emit("error", new Error("getMeasure 'device_id' not set.")); - return this; - } - - if (!options.scale) { - this.emit("error", new Error("getMeasure 'scale' not set.")); - return this; - } - - if (!options.type) { - this.emit("error", new Error("getMeasure 'type' not set.")); - return this; - } - - if (util.isArray(options.type)) { - options.type = options.type.join(','); - } - - // Remove any spaces from the type list if there is any. - options.type = options.type.replace(/\s/g, '').toLowerCase(); - - - var url = util.format('%s/api/getmeasure', BASE_URL); - - var form = { - access_token: access_token, - device_id: options.device_id, - scale: options.scale, - type: options.type, - }; - - if (options) { - - if (options.module_id) { - form.module_id = options.module_id; - } - - if (options.date_begin) { - if (options.date_begin <= 1E10) { - options.date_begin *= 1E3; - } - - form.date_begin = moment(options.date_begin).utc().unix(); - } - - if (options.date_end === 'last') { - form.date_end = 'last'; - } else if (options.date_end) { - if (options.date_end <= 1E10) { - options.date_end *= 1E3; - } - form.date_end = moment(options.date_end).utc().unix(); - } - - if (options.limit) { - form.limit = parseInt(options.limit, 10); - - if (form.limit > 1024) { - form.limit = 1024; - } - } - - if (options.optimize !== undefined) { - form.optimize = !!options.optimize; - } - - if (options.real_time !== undefined) { - form.real_time = !!options.real_time; - } - } - - request({ - url: url, - method: "POST", - form: form, - timeout: options && options.timeout || timeout - }, function (err, response, body) { - if (err || response.statusCode != 200) { - var error = this.handleRequestError(err, response, body, "getMeasure error"); - if (callback) { - callback(error); - } - return; - } - - body = JSON.parse(body); - - var measure = body.body; - - this.emit('get-measure', err, measure); - - if (callback) { - return callback(err, measure); - } - - return this; - - }.bind(this)); - - return this; -}; - -/** - * https://dev.netatmo.com/dev/resources/technical/reference/thermostat/syncschedule - * @param options - * @param callback - * @returns {*} - */ -netatmo.prototype.setSyncSchedule = function (options, callback) { - // Wait until authenticated. - if (!access_token) { - return this.on('authenticated', function () { - this.setSyncSchedule(options, callback); - }); - } - - if (!options) { - this.emit("error", new Error("setSyncSchedule 'options' not set.")); - return this; - } - - if (!options.device_id) { - this.emit("error", new Error("setSyncSchedule 'device_id' not set.")); - return this; - } - - if (!options.module_id) { - this.emit("error", new Error("setSyncSchedule 'module_id' not set.")); - return this; - } - - if (!options.zones) { - this.emit("error", new Error("setSyncSchedule 'zones' not set.")); - return this; - } - - if (!options.timetable) { - this.emit("error", new Error("setSyncSchedule 'timetable' not set.")); - return this; - } - - var url = util.format('%s/api/syncschedule', BASE_URL); - - var form = { - access_token: access_token, - device_id: options.device_id, - module_id: options.module_id, - zones: options.zones, - timetable: options.timetable, - }; - - request({ - url: url, - method: "POST", - form: form, - timeout: options && options.timeout || timeout - }, function (err, response, body) { - if (err || response.statusCode != 200) { - var error = this.handleRequestError(err, response, body, "setSyncSchedule error"); - if (callback) { - callback(error); - } - return; - } - - body = JSON.parse(body); - - this.emit('set-syncschedule', err, body.status); - - if (callback) { - return callback(err, body.status); - } - - return this; - - }.bind(this)); - - return this; -}; - -/** - * https://dev.netatmo.com/dev/resources/technical/reference/thermostat/setthermpoint - * @param options - * @param callback - * @returns {*} - */ -netatmo.prototype.setThermpoint = function (options, callback) { - // Wait until authenticated. - if (!access_token) { - return this.on('authenticated', function () { - this.setThermpoint(options, callback); - }); - } - - if (!options) { - this.emit("error", new Error("setThermpoint 'options' not set.")); - return this; - } - - if (!options.device_id) { - this.emit("error", new Error("setThermpoint 'device_id' not set.")); - return this; - } - - if (!options.module_id) { - this.emit("error", new Error("setThermpoint 'module_id' not set.")); - return this; - } - - if (!options.setpoint_mode) { - this.emit("error", new Error("setThermpoint 'setpoint_mode' not set.")); - return this; - } - - var url = util.format('%s/api/setthermpoint', BASE_URL); - - var form = { - access_token: access_token, - device_id: options.device_id, - module_id: options.module_id, - setpoint_mode: options.setpoint_mode, - }; - - if (options) { - - if (options.setpoint_endtime) { - form.setpoint_endtime = options.setpoint_endtime; - } - - if (options.setpoint_temp) { - form.setpoint_temp = options.setpoint_temp; - } - - } - - request({ - url: url, - method: "POST", - form: form, - timeout: options && options.timeout || timeout - }, function (err, response, body) { - if (err || response.statusCode != 200) { - var error = this.handleRequestError(err, response, body, "setThermpoint error"); - if (callback) { - callback(error); - } - return; - } - - body = JSON.parse(body); - - this.emit('get-thermostatsdata', err, body.status); - - if (callback) { - return callback(err, body.status); - } - - return this; - - }.bind(this)); - - return this; -}; - -/** - * https://dev.netatmo.com/dev/resources/technical/reference/welcome/gethomedata - * @param options - * @param callback - * @returns {*} - */ -netatmo.prototype.getHomeData = function (options, callback) { - // Wait until authenticated. - if (!access_token) { - return this.on('authenticated', function () { - this.getHomeData(options, callback); - }); - } - - var url = util.format('%s/api/gethomedata', BASE_URL); - - var form = { - access_token: access_token - }; - - if (options != null && callback == null) { - callback = options; - options = null; - } - - if (options) { - if (options.home_id) { - form.home_id = options.home_id; - } - if (options.size) { - form.size = options.size; - } - } - - request({ - url: url, - method: "POST", - form: form, - timeout: options && options.timeout || timeout - }, function (err, response, body) { - if (err || response.statusCode != 200) { - var error = this.handleRequestError(err, response, body, "getHomeData error"); - if (callback) { - callback(error); - } - return; - } - - body = JSON.parse(body); - - this.emit('get-homedata', err, body.body); - - if (callback) { - return callback(err, body.body); - } - - return this; - - }.bind(this)); - - return this; -}; - -/** - * https://dev.netatmo.com/dev/resources/technical/reference/welcome/getnextevents - * @param options - * @param callback - * @returns {*} - */ -netatmo.prototype.getNextEvents = function (options, callback) { - // Wait until authenticated. - if (!access_token) { - return this.on('authenticated', function () { - this.getNextEvents(options, callback); - }); - } - - if (!options) { - this.emit("error", new Error("getNextEvents 'options' not set.")); - return this; - } - - if (!options.home_id) { - this.emit("error", new Error("getNextEvents 'home_id' not set.")); - return this; - } - - if (!options.event_id) { - this.emit("error", new Error("getNextEvents 'event_id' not set.")); - return this; - } - - var url = util.format('%s/api/getnextevents', BASE_URL); - - var form = { - access_token: access_token, - home_id: options.home_id, - event_id: options.event_id, - }; - - if (options.size) { - form.size = options.size; - } - - request({ - url: url, - method: "POST", - form: form, - timeout: options && options.timeout || timeout - }, function (err, response, body) { - if (err || response.statusCode != 200) { - var error = this.handleRequestError(err, response, body, "getNextEvents error"); - if (callback) { - callback(error); - } - return; - } - - body = JSON.parse(body); - - this.emit('get-nextevents', err, body.body); - - if (callback) { - return callback(err, body.body); - } - - return this; - - }.bind(this)); - - return this; -}; - -/** - * https://dev.netatmo.com/dev/resources/technical/reference/welcome/getlasteventof - * @param options - * @param callback - * @returns {*} - */ -netatmo.prototype.getLastEventOf = function (options, callback) { - // Wait until authenticated. - if (!access_token) { - return this.on('authenticated', function () { - this.getLastEventOf(options, callback); - }); - } - - if (!options) { - this.emit("error", new Error("getLastEventOf 'options' not set.")); - return this; - } - - if (!options.home_id) { - this.emit("error", new Error("getLastEventOf 'home_id' not set.")); - return this; - } - - if (!options.person_id) { - this.emit("error", new Error("getLastEventOf 'person_id' not set.")); - return this; - } - - var url = util.format('%s/api/getlasteventof', BASE_URL); - - var form = { - access_token: access_token, - home_id: options.home_id, - person_id: options.person_id, - }; - - if (options.offset) { - form.offset = options.offset; - } - - request({ - url: url, - method: "POST", - form: form, - timeout: options && options.timeout || timeout - }, function (err, response, body) { - if (err || response.statusCode != 200) { - var error = this.handleRequestError(err, response, body, "getLastEventOf error"); - if (callback) { - callback(error); - } - return; - } - - body = JSON.parse(body); - - this.emit('get-lasteventof', err, body.body); - - if (callback) { - return callback(err, body.body); - } - - return this; - - }.bind(this)); - - return this; -}; - -/** - * https://dev.netatmo.com/dev/resources/technical/reference/welcome/geteventsuntil - * @param options - * @param callback - * @returns {*} - */ -netatmo.prototype.getEventsUntil = function (options, callback) { - // Wait until authenticated. - if (!access_token) { - return this.on('authenticated', function () { - this.getEventsUntil(options, callback); - }); - } - - if (!options) { - this.emit("error", new Error("getEventsUntil 'options' not set.")); - return this; - } - - if (!options.home_id) { - this.emit("error", new Error("getEventsUntil 'home_id' not set.")); - return this; - } - - if (!options.event_id) { - this.emit("error", new Error("getEventsUntil 'event_id' not set.")); - return this; - } - - var url = util.format('%s/api/geteventsuntil', BASE_URL); - - var form = { - access_token: access_token, - home_id: options.home_id, - event_id: options.event_id, - }; - - request({ - url: url, - method: "POST", - form: form, - timeout: options && options.timeout || timeout - }, function (err, response, body) { - if (err || response.statusCode != 200) { - var error = this.handleRequestError(err, response, body, "getEventsUntil error"); - if (callback) { - callback(error); - } - return; - } - - body = JSON.parse(body); - - this.emit('get-eventsuntil', err, body.body); - - if (callback) { - return callback(err, body.body); - } - - return this; - - }.bind(this)); - - return this; -}; - -/** - * https://dev.netatmo.com/dev/resources/technical/reference/welcome/getcamerapicture - * @param options - * @param callback - * @returns {*} - */ -netatmo.prototype.getCameraPicture = function (options, callback) { - // Wait until authenticated. - if (!access_token) { - return this.on('authenticated', function () { - this.getCameraPicture(options, callback); - }); - } - - if (!options) { - this.emit("error", new Error("getCameraPicture 'options' not set.")); - return this; - } - - if (!options.image_id) { - this.emit("error", new Error("getCameraPicture 'image_id' not set.")); - return this; - } - - if (!options.key) { - this.emit("error", new Error("getCameraPicture 'key' not set.")); - return this; - } - - var url = util.format('%s/api/getcamerapicture', BASE_URL); - - var qs = { - access_token: access_token, - image_id: options.image_id, - key: options.key, - }; - - request({ - url: url, - method: "GET", - qs: qs, - encoding: null, - contentType: 'image/jpg', - timeout: options && options.timeout || timeout - }, function (err, response, body) { - if (err || response.statusCode != 200) { - var error = this.handleRequestError(err, response, body, "getCameraPicture error"); - if (callback) { - callback(error); - } - return; - } - - this.emit('get-camerapicture', err, body); - - if (callback) { - return callback(err, body); - } - - return this; - - }.bind(this)); - - return this; -}; - -/** - * https://dev.netatmo.com/dev/resources/technical/reference/healthyhomecoach/gethomecoachsdata - * @param options - * @param callback - * @returns {*} - */ -netatmo.prototype.getHealthyHomeCoachData = function (options, callback) { - // Wait until authenticated. - if (!access_token) { - return this.on('authenticated', function () { - this.getHealthyHomeCoachData(options, callback); - }); - } - - if (options != null && callback == null) { - callback = options; - options = null; - } - - var url = util.format('%s/api/gethomecoachsdata?access_token=%s', BASE_URL, access_token); - if (options != null) { - url = util.format(url + '&device_id=%s', options.device_id); - } - - request({ - url: url, - method: "GET", - timeout: options && options.timeout || timeout - }, function (err, response, body) { - if (err || response.statusCode != 200) { - var error = this.handleRequestError(err, response, body, "getHealthyHomeCoachData error"); - if (callback) { - callback(error); - } - return; - } - - body = JSON.parse(body); - - var devices = body.body.devices; - - this.emit('get-healthhomecoaches-data', err, devices); - - if (callback) { - return callback(err, devices); - } - - return this; - - }.bind(this)); - - return this; -}; - -module.exports = netatmo; +var util = require('util'); +var EventEmitter = require("events").EventEmitter; +var request = require('request'); +var moment = require('moment'); + +const BASE_URL = 'https://api.netatmo.net'; + +var username; +var password; +var client_id; +var client_secret; +var scope; +var access_token; +var timeout; + +/** + * @constructor + * @param args + */ +var netatmo = function (args) { + EventEmitter.call(this); + this.authenticate(args); +}; + +util.inherits(netatmo, EventEmitter); + +/** + * handleRequestError + * @param err + * @param response + * @param body + * @param message + * @param critical + * @returns {Error} + */ +netatmo.prototype.handleRequestError = function (err, response, body, message, critical) { + var errorMessage = ""; + if (body && response.headers['content-type'] === 'application/json') { + errorMessage = JSON.parse(body); + errorMessage = errorMessage && (errorMessage.error.message || errorMessage.error); + } else if (typeof response !== 'undefined') { + errorMessage = "Status code" + response.statusCode; + } + else { + errorMessage = "No response"; + } + var error = new Error(message + ": " + errorMessage); + if (critical) { + this.emit("error", error); + } else { + this.emit("warning", error); + } + return error; +}; + +/** + * https://dev.netatmo.com/dev/resources/technical/guides/authentication + * @param args + * @param callback + * @returns {netatmo} + */ +netatmo.prototype.authenticate = function (args, callback) { + if (!args) { + this.emit("error", new Error("Authenticate 'args' not set.")); + return this; + } + + if (args.access_token) { + access_token = args.access_token; + return this; + } + + if (!args.client_id) { + this.emit("error", new Error("Authenticate 'client_id' not set.")); + return this; + } + + if (!args.client_secret) { + this.emit("error", new Error("Authenticate 'client_secret' not set.")); + return this; + } + + if (!args.username) { + this.emit("error", new Error("Authenticate 'username' not set.")); + return this; + } + + if (!args.password) { + this.emit("error", new Error("Authenticate 'password' not set.")); + return this; + } + + username = args.username; + password = args.password; + client_id = args.client_id; + client_secret = args.client_secret; + timeout = args.timeout; + scope = args.scope || 'read_station read_thermostat write_thermostat read_camera read_homecoach'; + + var form = { + client_id: client_id, + client_secret: client_secret, + username: username, + password: password, + scope: scope, + grant_type: 'password', + }; + + var url = util.format('%s/oauth2/token', BASE_URL); + + request({ + url: url, + method: "POST", + form: form, + timeout: timeout + }, function (err, response, body) { + if (err || response.statusCode != 200) { + var error = this.handleRequestError(err, response, body, "Authenticate error"); + if (callback) { + callback(error); + } + return error; + } + + body = JSON.parse(body); + + access_token = body.access_token; + + if (body.expires_in) { + setTimeout(this.authenticate_refresh.bind(this), body.expires_in * 1000, body.refresh_token); + } + + this.emit('authenticated'); + + if (callback) { + return callback(); + } + + return this; + }.bind(this)); + + return this; +}; + +/** + * https://dev.netatmo.com/dev/resources/technical/guides/authentication/refreshingatoken + * @param refresh_token + * @returns {netatmo} + */ +netatmo.prototype.authenticate_refresh = function (refresh_token) { + + var form = { + grant_type: 'refresh_token', + refresh_token: refresh_token, + client_id: client_id, + client_secret: client_secret, + }; + + var url = util.format('%s/oauth2/token', BASE_URL); + + request({ + url: url, + method: "POST", + form: form, + timeout: timeout + }, function (err, response, body) { + if (err || response.statusCode != 200) { + return this.handleRequestError(err, response, body, "Authenticate refresh error"); + } + + body = JSON.parse(body); + + access_token = body.access_token; + + if (body.expires_in) { + setTimeout(this.authenticate_refresh.bind(this), body.expires_in * 1000, body.refresh_token); + } + + return this; + }.bind(this)); + + return this; +}; + +/** + * https://dev.netatmo.com/dev/resources/technical/reference/weatherstation/getstationsdata + * @param options + * @param callback + * @returns {*} + */ +netatmo.prototype.getStationsData = function (options, callback) { + // Wait until authenticated. + if (!access_token) { + return this.on('authenticated', function () { + this.getStationsData(options, callback); + }); + } + + if (options != null && callback == null) { + callback = options; + options = null; + } + + var url = util.format('%s/api/getstationsdata', BASE_URL); + + var form = { + access_token: access_token, + }; + + if (options) { + if (options.device_id) { + form.device_id = options.device_id; + } + if (options.get_favorites) { + form.get_favorites = options.get_favorites; + } + } + + request({ + url: url, + method: "POST", + form: form, + timeout: options && options.timeout || timeout + }, function (err, response, body) { + if (err || response.statusCode != 200) { + var error = this.handleRequestError(err, response, body, "getStationsData error"); + if (callback) { + callback(error); + } + return error; + } + + body = JSON.parse(body); + + var devices = body.body.devices; + + this.emit('get-stationsdata', err, devices); + + if (callback) { + return callback(err, devices); + } + + return this; + + }.bind(this)); + + return this; +}; + +/** + * https://dev.netatmo.com/dev/resources/technical/reference/thermostat/getthermostatsdata + * @param options + * @param callback + * @returns {*} + */ +netatmo.prototype.getThermostatsData = function (options, callback) { + // Wait until authenticated. + if (!access_token) { + return this.on('authenticated', function () { + this.getThermostatsData(options, callback); + }); + } + + if (options != null && callback == null) { + callback = options; + options = null; + } + + var url = util.format('%s/api/getthermostatsdata?access_token=%s', BASE_URL, access_token); + if (options != null) { + url = util.format(url + '&device_id=%s', options.device_id); + } + + request({ + url: url, + method: "GET", + timeout: options && options.timeout || timeout + }, function (err, response, body) { + if (err || response.statusCode != 200) { + var error = this.handleRequestError(err, response, body, "getThermostatsData error"); + if (callback) { + callback(error); + } + return error; + } + + body = JSON.parse(body); + + var devices = body.body.devices; + + this.emit('get-thermostatsdata', err, devices); + + if (callback) { + return callback(err, devices); + } + + return this; + + }.bind(this)); + + return this; +}; + +/** + * https://dev.netatmo.com/dev/resources/technical/reference/common/getmeasure + * @param options + * @param callback + * @returns {*} + */ +netatmo.prototype.getMeasure = function (options, callback) { + // Wait until authenticated. + if (!access_token) { + return this.on('authenticated', function () { + this.getMeasure(options, callback); + }); + } + + if (!options) { + this.emit("error", new Error("getMeasure 'options' not set.")); + return this; + } + + if (!options.device_id) { + this.emit("error", new Error("getMeasure 'device_id' not set.")); + return this; + } + + if (!options.scale) { + this.emit("error", new Error("getMeasure 'scale' not set.")); + return this; + } + + if (!options.type) { + this.emit("error", new Error("getMeasure 'type' not set.")); + return this; + } + + if (util.isArray(options.type)) { + options.type = options.type.join(','); + } + + // Remove any spaces from the type list if there is any. + options.type = options.type.replace(/\s/g, '').toLowerCase(); + + + var url = util.format('%s/api/getmeasure', BASE_URL); + + var form = { + access_token: access_token, + device_id: options.device_id, + scale: options.scale, + type: options.type, + }; + + if (options) { + + if (options.module_id) { + form.module_id = options.module_id; + } + + if (options.date_begin) { + if (options.date_begin <= 1E10) { + options.date_begin *= 1E3; + } + + form.date_begin = moment(options.date_begin).utc().unix(); + } + + if (options.date_end === 'last') { + form.date_end = 'last'; + } else if (options.date_end) { + if (options.date_end <= 1E10) { + options.date_end *= 1E3; + } + form.date_end = moment(options.date_end).utc().unix(); + } + + if (options.limit) { + form.limit = parseInt(options.limit, 10); + + if (form.limit > 1024) { + form.limit = 1024; + } + } + + if (options.optimize !== undefined) { + form.optimize = !!options.optimize; + } + + if (options.real_time !== undefined) { + form.real_time = !!options.real_time; + } + } + + request({ + url: url, + method: "POST", + form: form, + timeout: options && options.timeout || timeout + }, function (err, response, body) { + if (err || response.statusCode != 200) { + var error = this.handleRequestError(err, response, body, "getMeasure error"); + if (callback) { + callback(error); + } + return error; + } + + body = JSON.parse(body); + + var measure = body.body; + + this.emit('get-measure', err, measure); + + if (callback) { + return callback(err, measure); + } + + return this; + + }.bind(this)); + + return this; +}; + +/** + * https://dev.netatmo.com/dev/resources/technical/reference/thermostat/syncschedule + * @param options + * @param callback + * @returns {*} + */ +netatmo.prototype.setSyncSchedule = function (options, callback) { + // Wait until authenticated. + if (!access_token) { + return this.on('authenticated', function () { + this.setSyncSchedule(options, callback); + }); + } + + if (!options) { + this.emit("error", new Error("setSyncSchedule 'options' not set.")); + return this; + } + + if (!options.device_id) { + this.emit("error", new Error("setSyncSchedule 'device_id' not set.")); + return this; + } + + if (!options.module_id) { + this.emit("error", new Error("setSyncSchedule 'module_id' not set.")); + return this; + } + + if (!options.zones) { + this.emit("error", new Error("setSyncSchedule 'zones' not set.")); + return this; + } + + if (!options.timetable) { + this.emit("error", new Error("setSyncSchedule 'timetable' not set.")); + return this; + } + + var url = util.format('%s/api/syncschedule', BASE_URL); + + var form = { + access_token: access_token, + device_id: options.device_id, + module_id: options.module_id, + zones: options.zones, + timetable: options.timetable, + }; + + request({ + url: url, + method: "POST", + form: form, + timeout: options && options.timeout || timeout + }, function (err, response, body) { + if (err || response.statusCode != 200) { + var error = this.handleRequestError(err, response, body, "setSyncSchedule error"); + if (callback) { + callback(error); + } + return error; + } + + body = JSON.parse(body); + + this.emit('set-syncschedule', err, body.status); + + if (callback) { + return callback(err, body.status); + } + + return this; + + }.bind(this)); + + return this; +}; + +/** + * https://dev.netatmo.com/dev/resources/technical/reference/thermostat/setthermpoint + * @param options + * @param callback + * @returns {*} + */ +netatmo.prototype.setThermpoint = function (options, callback) { + // Wait until authenticated. + if (!access_token) { + return this.on('authenticated', function () { + this.setThermpoint(options, callback); + }); + } + + if (!options) { + this.emit("error", new Error("setThermpoint 'options' not set.")); + return this; + } + + if (!options.device_id) { + this.emit("error", new Error("setThermpoint 'device_id' not set.")); + return this; + } + + if (!options.module_id) { + this.emit("error", new Error("setThermpoint 'module_id' not set.")); + return this; + } + + if (!options.setpoint_mode) { + this.emit("error", new Error("setThermpoint 'setpoint_mode' not set.")); + return this; + } + + var url = util.format('%s/api/setthermpoint', BASE_URL); + + var form = { + access_token: access_token, + device_id: options.device_id, + module_id: options.module_id, + setpoint_mode: options.setpoint_mode, + }; + + if (options) { + + if (options.setpoint_endtime) { + form.setpoint_endtime = options.setpoint_endtime; + } + + if (options.setpoint_temp) { + form.setpoint_temp = options.setpoint_temp; + } + + } + + request({ + url: url, + method: "POST", + form: form, + timeout: options && options.timeout || timeout + }, function (err, response, body) { + if (err || response.statusCode != 200) { + var error = this.handleRequestError(err, response, body, "setThermpoint error"); + if (callback) { + callback(error); + } + return error; + } + + body = JSON.parse(body); + + this.emit('get-thermostatsdata', err, body.status); + + if (callback) { + return callback(err, body.status); + } + + return this; + + }.bind(this)); + + return this; +}; + +/** + * https://dev.netatmo.com/dev/resources/technical/reference/welcome/gethomedata + * @param options + * @param callback + * @returns {*} + */ +netatmo.prototype.getHomeData = function (options, callback) { + // Wait until authenticated. + if (!access_token) { + return this.on('authenticated', function () { + this.getHomeData(options, callback); + }); + } + + var url = util.format('%s/api/gethomedata', BASE_URL); + + var form = { + access_token: access_token + }; + + if (options != null && callback == null) { + callback = options; + options = null; + } + + if (options) { + if (options.home_id) { + form.home_id = options.home_id; + } + if (options.size) { + form.size = options.size; + } + } + + request({ + url: url, + method: "POST", + form: form, + timeout: options && options.timeout || timeout + }, function (err, response, body) { + if (err || response.statusCode != 200) { + var error = this.handleRequestError(err, response, body, "getHomeData error"); + if (callback) { + callback(error); + } + return error; + } + + body = JSON.parse(body); + + this.emit('get-homedata', err, body.body); + + if (callback) { + return callback(err, body.body); + } + + return this; + + }.bind(this)); + + return this; +}; + +/** + * https://dev.netatmo.com/dev/resources/technical/reference/welcome/getnextevents + * @param options + * @param callback + * @returns {*} + */ +netatmo.prototype.getNextEvents = function (options, callback) { + // Wait until authenticated. + if (!access_token) { + return this.on('authenticated', function () { + this.getNextEvents(options, callback); + }); + } + + if (!options) { + this.emit("error", new Error("getNextEvents 'options' not set.")); + return this; + } + + if (!options.home_id) { + this.emit("error", new Error("getNextEvents 'home_id' not set.")); + return this; + } + + if (!options.event_id) { + this.emit("error", new Error("getNextEvents 'event_id' not set.")); + return this; + } + + var url = util.format('%s/api/getnextevents', BASE_URL); + + var form = { + access_token: access_token, + home_id: options.home_id, + event_id: options.event_id, + }; + + if (options.size) { + form.size = options.size; + } + + request({ + url: url, + method: "POST", + form: form, + timeout: options && options.timeout || timeout + }, function (err, response, body) { + if (err || response.statusCode != 200) { + var error = this.handleRequestError(err, response, body, "getNextEvents error"); + if (callback) { + callback(error); + } + return error; + } + + body = JSON.parse(body); + + this.emit('get-nextevents', err, body.body); + + if (callback) { + return callback(err, body.body); + } + + return this; + + }.bind(this)); + + return this; +}; + +/** + * https://dev.netatmo.com/dev/resources/technical/reference/welcome/getlasteventof + * @param options + * @param callback + * @returns {*} + */ +netatmo.prototype.getLastEventOf = function (options, callback) { + // Wait until authenticated. + if (!access_token) { + return this.on('authenticated', function () { + this.getLastEventOf(options, callback); + }); + } + + if (!options) { + this.emit("error", new Error("getLastEventOf 'options' not set.")); + return this; + } + + if (!options.home_id) { + this.emit("error", new Error("getLastEventOf 'home_id' not set.")); + return this; + } + + if (!options.person_id) { + this.emit("error", new Error("getLastEventOf 'person_id' not set.")); + return this; + } + + var url = util.format('%s/api/getlasteventof', BASE_URL); + + var form = { + access_token: access_token, + home_id: options.home_id, + person_id: options.person_id, + }; + + if (options.offset) { + form.offset = options.offset; + } + + request({ + url: url, + method: "POST", + form: form, + timeout: options && options.timeout || timeout + }, function (err, response, body) { + if (err || response.statusCode != 200) { + var error = this.handleRequestError(err, response, body, "getLastEventOf error"); + if (callback) { + callback(error); + } + return error; + } + + body = JSON.parse(body); + + this.emit('get-lasteventof', err, body.body); + + if (callback) { + return callback(err, body.body); + } + + return this; + + }.bind(this)); + + return this; +}; + +/** + * https://dev.netatmo.com/dev/resources/technical/reference/welcome/geteventsuntil + * @param options + * @param callback + * @returns {*} + */ +netatmo.prototype.getEventsUntil = function (options, callback) { + // Wait until authenticated. + if (!access_token) { + return this.on('authenticated', function () { + this.getEventsUntil(options, callback); + }); + } + + if (!options) { + this.emit("error", new Error("getEventsUntil 'options' not set.")); + return this; + } + + if (!options.home_id) { + this.emit("error", new Error("getEventsUntil 'home_id' not set.")); + return this; + } + + if (!options.event_id) { + this.emit("error", new Error("getEventsUntil 'event_id' not set.")); + return this; + } + + var url = util.format('%s/api/geteventsuntil', BASE_URL); + + var form = { + access_token: access_token, + home_id: options.home_id, + event_id: options.event_id, + }; + + request({ + url: url, + method: "POST", + form: form, + timeout: options && options.timeout || timeout + }, function (err, response, body) { + if (err || response.statusCode != 200) { + var error = this.handleRequestError(err, response, body, "getEventsUntil error"); + if (callback) { + callback(error); + } + return error; + } + + body = JSON.parse(body); + + this.emit('get-eventsuntil', err, body.body); + + if (callback) { + return callback(err, body.body); + } + + return this; + + }.bind(this)); + + return this; +}; + +/** + * https://dev.netatmo.com/dev/resources/technical/reference/welcome/getcamerapicture + * @param options + * @param callback + * @returns {*} + */ +netatmo.prototype.getCameraPicture = function (options, callback) { + // Wait until authenticated. + if (!access_token) { + return this.on('authenticated', function () { + this.getCameraPicture(options, callback); + }); + } + + if (!options) { + this.emit("error", new Error("getCameraPicture 'options' not set.")); + return this; + } + + if (!options.image_id) { + this.emit("error", new Error("getCameraPicture 'image_id' not set.")); + return this; + } + + if (!options.key) { + this.emit("error", new Error("getCameraPicture 'key' not set.")); + return this; + } + + var url = util.format('%s/api/getcamerapicture', BASE_URL); + + var qs = { + access_token: access_token, + image_id: options.image_id, + key: options.key, + }; + + request({ + url: url, + method: "GET", + qs: qs, + encoding: null, + contentType: 'image/jpg', + timeout: options && options.timeout || timeout + }, function (err, response, body) { + if (err || response.statusCode != 200) { + var error = this.handleRequestError(err, response, body, "getCameraPicture error"); + if (callback) { + callback(error); + } + return error; + } + + this.emit('get-camerapicture', err, body); + + if (callback) { + return callback(err, body); + } + + return this; + + }.bind(this)); + + return this; +}; + +/** + * https://dev.netatmo.com/dev/resources/technical/reference/healthyhomecoach/gethomecoachsdata + * @param options + * @param callback + * @returns {*} + */ +netatmo.prototype.getHealthyHomeCoachData = function (options, callback) { + // Wait until authenticated. + if (!access_token) { + return this.on('authenticated', function () { + this.getHealthyHomeCoachData(options, callback); + }); + } + + if (options != null && callback == null) { + callback = options; + options = null; + } + + var url = util.format('%s/api/gethomecoachsdata?access_token=%s', BASE_URL, access_token); + if (options != null) { + url = util.format(url + '&device_id=%s', options.device_id); + } + + request({ + url: url, + method: "GET", + timeout: options && options.timeout || timeout + }, function (err, response, body) { + if (err || response.statusCode != 200) { + var error = this.handleRequestError(err, response, body, "getHealthyHomeCoachData error"); + if (callback) { + callback(error); + } + return error; + } + + body = JSON.parse(body); + + var devices = body.body.devices; + + this.emit('get-healthhomecoaches-data', err, devices); + + if (callback) { + return callback(err, devices); + } + + return this; + + }.bind(this)); + + return this; +}; + +module.exports = netatmo; From 5dcbceefca497764fcb717962803c123e5c18659 Mon Sep 17 00:00:00 2001 From: Wolfgang Sommerer Date: Tue, 13 Jun 2017 20:06:25 +0200 Subject: [PATCH 4/5] changed crlf setting --- netatmo.js | 1954 ++++++++++++++++++++++++++-------------------------- 1 file changed, 977 insertions(+), 977 deletions(-) diff --git a/netatmo.js b/netatmo.js index d787969..d81f171 100644 --- a/netatmo.js +++ b/netatmo.js @@ -1,977 +1,977 @@ -var util = require('util'); -var EventEmitter = require("events").EventEmitter; -var request = require('request'); -var moment = require('moment'); - -const BASE_URL = 'https://api.netatmo.net'; - -var username; -var password; -var client_id; -var client_secret; -var scope; -var access_token; -var timeout; - -/** - * @constructor - * @param args - */ -var netatmo = function (args) { - EventEmitter.call(this); - this.authenticate(args); -}; - -util.inherits(netatmo, EventEmitter); - -/** - * handleRequestError - * @param err - * @param response - * @param body - * @param message - * @param critical - * @returns {Error} - */ -netatmo.prototype.handleRequestError = function (err, response, body, message, critical) { - var errorMessage = ""; - if (body && response.headers['content-type'] === 'application/json') { - errorMessage = JSON.parse(body); - errorMessage = errorMessage && (errorMessage.error.message || errorMessage.error); - } else if (typeof response !== 'undefined') { - errorMessage = "Status code" + response.statusCode; - } - else { - errorMessage = "No response"; - } - var error = new Error(message + ": " + errorMessage); - if (critical) { - this.emit("error", error); - } else { - this.emit("warning", error); - } - return error; -}; - -/** - * https://dev.netatmo.com/dev/resources/technical/guides/authentication - * @param args - * @param callback - * @returns {netatmo} - */ -netatmo.prototype.authenticate = function (args, callback) { - if (!args) { - this.emit("error", new Error("Authenticate 'args' not set.")); - return this; - } - - if (args.access_token) { - access_token = args.access_token; - return this; - } - - if (!args.client_id) { - this.emit("error", new Error("Authenticate 'client_id' not set.")); - return this; - } - - if (!args.client_secret) { - this.emit("error", new Error("Authenticate 'client_secret' not set.")); - return this; - } - - if (!args.username) { - this.emit("error", new Error("Authenticate 'username' not set.")); - return this; - } - - if (!args.password) { - this.emit("error", new Error("Authenticate 'password' not set.")); - return this; - } - - username = args.username; - password = args.password; - client_id = args.client_id; - client_secret = args.client_secret; - timeout = args.timeout; - scope = args.scope || 'read_station read_thermostat write_thermostat read_camera read_homecoach'; - - var form = { - client_id: client_id, - client_secret: client_secret, - username: username, - password: password, - scope: scope, - grant_type: 'password', - }; - - var url = util.format('%s/oauth2/token', BASE_URL); - - request({ - url: url, - method: "POST", - form: form, - timeout: timeout - }, function (err, response, body) { - if (err || response.statusCode != 200) { - var error = this.handleRequestError(err, response, body, "Authenticate error"); - if (callback) { - callback(error); - } - return error; - } - - body = JSON.parse(body); - - access_token = body.access_token; - - if (body.expires_in) { - setTimeout(this.authenticate_refresh.bind(this), body.expires_in * 1000, body.refresh_token); - } - - this.emit('authenticated'); - - if (callback) { - return callback(); - } - - return this; - }.bind(this)); - - return this; -}; - -/** - * https://dev.netatmo.com/dev/resources/technical/guides/authentication/refreshingatoken - * @param refresh_token - * @returns {netatmo} - */ -netatmo.prototype.authenticate_refresh = function (refresh_token) { - - var form = { - grant_type: 'refresh_token', - refresh_token: refresh_token, - client_id: client_id, - client_secret: client_secret, - }; - - var url = util.format('%s/oauth2/token', BASE_URL); - - request({ - url: url, - method: "POST", - form: form, - timeout: timeout - }, function (err, response, body) { - if (err || response.statusCode != 200) { - return this.handleRequestError(err, response, body, "Authenticate refresh error"); - } - - body = JSON.parse(body); - - access_token = body.access_token; - - if (body.expires_in) { - setTimeout(this.authenticate_refresh.bind(this), body.expires_in * 1000, body.refresh_token); - } - - return this; - }.bind(this)); - - return this; -}; - -/** - * https://dev.netatmo.com/dev/resources/technical/reference/weatherstation/getstationsdata - * @param options - * @param callback - * @returns {*} - */ -netatmo.prototype.getStationsData = function (options, callback) { - // Wait until authenticated. - if (!access_token) { - return this.on('authenticated', function () { - this.getStationsData(options, callback); - }); - } - - if (options != null && callback == null) { - callback = options; - options = null; - } - - var url = util.format('%s/api/getstationsdata', BASE_URL); - - var form = { - access_token: access_token, - }; - - if (options) { - if (options.device_id) { - form.device_id = options.device_id; - } - if (options.get_favorites) { - form.get_favorites = options.get_favorites; - } - } - - request({ - url: url, - method: "POST", - form: form, - timeout: options && options.timeout || timeout - }, function (err, response, body) { - if (err || response.statusCode != 200) { - var error = this.handleRequestError(err, response, body, "getStationsData error"); - if (callback) { - callback(error); - } - return error; - } - - body = JSON.parse(body); - - var devices = body.body.devices; - - this.emit('get-stationsdata', err, devices); - - if (callback) { - return callback(err, devices); - } - - return this; - - }.bind(this)); - - return this; -}; - -/** - * https://dev.netatmo.com/dev/resources/technical/reference/thermostat/getthermostatsdata - * @param options - * @param callback - * @returns {*} - */ -netatmo.prototype.getThermostatsData = function (options, callback) { - // Wait until authenticated. - if (!access_token) { - return this.on('authenticated', function () { - this.getThermostatsData(options, callback); - }); - } - - if (options != null && callback == null) { - callback = options; - options = null; - } - - var url = util.format('%s/api/getthermostatsdata?access_token=%s', BASE_URL, access_token); - if (options != null) { - url = util.format(url + '&device_id=%s', options.device_id); - } - - request({ - url: url, - method: "GET", - timeout: options && options.timeout || timeout - }, function (err, response, body) { - if (err || response.statusCode != 200) { - var error = this.handleRequestError(err, response, body, "getThermostatsData error"); - if (callback) { - callback(error); - } - return error; - } - - body = JSON.parse(body); - - var devices = body.body.devices; - - this.emit('get-thermostatsdata', err, devices); - - if (callback) { - return callback(err, devices); - } - - return this; - - }.bind(this)); - - return this; -}; - -/** - * https://dev.netatmo.com/dev/resources/technical/reference/common/getmeasure - * @param options - * @param callback - * @returns {*} - */ -netatmo.prototype.getMeasure = function (options, callback) { - // Wait until authenticated. - if (!access_token) { - return this.on('authenticated', function () { - this.getMeasure(options, callback); - }); - } - - if (!options) { - this.emit("error", new Error("getMeasure 'options' not set.")); - return this; - } - - if (!options.device_id) { - this.emit("error", new Error("getMeasure 'device_id' not set.")); - return this; - } - - if (!options.scale) { - this.emit("error", new Error("getMeasure 'scale' not set.")); - return this; - } - - if (!options.type) { - this.emit("error", new Error("getMeasure 'type' not set.")); - return this; - } - - if (util.isArray(options.type)) { - options.type = options.type.join(','); - } - - // Remove any spaces from the type list if there is any. - options.type = options.type.replace(/\s/g, '').toLowerCase(); - - - var url = util.format('%s/api/getmeasure', BASE_URL); - - var form = { - access_token: access_token, - device_id: options.device_id, - scale: options.scale, - type: options.type, - }; - - if (options) { - - if (options.module_id) { - form.module_id = options.module_id; - } - - if (options.date_begin) { - if (options.date_begin <= 1E10) { - options.date_begin *= 1E3; - } - - form.date_begin = moment(options.date_begin).utc().unix(); - } - - if (options.date_end === 'last') { - form.date_end = 'last'; - } else if (options.date_end) { - if (options.date_end <= 1E10) { - options.date_end *= 1E3; - } - form.date_end = moment(options.date_end).utc().unix(); - } - - if (options.limit) { - form.limit = parseInt(options.limit, 10); - - if (form.limit > 1024) { - form.limit = 1024; - } - } - - if (options.optimize !== undefined) { - form.optimize = !!options.optimize; - } - - if (options.real_time !== undefined) { - form.real_time = !!options.real_time; - } - } - - request({ - url: url, - method: "POST", - form: form, - timeout: options && options.timeout || timeout - }, function (err, response, body) { - if (err || response.statusCode != 200) { - var error = this.handleRequestError(err, response, body, "getMeasure error"); - if (callback) { - callback(error); - } - return error; - } - - body = JSON.parse(body); - - var measure = body.body; - - this.emit('get-measure', err, measure); - - if (callback) { - return callback(err, measure); - } - - return this; - - }.bind(this)); - - return this; -}; - -/** - * https://dev.netatmo.com/dev/resources/technical/reference/thermostat/syncschedule - * @param options - * @param callback - * @returns {*} - */ -netatmo.prototype.setSyncSchedule = function (options, callback) { - // Wait until authenticated. - if (!access_token) { - return this.on('authenticated', function () { - this.setSyncSchedule(options, callback); - }); - } - - if (!options) { - this.emit("error", new Error("setSyncSchedule 'options' not set.")); - return this; - } - - if (!options.device_id) { - this.emit("error", new Error("setSyncSchedule 'device_id' not set.")); - return this; - } - - if (!options.module_id) { - this.emit("error", new Error("setSyncSchedule 'module_id' not set.")); - return this; - } - - if (!options.zones) { - this.emit("error", new Error("setSyncSchedule 'zones' not set.")); - return this; - } - - if (!options.timetable) { - this.emit("error", new Error("setSyncSchedule 'timetable' not set.")); - return this; - } - - var url = util.format('%s/api/syncschedule', BASE_URL); - - var form = { - access_token: access_token, - device_id: options.device_id, - module_id: options.module_id, - zones: options.zones, - timetable: options.timetable, - }; - - request({ - url: url, - method: "POST", - form: form, - timeout: options && options.timeout || timeout - }, function (err, response, body) { - if (err || response.statusCode != 200) { - var error = this.handleRequestError(err, response, body, "setSyncSchedule error"); - if (callback) { - callback(error); - } - return error; - } - - body = JSON.parse(body); - - this.emit('set-syncschedule', err, body.status); - - if (callback) { - return callback(err, body.status); - } - - return this; - - }.bind(this)); - - return this; -}; - -/** - * https://dev.netatmo.com/dev/resources/technical/reference/thermostat/setthermpoint - * @param options - * @param callback - * @returns {*} - */ -netatmo.prototype.setThermpoint = function (options, callback) { - // Wait until authenticated. - if (!access_token) { - return this.on('authenticated', function () { - this.setThermpoint(options, callback); - }); - } - - if (!options) { - this.emit("error", new Error("setThermpoint 'options' not set.")); - return this; - } - - if (!options.device_id) { - this.emit("error", new Error("setThermpoint 'device_id' not set.")); - return this; - } - - if (!options.module_id) { - this.emit("error", new Error("setThermpoint 'module_id' not set.")); - return this; - } - - if (!options.setpoint_mode) { - this.emit("error", new Error("setThermpoint 'setpoint_mode' not set.")); - return this; - } - - var url = util.format('%s/api/setthermpoint', BASE_URL); - - var form = { - access_token: access_token, - device_id: options.device_id, - module_id: options.module_id, - setpoint_mode: options.setpoint_mode, - }; - - if (options) { - - if (options.setpoint_endtime) { - form.setpoint_endtime = options.setpoint_endtime; - } - - if (options.setpoint_temp) { - form.setpoint_temp = options.setpoint_temp; - } - - } - - request({ - url: url, - method: "POST", - form: form, - timeout: options && options.timeout || timeout - }, function (err, response, body) { - if (err || response.statusCode != 200) { - var error = this.handleRequestError(err, response, body, "setThermpoint error"); - if (callback) { - callback(error); - } - return error; - } - - body = JSON.parse(body); - - this.emit('get-thermostatsdata', err, body.status); - - if (callback) { - return callback(err, body.status); - } - - return this; - - }.bind(this)); - - return this; -}; - -/** - * https://dev.netatmo.com/dev/resources/technical/reference/welcome/gethomedata - * @param options - * @param callback - * @returns {*} - */ -netatmo.prototype.getHomeData = function (options, callback) { - // Wait until authenticated. - if (!access_token) { - return this.on('authenticated', function () { - this.getHomeData(options, callback); - }); - } - - var url = util.format('%s/api/gethomedata', BASE_URL); - - var form = { - access_token: access_token - }; - - if (options != null && callback == null) { - callback = options; - options = null; - } - - if (options) { - if (options.home_id) { - form.home_id = options.home_id; - } - if (options.size) { - form.size = options.size; - } - } - - request({ - url: url, - method: "POST", - form: form, - timeout: options && options.timeout || timeout - }, function (err, response, body) { - if (err || response.statusCode != 200) { - var error = this.handleRequestError(err, response, body, "getHomeData error"); - if (callback) { - callback(error); - } - return error; - } - - body = JSON.parse(body); - - this.emit('get-homedata', err, body.body); - - if (callback) { - return callback(err, body.body); - } - - return this; - - }.bind(this)); - - return this; -}; - -/** - * https://dev.netatmo.com/dev/resources/technical/reference/welcome/getnextevents - * @param options - * @param callback - * @returns {*} - */ -netatmo.prototype.getNextEvents = function (options, callback) { - // Wait until authenticated. - if (!access_token) { - return this.on('authenticated', function () { - this.getNextEvents(options, callback); - }); - } - - if (!options) { - this.emit("error", new Error("getNextEvents 'options' not set.")); - return this; - } - - if (!options.home_id) { - this.emit("error", new Error("getNextEvents 'home_id' not set.")); - return this; - } - - if (!options.event_id) { - this.emit("error", new Error("getNextEvents 'event_id' not set.")); - return this; - } - - var url = util.format('%s/api/getnextevents', BASE_URL); - - var form = { - access_token: access_token, - home_id: options.home_id, - event_id: options.event_id, - }; - - if (options.size) { - form.size = options.size; - } - - request({ - url: url, - method: "POST", - form: form, - timeout: options && options.timeout || timeout - }, function (err, response, body) { - if (err || response.statusCode != 200) { - var error = this.handleRequestError(err, response, body, "getNextEvents error"); - if (callback) { - callback(error); - } - return error; - } - - body = JSON.parse(body); - - this.emit('get-nextevents', err, body.body); - - if (callback) { - return callback(err, body.body); - } - - return this; - - }.bind(this)); - - return this; -}; - -/** - * https://dev.netatmo.com/dev/resources/technical/reference/welcome/getlasteventof - * @param options - * @param callback - * @returns {*} - */ -netatmo.prototype.getLastEventOf = function (options, callback) { - // Wait until authenticated. - if (!access_token) { - return this.on('authenticated', function () { - this.getLastEventOf(options, callback); - }); - } - - if (!options) { - this.emit("error", new Error("getLastEventOf 'options' not set.")); - return this; - } - - if (!options.home_id) { - this.emit("error", new Error("getLastEventOf 'home_id' not set.")); - return this; - } - - if (!options.person_id) { - this.emit("error", new Error("getLastEventOf 'person_id' not set.")); - return this; - } - - var url = util.format('%s/api/getlasteventof', BASE_URL); - - var form = { - access_token: access_token, - home_id: options.home_id, - person_id: options.person_id, - }; - - if (options.offset) { - form.offset = options.offset; - } - - request({ - url: url, - method: "POST", - form: form, - timeout: options && options.timeout || timeout - }, function (err, response, body) { - if (err || response.statusCode != 200) { - var error = this.handleRequestError(err, response, body, "getLastEventOf error"); - if (callback) { - callback(error); - } - return error; - } - - body = JSON.parse(body); - - this.emit('get-lasteventof', err, body.body); - - if (callback) { - return callback(err, body.body); - } - - return this; - - }.bind(this)); - - return this; -}; - -/** - * https://dev.netatmo.com/dev/resources/technical/reference/welcome/geteventsuntil - * @param options - * @param callback - * @returns {*} - */ -netatmo.prototype.getEventsUntil = function (options, callback) { - // Wait until authenticated. - if (!access_token) { - return this.on('authenticated', function () { - this.getEventsUntil(options, callback); - }); - } - - if (!options) { - this.emit("error", new Error("getEventsUntil 'options' not set.")); - return this; - } - - if (!options.home_id) { - this.emit("error", new Error("getEventsUntil 'home_id' not set.")); - return this; - } - - if (!options.event_id) { - this.emit("error", new Error("getEventsUntil 'event_id' not set.")); - return this; - } - - var url = util.format('%s/api/geteventsuntil', BASE_URL); - - var form = { - access_token: access_token, - home_id: options.home_id, - event_id: options.event_id, - }; - - request({ - url: url, - method: "POST", - form: form, - timeout: options && options.timeout || timeout - }, function (err, response, body) { - if (err || response.statusCode != 200) { - var error = this.handleRequestError(err, response, body, "getEventsUntil error"); - if (callback) { - callback(error); - } - return error; - } - - body = JSON.parse(body); - - this.emit('get-eventsuntil', err, body.body); - - if (callback) { - return callback(err, body.body); - } - - return this; - - }.bind(this)); - - return this; -}; - -/** - * https://dev.netatmo.com/dev/resources/technical/reference/welcome/getcamerapicture - * @param options - * @param callback - * @returns {*} - */ -netatmo.prototype.getCameraPicture = function (options, callback) { - // Wait until authenticated. - if (!access_token) { - return this.on('authenticated', function () { - this.getCameraPicture(options, callback); - }); - } - - if (!options) { - this.emit("error", new Error("getCameraPicture 'options' not set.")); - return this; - } - - if (!options.image_id) { - this.emit("error", new Error("getCameraPicture 'image_id' not set.")); - return this; - } - - if (!options.key) { - this.emit("error", new Error("getCameraPicture 'key' not set.")); - return this; - } - - var url = util.format('%s/api/getcamerapicture', BASE_URL); - - var qs = { - access_token: access_token, - image_id: options.image_id, - key: options.key, - }; - - request({ - url: url, - method: "GET", - qs: qs, - encoding: null, - contentType: 'image/jpg', - timeout: options && options.timeout || timeout - }, function (err, response, body) { - if (err || response.statusCode != 200) { - var error = this.handleRequestError(err, response, body, "getCameraPicture error"); - if (callback) { - callback(error); - } - return error; - } - - this.emit('get-camerapicture', err, body); - - if (callback) { - return callback(err, body); - } - - return this; - - }.bind(this)); - - return this; -}; - -/** - * https://dev.netatmo.com/dev/resources/technical/reference/healthyhomecoach/gethomecoachsdata - * @param options - * @param callback - * @returns {*} - */ -netatmo.prototype.getHealthyHomeCoachData = function (options, callback) { - // Wait until authenticated. - if (!access_token) { - return this.on('authenticated', function () { - this.getHealthyHomeCoachData(options, callback); - }); - } - - if (options != null && callback == null) { - callback = options; - options = null; - } - - var url = util.format('%s/api/gethomecoachsdata?access_token=%s', BASE_URL, access_token); - if (options != null) { - url = util.format(url + '&device_id=%s', options.device_id); - } - - request({ - url: url, - method: "GET", - timeout: options && options.timeout || timeout - }, function (err, response, body) { - if (err || response.statusCode != 200) { - var error = this.handleRequestError(err, response, body, "getHealthyHomeCoachData error"); - if (callback) { - callback(error); - } - return error; - } - - body = JSON.parse(body); - - var devices = body.body.devices; - - this.emit('get-healthhomecoaches-data', err, devices); - - if (callback) { - return callback(err, devices); - } - - return this; - - }.bind(this)); - - return this; -}; - -module.exports = netatmo; +var util = require('util'); +var EventEmitter = require("events").EventEmitter; +var request = require('request'); +var moment = require('moment'); + +const BASE_URL = 'https://api.netatmo.net'; + +var username; +var password; +var client_id; +var client_secret; +var scope; +var access_token; +var timeout; + +/** + * @constructor + * @param args + */ +var netatmo = function (args) { + EventEmitter.call(this); + this.authenticate(args); +}; + +util.inherits(netatmo, EventEmitter); + +/** + * handleRequestError + * @param err + * @param response + * @param body + * @param message + * @param critical + * @returns {Error} + */ +netatmo.prototype.handleRequestError = function (err, response, body, message, critical) { + var errorMessage = ""; + if (body && response.headers['content-type'] === 'application/json') { + errorMessage = JSON.parse(body); + errorMessage = errorMessage && (errorMessage.error.message || errorMessage.error); + } else if (typeof response !== 'undefined') { + errorMessage = "Status code" + response.statusCode; + } + else { + errorMessage = "No response"; + } + var error = new Error(message + ": " + errorMessage); + if (critical) { + this.emit("error", error); + } else { + this.emit("warning", error); + } + return error; +}; + +/** + * https://dev.netatmo.com/dev/resources/technical/guides/authentication + * @param args + * @param callback + * @returns {netatmo} + */ +netatmo.prototype.authenticate = function (args, callback) { + if (!args) { + this.emit("error", new Error("Authenticate 'args' not set.")); + return this; + } + + if (args.access_token) { + access_token = args.access_token; + return this; + } + + if (!args.client_id) { + this.emit("error", new Error("Authenticate 'client_id' not set.")); + return this; + } + + if (!args.client_secret) { + this.emit("error", new Error("Authenticate 'client_secret' not set.")); + return this; + } + + if (!args.username) { + this.emit("error", new Error("Authenticate 'username' not set.")); + return this; + } + + if (!args.password) { + this.emit("error", new Error("Authenticate 'password' not set.")); + return this; + } + + username = args.username; + password = args.password; + client_id = args.client_id; + client_secret = args.client_secret; + timeout = args.timeout; + scope = args.scope || 'read_station read_thermostat write_thermostat read_camera read_homecoach'; + + var form = { + client_id: client_id, + client_secret: client_secret, + username: username, + password: password, + scope: scope, + grant_type: 'password', + }; + + var url = util.format('%s/oauth2/token', BASE_URL); + + request({ + url: url, + method: "POST", + form: form, + timeout: timeout + }, function (err, response, body) { + if (err || response.statusCode != 200) { + var error = this.handleRequestError(err, response, body, "Authenticate error"); + if (callback) { + callback(error); + } + return error; + } + + body = JSON.parse(body); + + access_token = body.access_token; + + if (body.expires_in) { + setTimeout(this.authenticate_refresh.bind(this), body.expires_in * 1000, body.refresh_token); + } + + this.emit('authenticated'); + + if (callback) { + return callback(); + } + + return this; + }.bind(this)); + + return this; +}; + +/** + * https://dev.netatmo.com/dev/resources/technical/guides/authentication/refreshingatoken + * @param refresh_token + * @returns {netatmo} + */ +netatmo.prototype.authenticate_refresh = function (refresh_token) { + + var form = { + grant_type: 'refresh_token', + refresh_token: refresh_token, + client_id: client_id, + client_secret: client_secret, + }; + + var url = util.format('%s/oauth2/token', BASE_URL); + + request({ + url: url, + method: "POST", + form: form, + timeout: timeout + }, function (err, response, body) { + if (err || response.statusCode != 200) { + return this.handleRequestError(err, response, body, "Authenticate refresh error"); + } + + body = JSON.parse(body); + + access_token = body.access_token; + + if (body.expires_in) { + setTimeout(this.authenticate_refresh.bind(this), body.expires_in * 1000, body.refresh_token); + } + + return this; + }.bind(this)); + + return this; +}; + +/** + * https://dev.netatmo.com/dev/resources/technical/reference/weatherstation/getstationsdata + * @param options + * @param callback + * @returns {*} + */ +netatmo.prototype.getStationsData = function (options, callback) { + // Wait until authenticated. + if (!access_token) { + return this.on('authenticated', function () { + this.getStationsData(options, callback); + }); + } + + if (options != null && callback == null) { + callback = options; + options = null; + } + + var url = util.format('%s/api/getstationsdata', BASE_URL); + + var form = { + access_token: access_token, + }; + + if (options) { + if (options.device_id) { + form.device_id = options.device_id; + } + if (options.get_favorites) { + form.get_favorites = options.get_favorites; + } + } + + request({ + url: url, + method: "POST", + form: form, + timeout: options && options.timeout || timeout + }, function (err, response, body) { + if (err || response.statusCode != 200) { + var error = this.handleRequestError(err, response, body, "getStationsData error"); + if (callback) { + callback(error); + } + return error; + } + + body = JSON.parse(body); + + var devices = body.body.devices; + + this.emit('get-stationsdata', err, devices); + + if (callback) { + return callback(err, devices); + } + + return this; + + }.bind(this)); + + return this; +}; + +/** + * https://dev.netatmo.com/dev/resources/technical/reference/thermostat/getthermostatsdata + * @param options + * @param callback + * @returns {*} + */ +netatmo.prototype.getThermostatsData = function (options, callback) { + // Wait until authenticated. + if (!access_token) { + return this.on('authenticated', function () { + this.getThermostatsData(options, callback); + }); + } + + if (options != null && callback == null) { + callback = options; + options = null; + } + + var url = util.format('%s/api/getthermostatsdata?access_token=%s', BASE_URL, access_token); + if (options != null) { + url = util.format(url + '&device_id=%s', options.device_id); + } + + request({ + url: url, + method: "GET", + timeout: options && options.timeout || timeout + }, function (err, response, body) { + if (err || response.statusCode != 200) { + var error = this.handleRequestError(err, response, body, "getThermostatsData error"); + if (callback) { + callback(error); + } + return error; + } + + body = JSON.parse(body); + + var devices = body.body.devices; + + this.emit('get-thermostatsdata', err, devices); + + if (callback) { + return callback(err, devices); + } + + return this; + + }.bind(this)); + + return this; +}; + +/** + * https://dev.netatmo.com/dev/resources/technical/reference/common/getmeasure + * @param options + * @param callback + * @returns {*} + */ +netatmo.prototype.getMeasure = function (options, callback) { + // Wait until authenticated. + if (!access_token) { + return this.on('authenticated', function () { + this.getMeasure(options, callback); + }); + } + + if (!options) { + this.emit("error", new Error("getMeasure 'options' not set.")); + return this; + } + + if (!options.device_id) { + this.emit("error", new Error("getMeasure 'device_id' not set.")); + return this; + } + + if (!options.scale) { + this.emit("error", new Error("getMeasure 'scale' not set.")); + return this; + } + + if (!options.type) { + this.emit("error", new Error("getMeasure 'type' not set.")); + return this; + } + + if (util.isArray(options.type)) { + options.type = options.type.join(','); + } + + // Remove any spaces from the type list if there is any. + options.type = options.type.replace(/\s/g, '').toLowerCase(); + + + var url = util.format('%s/api/getmeasure', BASE_URL); + + var form = { + access_token: access_token, + device_id: options.device_id, + scale: options.scale, + type: options.type, + }; + + if (options) { + + if (options.module_id) { + form.module_id = options.module_id; + } + + if (options.date_begin) { + if (options.date_begin <= 1E10) { + options.date_begin *= 1E3; + } + + form.date_begin = moment(options.date_begin).utc().unix(); + } + + if (options.date_end === 'last') { + form.date_end = 'last'; + } else if (options.date_end) { + if (options.date_end <= 1E10) { + options.date_end *= 1E3; + } + form.date_end = moment(options.date_end).utc().unix(); + } + + if (options.limit) { + form.limit = parseInt(options.limit, 10); + + if (form.limit > 1024) { + form.limit = 1024; + } + } + + if (options.optimize !== undefined) { + form.optimize = !!options.optimize; + } + + if (options.real_time !== undefined) { + form.real_time = !!options.real_time; + } + } + + request({ + url: url, + method: "POST", + form: form, + timeout: options && options.timeout || timeout + }, function (err, response, body) { + if (err || response.statusCode != 200) { + var error = this.handleRequestError(err, response, body, "getMeasure error"); + if (callback) { + callback(error); + } + return error; + } + + body = JSON.parse(body); + + var measure = body.body; + + this.emit('get-measure', err, measure); + + if (callback) { + return callback(err, measure); + } + + return this; + + }.bind(this)); + + return this; +}; + +/** + * https://dev.netatmo.com/dev/resources/technical/reference/thermostat/syncschedule + * @param options + * @param callback + * @returns {*} + */ +netatmo.prototype.setSyncSchedule = function (options, callback) { + // Wait until authenticated. + if (!access_token) { + return this.on('authenticated', function () { + this.setSyncSchedule(options, callback); + }); + } + + if (!options) { + this.emit("error", new Error("setSyncSchedule 'options' not set.")); + return this; + } + + if (!options.device_id) { + this.emit("error", new Error("setSyncSchedule 'device_id' not set.")); + return this; + } + + if (!options.module_id) { + this.emit("error", new Error("setSyncSchedule 'module_id' not set.")); + return this; + } + + if (!options.zones) { + this.emit("error", new Error("setSyncSchedule 'zones' not set.")); + return this; + } + + if (!options.timetable) { + this.emit("error", new Error("setSyncSchedule 'timetable' not set.")); + return this; + } + + var url = util.format('%s/api/syncschedule', BASE_URL); + + var form = { + access_token: access_token, + device_id: options.device_id, + module_id: options.module_id, + zones: options.zones, + timetable: options.timetable, + }; + + request({ + url: url, + method: "POST", + form: form, + timeout: options && options.timeout || timeout + }, function (err, response, body) { + if (err || response.statusCode != 200) { + var error = this.handleRequestError(err, response, body, "setSyncSchedule error"); + if (callback) { + callback(error); + } + return error; + } + + body = JSON.parse(body); + + this.emit('set-syncschedule', err, body.status); + + if (callback) { + return callback(err, body.status); + } + + return this; + + }.bind(this)); + + return this; +}; + +/** + * https://dev.netatmo.com/dev/resources/technical/reference/thermostat/setthermpoint + * @param options + * @param callback + * @returns {*} + */ +netatmo.prototype.setThermpoint = function (options, callback) { + // Wait until authenticated. + if (!access_token) { + return this.on('authenticated', function () { + this.setThermpoint(options, callback); + }); + } + + if (!options) { + this.emit("error", new Error("setThermpoint 'options' not set.")); + return this; + } + + if (!options.device_id) { + this.emit("error", new Error("setThermpoint 'device_id' not set.")); + return this; + } + + if (!options.module_id) { + this.emit("error", new Error("setThermpoint 'module_id' not set.")); + return this; + } + + if (!options.setpoint_mode) { + this.emit("error", new Error("setThermpoint 'setpoint_mode' not set.")); + return this; + } + + var url = util.format('%s/api/setthermpoint', BASE_URL); + + var form = { + access_token: access_token, + device_id: options.device_id, + module_id: options.module_id, + setpoint_mode: options.setpoint_mode, + }; + + if (options) { + + if (options.setpoint_endtime) { + form.setpoint_endtime = options.setpoint_endtime; + } + + if (options.setpoint_temp) { + form.setpoint_temp = options.setpoint_temp; + } + + } + + request({ + url: url, + method: "POST", + form: form, + timeout: options && options.timeout || timeout + }, function (err, response, body) { + if (err || response.statusCode != 200) { + var error = this.handleRequestError(err, response, body, "setThermpoint error"); + if (callback) { + callback(error); + } + return error; + } + + body = JSON.parse(body); + + this.emit('get-thermostatsdata', err, body.status); + + if (callback) { + return callback(err, body.status); + } + + return this; + + }.bind(this)); + + return this; +}; + +/** + * https://dev.netatmo.com/dev/resources/technical/reference/welcome/gethomedata + * @param options + * @param callback + * @returns {*} + */ +netatmo.prototype.getHomeData = function (options, callback) { + // Wait until authenticated. + if (!access_token) { + return this.on('authenticated', function () { + this.getHomeData(options, callback); + }); + } + + var url = util.format('%s/api/gethomedata', BASE_URL); + + var form = { + access_token: access_token + }; + + if (options != null && callback == null) { + callback = options; + options = null; + } + + if (options) { + if (options.home_id) { + form.home_id = options.home_id; + } + if (options.size) { + form.size = options.size; + } + } + + request({ + url: url, + method: "POST", + form: form, + timeout: options && options.timeout || timeout + }, function (err, response, body) { + if (err || response.statusCode != 200) { + var error = this.handleRequestError(err, response, body, "getHomeData error"); + if (callback) { + callback(error); + } + return error; + } + + body = JSON.parse(body); + + this.emit('get-homedata', err, body.body); + + if (callback) { + return callback(err, body.body); + } + + return this; + + }.bind(this)); + + return this; +}; + +/** + * https://dev.netatmo.com/dev/resources/technical/reference/welcome/getnextevents + * @param options + * @param callback + * @returns {*} + */ +netatmo.prototype.getNextEvents = function (options, callback) { + // Wait until authenticated. + if (!access_token) { + return this.on('authenticated', function () { + this.getNextEvents(options, callback); + }); + } + + if (!options) { + this.emit("error", new Error("getNextEvents 'options' not set.")); + return this; + } + + if (!options.home_id) { + this.emit("error", new Error("getNextEvents 'home_id' not set.")); + return this; + } + + if (!options.event_id) { + this.emit("error", new Error("getNextEvents 'event_id' not set.")); + return this; + } + + var url = util.format('%s/api/getnextevents', BASE_URL); + + var form = { + access_token: access_token, + home_id: options.home_id, + event_id: options.event_id, + }; + + if (options.size) { + form.size = options.size; + } + + request({ + url: url, + method: "POST", + form: form, + timeout: options && options.timeout || timeout + }, function (err, response, body) { + if (err || response.statusCode != 200) { + var error = this.handleRequestError(err, response, body, "getNextEvents error"); + if (callback) { + callback(error); + } + return error; + } + + body = JSON.parse(body); + + this.emit('get-nextevents', err, body.body); + + if (callback) { + return callback(err, body.body); + } + + return this; + + }.bind(this)); + + return this; +}; + +/** + * https://dev.netatmo.com/dev/resources/technical/reference/welcome/getlasteventof + * @param options + * @param callback + * @returns {*} + */ +netatmo.prototype.getLastEventOf = function (options, callback) { + // Wait until authenticated. + if (!access_token) { + return this.on('authenticated', function () { + this.getLastEventOf(options, callback); + }); + } + + if (!options) { + this.emit("error", new Error("getLastEventOf 'options' not set.")); + return this; + } + + if (!options.home_id) { + this.emit("error", new Error("getLastEventOf 'home_id' not set.")); + return this; + } + + if (!options.person_id) { + this.emit("error", new Error("getLastEventOf 'person_id' not set.")); + return this; + } + + var url = util.format('%s/api/getlasteventof', BASE_URL); + + var form = { + access_token: access_token, + home_id: options.home_id, + person_id: options.person_id, + }; + + if (options.offset) { + form.offset = options.offset; + } + + request({ + url: url, + method: "POST", + form: form, + timeout: options && options.timeout || timeout + }, function (err, response, body) { + if (err || response.statusCode != 200) { + var error = this.handleRequestError(err, response, body, "getLastEventOf error"); + if (callback) { + callback(error); + } + return error; + } + + body = JSON.parse(body); + + this.emit('get-lasteventof', err, body.body); + + if (callback) { + return callback(err, body.body); + } + + return this; + + }.bind(this)); + + return this; +}; + +/** + * https://dev.netatmo.com/dev/resources/technical/reference/welcome/geteventsuntil + * @param options + * @param callback + * @returns {*} + */ +netatmo.prototype.getEventsUntil = function (options, callback) { + // Wait until authenticated. + if (!access_token) { + return this.on('authenticated', function () { + this.getEventsUntil(options, callback); + }); + } + + if (!options) { + this.emit("error", new Error("getEventsUntil 'options' not set.")); + return this; + } + + if (!options.home_id) { + this.emit("error", new Error("getEventsUntil 'home_id' not set.")); + return this; + } + + if (!options.event_id) { + this.emit("error", new Error("getEventsUntil 'event_id' not set.")); + return this; + } + + var url = util.format('%s/api/geteventsuntil', BASE_URL); + + var form = { + access_token: access_token, + home_id: options.home_id, + event_id: options.event_id, + }; + + request({ + url: url, + method: "POST", + form: form, + timeout: options && options.timeout || timeout + }, function (err, response, body) { + if (err || response.statusCode != 200) { + var error = this.handleRequestError(err, response, body, "getEventsUntil error"); + if (callback) { + callback(error); + } + return error; + } + + body = JSON.parse(body); + + this.emit('get-eventsuntil', err, body.body); + + if (callback) { + return callback(err, body.body); + } + + return this; + + }.bind(this)); + + return this; +}; + +/** + * https://dev.netatmo.com/dev/resources/technical/reference/welcome/getcamerapicture + * @param options + * @param callback + * @returns {*} + */ +netatmo.prototype.getCameraPicture = function (options, callback) { + // Wait until authenticated. + if (!access_token) { + return this.on('authenticated', function () { + this.getCameraPicture(options, callback); + }); + } + + if (!options) { + this.emit("error", new Error("getCameraPicture 'options' not set.")); + return this; + } + + if (!options.image_id) { + this.emit("error", new Error("getCameraPicture 'image_id' not set.")); + return this; + } + + if (!options.key) { + this.emit("error", new Error("getCameraPicture 'key' not set.")); + return this; + } + + var url = util.format('%s/api/getcamerapicture', BASE_URL); + + var qs = { + access_token: access_token, + image_id: options.image_id, + key: options.key, + }; + + request({ + url: url, + method: "GET", + qs: qs, + encoding: null, + contentType: 'image/jpg', + timeout: options && options.timeout || timeout + }, function (err, response, body) { + if (err || response.statusCode != 200) { + var error = this.handleRequestError(err, response, body, "getCameraPicture error"); + if (callback) { + callback(error); + } + return error; + } + + this.emit('get-camerapicture', err, body); + + if (callback) { + return callback(err, body); + } + + return this; + + }.bind(this)); + + return this; +}; + +/** + * https://dev.netatmo.com/dev/resources/technical/reference/healthyhomecoach/gethomecoachsdata + * @param options + * @param callback + * @returns {*} + */ +netatmo.prototype.getHealthyHomeCoachData = function (options, callback) { + // Wait until authenticated. + if (!access_token) { + return this.on('authenticated', function () { + this.getHealthyHomeCoachData(options, callback); + }); + } + + if (options != null && callback == null) { + callback = options; + options = null; + } + + var url = util.format('%s/api/gethomecoachsdata?access_token=%s', BASE_URL, access_token); + if (options != null) { + url = util.format(url + '&device_id=%s', options.device_id); + } + + request({ + url: url, + method: "GET", + timeout: options && options.timeout || timeout + }, function (err, response, body) { + if (err || response.statusCode != 200) { + var error = this.handleRequestError(err, response, body, "getHealthyHomeCoachData error"); + if (callback) { + callback(error); + } + return error; + } + + body = JSON.parse(body); + + var devices = body.body.devices; + + this.emit('get-healthhomecoaches-data', err, devices); + + if (callback) { + return callback(err, devices); + } + + return this; + + }.bind(this)); + + return this; +}; + +module.exports = netatmo; From 6371f8b62efa780ec18794d61bcb9d4ed695bff2 Mon Sep 17 00:00:00 2001 From: Wolfgang Sommerer Date: Tue, 13 Jun 2017 20:11:54 +0200 Subject: [PATCH 5/5] methods now return error after invoking callback --- netatmo.js | 29 ++++++++++++++--------------- 1 file changed, 14 insertions(+), 15 deletions(-) diff --git a/netatmo.js b/netatmo.js index bf00413..d81f171 100644 --- a/netatmo.js +++ b/netatmo.js @@ -14,7 +14,7 @@ var access_token; var timeout; /** - * @constructor + * @constructor * @param args */ var netatmo = function (args) { @@ -119,7 +119,7 @@ netatmo.prototype.authenticate = function (args, callback) { if (callback) { callback(error); } - return; + return error; } body = JSON.parse(body); @@ -165,8 +165,7 @@ netatmo.prototype.authenticate_refresh = function (refresh_token) { timeout: timeout }, function (err, response, body) { if (err || response.statusCode != 200) { - var error = this.handleRequestError(err, response, body, "Authenticate refresh error"); - return; + return this.handleRequestError(err, response, body, "Authenticate refresh error"); } body = JSON.parse(body); @@ -228,7 +227,7 @@ netatmo.prototype.getStationsData = function (options, callback) { if (callback) { callback(error); } - return; + return error; } body = JSON.parse(body); @@ -282,7 +281,7 @@ netatmo.prototype.getThermostatsData = function (options, callback) { if (callback) { callback(error); } - return; + return error; } body = JSON.parse(body); @@ -404,7 +403,7 @@ netatmo.prototype.getMeasure = function (options, callback) { if (callback) { callback(error); } - return; + return error; } body = JSON.parse(body); @@ -484,7 +483,7 @@ netatmo.prototype.setSyncSchedule = function (options, callback) { if (callback) { callback(error); } - return; + return error; } body = JSON.parse(body); @@ -568,7 +567,7 @@ netatmo.prototype.setThermpoint = function (options, callback) { if (callback) { callback(error); } - return; + return error; } body = JSON.parse(body); @@ -631,7 +630,7 @@ netatmo.prototype.getHomeData = function (options, callback) { if (callback) { callback(error); } - return; + return error; } body = JSON.parse(body); @@ -701,7 +700,7 @@ netatmo.prototype.getNextEvents = function (options, callback) { if (callback) { callback(error); } - return; + return error; } body = JSON.parse(body); @@ -771,7 +770,7 @@ netatmo.prototype.getLastEventOf = function (options, callback) { if (callback) { callback(error); } - return; + return error; } body = JSON.parse(body); @@ -837,7 +836,7 @@ netatmo.prototype.getEventsUntil = function (options, callback) { if (callback) { callback(error); } - return; + return error; } body = JSON.parse(body); @@ -905,7 +904,7 @@ netatmo.prototype.getCameraPicture = function (options, callback) { if (callback) { callback(error); } - return; + return error; } this.emit('get-camerapicture', err, body); @@ -955,7 +954,7 @@ netatmo.prototype.getHealthyHomeCoachData = function (options, callback) { if (callback) { callback(error); } - return; + return error; } body = JSON.parse(body);