From ae3e616902d34220d6d3a61902a847ba6cddbed1 Mon Sep 17 00:00:00 2001 From: Michel Remacle Date: Wed, 12 Jun 2024 19:41:08 +0200 Subject: [PATCH 01/26] Save token to file --- smappee-api.js | 107 +++++++++++++++++++++++++++++++------------------ 1 file changed, 69 insertions(+), 38 deletions(-) diff --git a/smappee-api.js b/smappee-api.js index 6506925..c598e06 100644 --- a/smappee-api.js +++ b/smappee-api.js @@ -2,6 +2,7 @@ var http = require('http'); var request = require('request'); var querystring = require('querystring'); var moment = require('moment'); +const fs = require('node:fs'); function SmappeeAPI(settings) { @@ -10,7 +11,7 @@ function SmappeeAPI(settings) { var username = settings.username; var password = settings.password; - this.debug = settings.debug || false; + this.debug = settings.debug || true; var thisObject = this; @@ -34,7 +35,7 @@ function SmappeeAPI(settings) { * @param handler function that will be called when request is completed. */ this.getServiceLocations = function(handler) { - _get('https://app1pub.smappee.net/dev/v1/servicelocation', {}, handler); + _get('https://app1pub.smappee.net/dev/v3/servicelocation', {}, handler); }; /** @@ -46,7 +47,7 @@ function SmappeeAPI(settings) { * @param handler function that will be called when request is completed. */ this.getServiceLocationInfo = function(serviceLocationId, handler) { - var url = 'https://app1pub.smappee.net/dev/v1/servicelocation/' + serviceLocationId + '/info'; + var url = 'https://app1pub.smappee.net/dev/v3/servicelocation/' + serviceLocationId + '/info'; _get(url, {}, handler); }; @@ -62,7 +63,7 @@ function SmappeeAPI(settings) { * @param handler function that will be called when request is completed. */ this.getConsumptions = function(serviceLocationId, aggregation, from, to, handler) { - var url = 'https://app1pub.smappee.net/dev/v1/servicelocation/' + serviceLocationId + '/consumption'; + var url = 'https://app1pub.smappee.net/dev/v3/servicelocation/' + serviceLocationId + '/consumption'; var fields = { aggregation: aggregation, from: from, @@ -72,7 +73,7 @@ function SmappeeAPI(settings) { }; this.getLatestConsumption = function(serviceLocationId, handler) { - var url = 'https://app1pub.smappee.net/dev/v1/servicelocation/' + serviceLocationId + '/consumption'; + var url = 'https://app1pub.smappee.net/dev/v3/servicelocation/' + serviceLocationId + '/consumption'; var fields = { aggregation: this.AGGREGATION_TYPES.MINUTES, from: moment().subtract(30, 'minutes').utc().valueOf(), @@ -88,7 +89,7 @@ function SmappeeAPI(settings) { }; this.getMonthlyConsumptionsForLastYear = function(serviceLocationId, handler) { - var url = 'https://app1pub.smappee.net/dev/v1/servicelocation/' + serviceLocationId + '/consumption'; + var url = 'https://app1pub.smappee.net/dev/v3/servicelocation/' + serviceLocationId + '/consumption'; var fields = { aggregation: this.AGGREGATION_TYPES.MONTHLY, from: moment().subtract(1, 'year').utc().valueOf(), @@ -98,7 +99,7 @@ function SmappeeAPI(settings) { }; this.getEvents = function(serviceLocationId, applianceId, from, to, maxNumber, handler) { - var url = 'https://app1pub.smappee.net/dev/v1/servicelocation/' + serviceLocationId + '/events'; + var url = 'https://app1pub.smappee.net/dev/v3/servicelocation/' + serviceLocationId + '/events'; var fields = { applienceId: applianceId, from: from, @@ -109,13 +110,13 @@ function SmappeeAPI(settings) { }; this.turnActuatorOn = function(serviceLocationId, actuatorId, duration, handler) { - var url = 'https://app1pub.smappee.net/dev/v1/servicelocation/' + serviceLocationId + '/actuator/' + actuatorId + '/on'; + var url = 'https://app1pub.smappee.net/dev/v3/servicelocation/' + serviceLocationId + '/actuator/' + actuatorId + '/on'; _post(url, "{'duration': " + duration + "}", handler); }; this.turnActuatorOff = function(serviceLocationId, actuatorId, duration, handler) { - var url = 'https://app1pub.smappee.net/dev/v1/servicelocation/' + serviceLocationId + '/actuator/' + actuatorId + '/off'; + var url = 'https://app1pub.smappee.net/dev/v3/servicelocation/' + serviceLocationId + '/actuator/' + actuatorId + '/off'; _post(url, "{'duration': " + duration + "}", handler); }; @@ -124,37 +125,67 @@ function SmappeeAPI(settings) { var _getAccessToken = function(handler) { if (typeof accessToken == 'undefined') { - var body = { - client_id: clientId, - client_secret: clientSecret, - username: username, - password: password, - grant_type: 'password' - }; - - if (thisObject.debug) { - console.log("Making oAuth call..."); - } - - var options = { - url: 'https://app1pub.smappee.net/dev/v1/oauth2/token', - headers: { - 'Host': 'app1pub.smappee.net' - }, - form: body - }; - - request.post(options, function (err, httpResponse, body) { - if (err) { - return console.error('Request failed:', err); - } + + const tokenFile = fs.readFileSync('./token.json'); + if (tokenFile!=null){ if (thisObject.debug) { - console.log('Server responded with:', body); + console.log("Read token from file "); } - - accessToken = JSON.parse(body); - handler(accessToken); - }); + var existingToken = JSON.parse(tokenFile); + console.log(existingToken); + + //save time in seconds and compare + + if (existingToken.refresh_token.length>0){ + //token exist + if (thisObject.debug) { + console.log("SKIP oAuth call, token still valid."); + } + accessToken=existingToken; + handler(accessToken); + } else { + var body = { + client_id: clientId, + client_secret: clientSecret, + username: username, + password: password, + grant_type: 'password' + }; + + if (thisObject.debug) { + console.log("Making oAuth call..."); + } + + var options = { + url: 'https://app1pub.smappee.net/dev/v3/oauth2/token', + headers: { + 'Host': 'app1pub.smappee.net' + }, + form: body + }; + + request.post(options, function (err, httpResponse, body) { + if (err) { + return console.error('Request failed:', err); + } + if (thisObject.debug) { + console.log('Server responded with:', body); + } + + accessToken = JSON.parse(body); + + fs.writeFileSync('./token.json', body, err => { + if (err) { + console.error(err); + } else { + // file written successfully + } + }); + handler(accessToken); + }); + } + } + } else { handler(accessToken) } From 69ef35288bda91d4e112eaff029cc1b628430ca7 Mon Sep 17 00:00:00 2001 From: Michel Remacle Date: Wed, 12 Jun 2024 19:47:06 +0200 Subject: [PATCH 02/26] Update README.md --- README.md | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index a3dfbf0..b3edd21 100644 --- a/README.md +++ b/README.md @@ -1,3 +1,5 @@ +Please note : this fork is probably not working at this stage. I'm trying to edit it to get the refresh token working and add the EV Line charging session stuff. I did never code in Node JS so I'll see what I can do... + # Smappee-NodeJS Smappee nodejs project to read smappee data. @@ -73,4 +75,4 @@ var to = moment().utc().valueOf(); smappee.getConsumptions("0000", smappee.AGGREGATION_TYPES.MONTHLY, from, to, function(output) { console.log(output); }) -``` \ No newline at end of file +``` From 2d7896aaad05f7d0da954667a51e42e7b6d591c6 Mon Sep 17 00:00:00 2001 From: Michel Remacle Date: Thu, 13 Jun 2024 17:28:13 +0200 Subject: [PATCH 03/26] Store token and reuse it (Todo : send refresh token) --- smappee-api.js | 135 +++++++++++++++++++++++++++---------------------- 1 file changed, 74 insertions(+), 61 deletions(-) diff --git a/smappee-api.js b/smappee-api.js index c598e06..5116edd 100644 --- a/smappee-api.js +++ b/smappee-api.js @@ -122,72 +122,85 @@ function SmappeeAPI(settings) { // HELPER METHODS ++++++++++++++++++++++++++++++++++++++++ - + var _getAccessToken = function(handler) { - if (typeof accessToken == 'undefined') { - - const tokenFile = fs.readFileSync('./token.json'); - if (tokenFile!=null){ + var tokenFile = null; + var existingToken = null; + let timestampNow = Date.now(); + var timestampNowSeconds = timestampNow/1000; + + if (fs.existsSync('./token.json') && fs.existsSync('./tokenBirth.txt')) { + tokenFile=fs.readFileSync('./token.json'); + existingToken = JSON.parse(tokenFile); + if (thisObject.debug) { + console.log("existingToken : "+tokenFile); + } + var tokenBirth=fs.readFileSync('./tokenBirth.txt'); + var tokenDeath = Number(tokenBirth)+existingToken.expires_in-60; //token death with 60sec threshold + if (tokenDeath0){ - //token exist - if (thisObject.debug) { - console.log("SKIP oAuth call, token still valid."); - } - accessToken=existingToken; - handler(accessToken); - } else { - var body = { - client_id: clientId, - client_secret: clientSecret, - username: username, - password: password, - grant_type: 'password' - }; - - if (thisObject.debug) { - console.log("Making oAuth call..."); - } - - var options = { - url: 'https://app1pub.smappee.net/dev/v3/oauth2/token', - headers: { - 'Host': 'app1pub.smappee.net' - }, - form: body - }; - - request.post(options, function (err, httpResponse, body) { - if (err) { - return console.error('Request failed:', err); - } - if (thisObject.debug) { - console.log('Server responded with:', body); - } - - accessToken = JSON.parse(body); - - fs.writeFileSync('./token.json', body, err => { - if (err) { - console.error(err); - } else { - // file written successfully - } - }); - handler(accessToken); - }); + console.log("Token expired : "+tokenDeath); + console.log("Time is now : "+timestampNowSeconds); + //TODO: Request refresh token instead of new token + console.log("TODO : Must request refresh token instead of new token"); } + accessToken=null; + } else { + accessToken=existingToken; } - + } + + if (accessToken!=null){ + handler(accessToken); } else { - handler(accessToken) + var body = { + client_id: clientId, + client_secret: clientSecret, + username: username, + password: password, + grant_type: 'password' + }; + + if (thisObject.debug) { + console.log("Making oAuth call..."); + } + + var options = { + url: 'https://app1pub.smappee.net/dev/v3/oauth2/token', + headers: { + 'Host': 'app1pub.smappee.net' + }, + form: body + }; + + request.post(options, function (err, httpResponse, body) { + if (err) { + return console.error('Request failed:', err); + } + if (thisObject.debug) { + console.log('Server responded with:', body); + } + + accessToken = JSON.parse(body); + + fs.writeFileSync('./token.json', body, err => { + if (err) { + console.error(err); + } else { + // file written successfully + } + }); + + fs.writeFileSync('./tokenBirth.txt', timestampNowSeconds.toString(), err => { + if (err) { + console.error(err); + } else { + // file written successfully + } + }); + handler(accessToken); + }); } }; From 2f9b3543f2322168bbe1b85726768197bb3fef92 Mon Sep 17 00:00:00 2001 From: Michel Remacle Date: Sun, 16 Jun 2024 10:38:07 +0200 Subject: [PATCH 04/26] Update README.md --- README.md | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/README.md b/README.md index b3edd21..cabf622 100644 --- a/README.md +++ b/README.md @@ -1,4 +1,10 @@ Please note : this fork is probably not working at this stage. I'm trying to edit it to get the refresh token working and add the EV Line charging session stuff. I did never code in Node JS so I'll see what I can do... +Todo list : +- Store token in file +- Read token from file +- Request new token if token is expired +- Implement EV line API requests +- Send data to MQTT server # Smappee-NodeJS Smappee nodejs project to read smappee data. From 0e25fc2473e7f03e0c4772538617583217642630 Mon Sep 17 00:00:00 2001 From: Michel Remacle Date: Sun, 16 Jun 2024 11:05:48 +0200 Subject: [PATCH 05/26] Refresh token if current is expired --- smappee-api.js | 95 ++++++++++++++++++++++++++++++++++++++++---------- 1 file changed, 76 insertions(+), 19 deletions(-) diff --git a/smappee-api.js b/smappee-api.js index 5116edd..afa39bd 100644 --- a/smappee-api.js +++ b/smappee-api.js @@ -120,6 +120,26 @@ function SmappeeAPI(settings) { _post(url, "{'duration': " + duration + "}", handler); }; + this.getCurrentChargingSession = function(chargingStationSN, handler) { + var url = 'https://app1pub.smappee.net/dev/v3/chargingstations/' + chargingStationSN + '/sessions'; + + if (thisObject.debug) { + console.log("getCurrentChargingSession url : "+ url); + } + + var fields = { + active: true, + range:"1635721200000" + }; + _get(url, fields, function(output) { + if (output.length > 0) { + handler(output); + } else { + handler(undefined); + } + }); + }; + // HELPER METHODS ++++++++++++++++++++++++++++++++++++++++ @@ -129,31 +149,68 @@ function SmappeeAPI(settings) { let timestampNow = Date.now(); var timestampNowSeconds = timestampNow/1000; + //Try to read Access token from file if (fs.existsSync('./token.json') && fs.existsSync('./tokenBirth.txt')) { tokenFile=fs.readFileSync('./token.json'); existingToken = JSON.parse(tokenFile); if (thisObject.debug) { - console.log("existingToken : "+tokenFile); + console.log("Existing Token found : "+tokenFile); } var tokenBirth=fs.readFileSync('./tokenBirth.txt'); var tokenDeath = Number(tokenBirth)+existingToken.expires_in-60; //token death with 60sec threshold if (tokenDeath { + if (err) { + console.error('Could not save token to file', err); + } + }); + + fs.writeFileSync('./tokenBirth.txt', timestampNowSeconds.toString(), err => { + if (err) { + console.error('Could not save token birth to file', err); + } + }); + }); } else { + //Using existing Token from file accessToken=existingToken; } } - if (accessToken!=null){ - handler(accessToken); - } else { + if (accessToken==null){ + //Still got no Token, requesting a new one var body = { client_id: clientId, client_secret: clientSecret, @@ -186,22 +243,23 @@ function SmappeeAPI(settings) { fs.writeFileSync('./token.json', body, err => { if (err) { - console.error(err); - } else { - // file written successfully - } + console.error('Could not save token to file', err); + } }); fs.writeFileSync('./tokenBirth.txt', timestampNowSeconds.toString(), err => { if (err) { - console.error(err); - } else { - // file written successfully - } + console.error('Could not save token birth to file', err); + } }); - handler(accessToken); }); } + + if (accessToken!=null){ + handler(accessToken); + } else { + return console.error('Could not get valid token'); + } }; var _post = function(url, fields, handler) { @@ -224,7 +282,7 @@ function SmappeeAPI(settings) { return console.error('Request failed:', err); } if (thisObject.debug) { - console.log('Server responded with:', body); + //console.log('Server responded with:', body); } handler({status: 'OK'}); @@ -254,7 +312,6 @@ function SmappeeAPI(settings) { if (thisObject.debug) { console.log('Server responded with:', body); } - var output = JSON.parse(body); handler(output); }); //end of GET request From 5a59f0e3d8e7ca3467a7bc8de4b4573ba0a03608 Mon Sep 17 00:00:00 2001 From: Michel Remacle Date: Sun, 16 Jun 2024 11:07:16 +0200 Subject: [PATCH 06/26] Update README.md --- README.md | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/README.md b/README.md index cabf622..386e1de 100644 --- a/README.md +++ b/README.md @@ -1,9 +1,9 @@ Please note : this fork is probably not working at this stage. I'm trying to edit it to get the refresh token working and add the EV Line charging session stuff. I did never code in Node JS so I'll see what I can do... Todo list : -- Store token in file -- Read token from file -- Request new token if token is expired -- Implement EV line API requests +- Store token in file ✅ +- Read token from file ✅ +- Request new token if token is expired ✅ +- Implement EV line API requests ✅ (read current session only) - Send data to MQTT server # Smappee-NodeJS From a94f03331363ac3325ae1ce104f04d0830b9cfe6 Mon Sep 17 00:00:00 2001 From: Michel Remacle Date: Sun, 16 Jun 2024 20:39:45 +0200 Subject: [PATCH 07/26] Update README.md --- README.md | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index 386e1de..c29b60c 100644 --- a/README.md +++ b/README.md @@ -4,7 +4,7 @@ Todo list : - Read token from file ✅ - Request new token if token is expired ✅ - Implement EV line API requests ✅ (read current session only) -- Send data to MQTT server +- Send data to MQTT server : partial # Smappee-NodeJS Smappee nodejs project to read smappee data. @@ -28,7 +28,11 @@ var smappee = new SmappeeAPI({ clientSecret: "xxx", username: "xxx", - password: "xxx" + password: "xxx", + + mqtt_server: "xxx", + mqtt_port: "xxx", + mqtt_baseTopic : "smappee/" }); module.exports = smappee; From 174deb9217adaa3d176305e55b04162d7e85a07f Mon Sep 17 00:00:00 2001 From: Michel Remacle Date: Sun, 16 Jun 2024 20:48:49 +0200 Subject: [PATCH 08/26] Update mqtt methods --- package.json | 11 +++--- smappee-api.js | 91 +++++++++++++++++++++++++++++++++++++++++++++++--- 2 files changed, 93 insertions(+), 9 deletions(-) diff --git a/package.json b/package.json index 94db04d..5ee419c 100644 --- a/package.json +++ b/package.json @@ -1,18 +1,19 @@ { "name": "smappee-nodejs", - "version": "1.0.0", + "version": "2.0", "main": "./smappee-api.js", - "author": "CONNCTED", + "author": "hyzteric", "keywords": [ "smappee", - "energy monitoring" + "energy monitoring", + "mqtt" ], "repository": { "type": "git", - "url": "git://github.com/CONNCTED/Smappee-NodeJS.git" + "url": "git://github.com/hyzteric/Smappee-NodeJS.git" }, "bugs": { - "url": "http://github.com/CONNCTED/Smappee-NodeJS/issues" + "url": "http://github.com/hyzteric/Smappee-NodeJS/issues" }, "dependencies": { "moment": "^2.10.6", diff --git a/smappee-api.js b/smappee-api.js index afa39bd..f1d9bd5 100644 --- a/smappee-api.js +++ b/smappee-api.js @@ -3,6 +3,7 @@ var request = require('request'); var querystring = require('querystring'); var moment = require('moment'); const fs = require('node:fs'); +const mqtt = require('mqtt'); function SmappeeAPI(settings) { @@ -10,8 +11,11 @@ function SmappeeAPI(settings) { var clientSecret = settings.clientSecret; var username = settings.username; var password = settings.password; + var mqtt_server = settings.mqtt_server; + var mqtt_port = settings.mqtt_port; + var mqtt_baseTopic = settings.mqtt_baseTopic; - this.debug = settings.debug || true; + this.debug = settings.debug || false; var thisObject = this; @@ -69,20 +73,51 @@ function SmappeeAPI(settings) { from: from, to: to }; - _get(url, fields, handler); + //_get(url, fields, handler); + _get(url, fields, function(output) { + if (thisObject.debug) { + console.log("getConsumptions output : "+ JSON.stringify(output)); + } + + if (output!=null) { + if (thisObject.debug) { + console.log("publishing getConsumptions output to mqtt"); + } + _publishMQTT(mqtt_baseTopic+"consumptions",JSON.stringify(output)); + handler(output); + } else { + if (thisObject.debug) { + console.log("getConsumptions output null"); + } + _publishMQTT(mqtt_baseTopic+"consumptions","no consumptions"); + handler(undefined); + } + }); }; this.getLatestConsumption = function(serviceLocationId, handler) { var url = 'https://app1pub.smappee.net/dev/v3/servicelocation/' + serviceLocationId + '/consumption'; var fields = { aggregation: this.AGGREGATION_TYPES.MINUTES, - from: moment().subtract(30, 'minutes').utc().valueOf(), - to: moment().add(30, 'minutes').utc().valueOf() + from: moment().subtract(20, 'minutes').utc().valueOf(), + to: moment().add(5, 'minutes').utc().valueOf() }; _get(url, fields, function(output) { + if (thisObject.debug) { + console.log("getConsumptions output : "+ JSON.stringify(output)); + } + if (output.consumptions.length > 0) { + if (thisObject.debug) { + console.log("publishing getConsumptions output to mqtt"); + } + _publishMQTT(mqtt_baseTopic+"consumptions",JSON.stringify(output.consumptions[output.consumptions.length - 1])); handler(output.consumptions[output.consumptions.length - 1]); } else { + if (thisObject.debug) { + console.log("getConsumptions output null"); + } + _publishMQTT(mqtt_baseTopic+"consumptions","no consumptions"); handler(undefined); } }); @@ -133,8 +168,10 @@ function SmappeeAPI(settings) { }; _get(url, fields, function(output) { if (output.length > 0) { + _publishMQTT(mqtt_baseTopic+"currentChargingSession",output); handler(output); } else { + _publishMQTT(mqtt_baseTopic+"currentChargingSession","no session"); handler(undefined); } }); @@ -318,6 +355,52 @@ function SmappeeAPI(settings) { }); //end of access token request }; + var _publishMQTT = function(topic, value){ + + const options = { + protocol: 'mqtt', + host: mqtt_server, + port: mqtt_port + }; + + if (thisObject.debug) { + console.log('Connecting to mqtt'); + } + const client = mqtt.connect(options); + + + + client.on('offline', () => { + console.log('Client is offline'); + }); + + client.on('reconnect', () => { + console.log('Reconnecting to MQTT broker'); + }); + + client.on('end', () => { + console.log('Connection to MQTT broker ended'); + }); + + client.on('connect', () => { + console.log('Connected to MQTT broker'); + client.publish(topic, value, { retain: true }, (err) => { + if (thisObject.debug) { + console.log('Publishing to mqtt'); + } + if (err) { + console.error('Failed to publish message:', err); + } else { + if (thisObject.debug) { + console.log('Message published with retain flag set to true'); + } + } + }); + client.end(); + }); + + } + } module.exports = SmappeeAPI; \ No newline at end of file From 7d4036df4990a3455b53d328ed3c7cb007f1b50b Mon Sep 17 00:00:00 2001 From: Michel Remacle Date: Sun, 16 Jun 2024 20:53:36 +0200 Subject: [PATCH 09/26] Update README.md --- README.md | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index c29b60c..e2d5a76 100644 --- a/README.md +++ b/README.md @@ -38,7 +38,7 @@ var smappee = new SmappeeAPI({ module.exports = smappee; ``` -### In another nodejs file +### In another nodejs file : test.js ```javascript var smappee = require('./my-smappee'); @@ -46,6 +46,8 @@ smappee.getServiceLocations(function(output) { console.log(output); }) ``` +### To run your test.js file type : +> node test.js The following functions are available: `getServiceLocations(callback)`, `getServiceLocationInfo(serviceLocationId, callback)`, ... @@ -86,3 +88,4 @@ smappee.getConsumptions("0000", smappee.AGGREGATION_TYPES.MONTHLY, from, to, fun console.log(output); }) ``` +TIP: To convert the 5 minute interval from Energy [Wh] to Power [W], like the Smappee Dashboard reports these, you have to do these values times 12 as there are 12 x 5 minute intervals in an hour. From ea672c83ef3ecff87d7164dd3c6a4a0b4fc9e0ff Mon Sep 17 00:00:00 2001 From: Michel Remacle Date: Mon, 17 Jun 2024 21:41:36 +0200 Subject: [PATCH 10/26] Fix mqtt publish for charging session --- smappee-api.js | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/smappee-api.js b/smappee-api.js index f1d9bd5..c14f2fd 100644 --- a/smappee-api.js +++ b/smappee-api.js @@ -168,7 +168,8 @@ function SmappeeAPI(settings) { }; _get(url, fields, function(output) { if (output.length > 0) { - _publishMQTT(mqtt_baseTopic+"currentChargingSession",output); + _publishMQTT(mqtt_baseTopic+"currentChargingSession",JSON.stringify(output)); + //_publishMQTT(mqtt_baseTopic+"currentChargingSession",output); handler(output); } else { _publishMQTT(mqtt_baseTopic+"currentChargingSession","no session"); From 50db9e2767028822ef975cc86775d14f33c53315 Mon Sep 17 00:00:00 2001 From: Michel Remacle Date: Thu, 20 Jun 2024 11:12:01 +0200 Subject: [PATCH 11/26] chargingstations session api response contains extra [ ] that needed removal --- smappee-api.js | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/smappee-api.js b/smappee-api.js index c14f2fd..f641184 100644 --- a/smappee-api.js +++ b/smappee-api.js @@ -168,9 +168,12 @@ function SmappeeAPI(settings) { }; _get(url, fields, function(output) { if (output.length > 0) { - _publishMQTT(mqtt_baseTopic+"currentChargingSession",JSON.stringify(output)); - //_publishMQTT(mqtt_baseTopic+"currentChargingSession",output); - handler(output); + var apiResponse = JSON.stringify(output); + if (apiResponse.startsWith("[")){ + apiResponse = apiResponse.substring(1, apiResponse.length-1);//remove invalid [ ] around response + } + _publishMQTT(mqtt_baseTopic+"currentChargingSession",apiResponse); + handler(apiResponse); } else { _publishMQTT(mqtt_baseTopic+"currentChargingSession","no session"); handler(undefined); From 4fd97783dc02d83e3a50b4fa427c077702c34a85 Mon Sep 17 00:00:00 2001 From: Michel Remacle Date: Thu, 20 Jun 2024 14:57:12 +0200 Subject: [PATCH 12/26] add timestamp to charging sessions manually --- smappee-api.js | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/smappee-api.js b/smappee-api.js index f641184..b95ad03 100644 --- a/smappee-api.js +++ b/smappee-api.js @@ -172,6 +172,11 @@ function SmappeeAPI(settings) { if (apiResponse.startsWith("[")){ apiResponse = apiResponse.substring(1, apiResponse.length-1);//remove invalid [ ] around response } + let timestampNow = Date.now(); + var timestampNowSeconds = timestampNow/1000; + apiResponse=apiResponse.substring(0, apiResponse.length-1); + apiResponse+=",\"timestamp\":"+timestampNowSeconds.toString()+"}"; + console.log("apiResponse with timestamp : "+ apiResponse); _publishMQTT(mqtt_baseTopic+"currentChargingSession",apiResponse); handler(apiResponse); } else { From e7752262902f7f8642769eec7e43e2e21b467cef Mon Sep 17 00:00:00 2001 From: Michel Remacle Date: Thu, 20 Jun 2024 19:36:13 +0200 Subject: [PATCH 13/26] Add negative session ID when there is no charging session --- smappee-api.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/smappee-api.js b/smappee-api.js index b95ad03..66858a9 100644 --- a/smappee-api.js +++ b/smappee-api.js @@ -180,7 +180,7 @@ function SmappeeAPI(settings) { _publishMQTT(mqtt_baseTopic+"currentChargingSession",apiResponse); handler(apiResponse); } else { - _publishMQTT(mqtt_baseTopic+"currentChargingSession","no session"); + _publishMQTT(mqtt_baseTopic+"currentChargingSession","{\"id\":-1}"); handler(undefined); } }); From e5f1a6989ac449198ca615f4eb5b8e079f23e009 Mon Sep 17 00:00:00 2001 From: Michel Remacle Date: Wed, 3 Jul 2024 15:53:22 +0200 Subject: [PATCH 14/26] Update README.md --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index e2d5a76..d9aa636 100644 --- a/README.md +++ b/README.md @@ -7,7 +7,7 @@ Todo list : - Send data to MQTT server : partial # Smappee-NodeJS -Smappee nodejs project to read smappee data. +This nodejs project allows to read data from the smappee API and send it to an MQTT server. Tested with Mosquitto MQTT server and openHab to import Electric Car (EV) charging session status and house consumption. Based on https://support.smappee.com/hc/en-us/articles/202153935-Where-can-I-find-the-API-documentation- From 99830726c4e304effebcb09f005b4f26ed41eabf Mon Sep 17 00:00:00 2001 From: Michel Remacle Date: Wed, 10 Jul 2024 09:23:41 +0200 Subject: [PATCH 15/26] less log --- smappee-api.js | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/smappee-api.js b/smappee-api.js index 66858a9..1c1ce4f 100644 --- a/smappee-api.js +++ b/smappee-api.js @@ -232,7 +232,7 @@ function SmappeeAPI(settings) { return console.error('Request failed:', err); } if (thisObject.debug) { - console.log('Server responded with:', body); + //console.log('Server responded with:', body); } accessToken = JSON.parse(body); @@ -282,7 +282,7 @@ function SmappeeAPI(settings) { return console.error('Request failed:', err); } if (thisObject.debug) { - console.log('Server responded with:', body); + //console.log('Server responded with:', body); } accessToken = JSON.parse(body); @@ -356,7 +356,7 @@ function SmappeeAPI(settings) { return console.error('Request failed:', err); } if (thisObject.debug) { - console.log('Server responded with:', body); + //console.log('Server responded with:', body); } var output = JSON.parse(body); handler(output); From 651665b56199b0c583bf0e2e6890975ba721ab3e Mon Sep 17 00:00:00 2001 From: Michel Remacle Date: Wed, 10 Jul 2024 10:18:22 +0200 Subject: [PATCH 16/26] Update README.md --- README.md | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index d9aa636..666f7ca 100644 --- a/README.md +++ b/README.md @@ -6,14 +6,15 @@ Todo list : - Implement EV line API requests ✅ (read current session only) - Send data to MQTT server : partial -# Smappee-NodeJS +# Smappee2MQTT (with NodeJS) This nodejs project allows to read data from the smappee API and send it to an MQTT server. Tested with Mosquitto MQTT server and openHab to import Electric Car (EV) charging session status and house consumption. Based on https://support.smappee.com/hc/en-us/articles/202153935-Where-can-I-find-the-API-documentation- ## Installation ```bash -npm install smappee-nodejs --save +#this below actually installs the original forked project, I don't know yet how to publish mine +#npm install smappee-nodejs --save ``` ## Usage From 37743fa6b62dded2e7118b10679eb57007de5830 Mon Sep 17 00:00:00 2001 From: Michel Remacle Date: Wed, 10 Jul 2024 11:48:05 +0200 Subject: [PATCH 17/26] Update README.md --- README.md | 1 + 1 file changed, 1 insertion(+) diff --git a/README.md b/README.md index 666f7ca..cc43a47 100644 --- a/README.md +++ b/README.md @@ -5,6 +5,7 @@ Todo list : - Request new token if token is expired ✅ - Implement EV line API requests ✅ (read current session only) - Send data to MQTT server : partial +- Remove deprecated dependency : https://github.com/request/request/issues/3142 # Smappee2MQTT (with NodeJS) This nodejs project allows to read data from the smappee API and send it to an MQTT server. Tested with Mosquitto MQTT server and openHab to import Electric Car (EV) charging session status and house consumption. From cf7be7e748c7ace9d8d3e2db169d158a5cd02584 Mon Sep 17 00:00:00 2001 From: Michel Remacle Date: Wed, 10 Jul 2024 12:14:00 +0200 Subject: [PATCH 18/26] update package info with new git name --- package.json | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/package.json b/package.json index 5ee419c..4805670 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,7 @@ { - "name": "smappee-nodejs", + "name": "smappee2mqtt", "version": "2.0", + "description": "Connect to the Smappee API and push the data into your MQTT server", "main": "./smappee-api.js", "author": "hyzteric", "keywords": [ @@ -8,12 +9,14 @@ "energy monitoring", "mqtt" ], + "license": "OSL-3.0", + "homepage": "https://github.com/hyzteric/Smappee2MQTT/", "repository": { "type": "git", - "url": "git://github.com/hyzteric/Smappee-NodeJS.git" + "url": "git://github.com/hyzteric/Smappee2MQTT.git" }, "bugs": { - "url": "http://github.com/hyzteric/Smappee-NodeJS/issues" + "url": "https://github.com/hyzteric/Smappee2MQTT/issues" }, "dependencies": { "moment": "^2.10.6", From ea4361cfd3ff8d8cb27716410791483b8c10d600 Mon Sep 17 00:00:00 2001 From: Michel Remacle Date: Wed, 10 Jul 2024 12:25:43 +0200 Subject: [PATCH 19/26] update version number --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 4805670..bed243d 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "smappee2mqtt", - "version": "2.0", + "version": "1.0.0", "description": "Connect to the Smappee API and push the data into your MQTT server", "main": "./smappee-api.js", "author": "hyzteric", From e3f93898d26a93779de4020d061456e8c14b9274 Mon Sep 17 00:00:00 2001 From: Michel Remacle Date: Wed, 10 Jul 2024 14:16:10 +0200 Subject: [PATCH 20/26] add mqtt trace --- example/my-smappee.js | 8 +- package-lock.json | 500 ++++++++++++++++++++++++++++++++++++++++++ smappee-api.js | 14 +- 3 files changed, 519 insertions(+), 3 deletions(-) create mode 100644 package-lock.json diff --git a/example/my-smappee.js b/example/my-smappee.js index 76955b3..c4376d1 100644 --- a/example/my-smappee.js +++ b/example/my-smappee.js @@ -1,4 +1,4 @@ -var SmappeeAPI = require('smappee-nodejs'); +var SmappeeAPI = require('smappee2mqtt'); var smappee = new SmappeeAPI({ debug: true, @@ -7,7 +7,11 @@ var smappee = new SmappeeAPI({ clientSecret: "xxx", username: "xxx", - password: "xxx" + password: "xxx", + + mqtt_server: "192.168.0.1", + mqtt_port: "1883", + mqtt_baseTopic : "smappee/" }); module.exports = smappee; \ No newline at end of file diff --git a/package-lock.json b/package-lock.json new file mode 100644 index 0000000..b2fa2cb --- /dev/null +++ b/package-lock.json @@ -0,0 +1,500 @@ +{ + "name": "smappee2mqtt", + "version": "2.0", + "lockfileVersion": 3, + "requires": true, + "packages": { + "": { + "name": "smappee2mqtt", + "version": "2.0", + "license": "OSL-3.0", + "dependencies": { + "moment": "^2.10.6", + "request": "^2.67.0" + } + }, + "node_modules/ajv": { + "version": "6.12.6", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.6.tgz", + "integrity": "sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==", + "license": "MIT", + "dependencies": { + "fast-deep-equal": "^3.1.1", + "fast-json-stable-stringify": "^2.0.0", + "json-schema-traverse": "^0.4.1", + "uri-js": "^4.2.2" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/epoberezkin" + } + }, + "node_modules/asn1": { + "version": "0.2.6", + "resolved": "https://registry.npmjs.org/asn1/-/asn1-0.2.6.tgz", + "integrity": "sha512-ix/FxPn0MDjeyJ7i/yoHGFt/EX6LyNbxSEhPPXODPL+KB0VPk86UYfL0lMdy+KCnv+fmvIzySwaK5COwqVbWTQ==", + "license": "MIT", + "dependencies": { + "safer-buffer": "~2.1.0" + } + }, + "node_modules/assert-plus": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/assert-plus/-/assert-plus-1.0.0.tgz", + "integrity": "sha512-NfJ4UzBCcQGLDlQq7nHxH+tv3kyZ0hHQqF5BO6J7tNJeP5do1llPr8dZ8zHonfhAu0PHAdMkSo+8o0wxg9lZWw==", + "license": "MIT", + "engines": { + "node": ">=0.8" + } + }, + "node_modules/asynckit": { + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz", + "integrity": "sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q==", + "license": "MIT" + }, + "node_modules/aws-sign2": { + "version": "0.7.0", + "resolved": "https://registry.npmjs.org/aws-sign2/-/aws-sign2-0.7.0.tgz", + "integrity": "sha512-08kcGqnYf/YmjoRhfxyu+CLxBjUtHLXLXX/vUfx9l2LYzG3c1m61nrpyFUZI6zeS+Li/wWMMidD9KgrqtGq3mA==", + "license": "Apache-2.0", + "engines": { + "node": "*" + } + }, + "node_modules/aws4": { + "version": "1.13.0", + "resolved": "https://registry.npmjs.org/aws4/-/aws4-1.13.0.tgz", + "integrity": "sha512-3AungXC4I8kKsS9PuS4JH2nc+0bVY/mjgrephHTIi8fpEeGsTHBUJeosp0Wc1myYMElmD0B3Oc4XL/HVJ4PV2g==", + "license": "MIT" + }, + "node_modules/bcrypt-pbkdf": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/bcrypt-pbkdf/-/bcrypt-pbkdf-1.0.2.tgz", + "integrity": "sha512-qeFIXtP4MSoi6NLqO12WfqARWWuCKi2Rn/9hJLEmtB5yTNr9DqFWkJRCf2qShWzPeAMRnOgCrq0sg/KLv5ES9w==", + "license": "BSD-3-Clause", + "dependencies": { + "tweetnacl": "^0.14.3" + } + }, + "node_modules/caseless": { + "version": "0.12.0", + "resolved": "https://registry.npmjs.org/caseless/-/caseless-0.12.0.tgz", + "integrity": "sha512-4tYFyifaFfGacoiObjJegolkwSU4xQNGbVgUiNYVUxbQ2x2lUsFvY4hVgVzGiIe6WLOPqycWXA40l+PWsxthUw==", + "license": "Apache-2.0" + }, + "node_modules/combined-stream": { + "version": "1.0.8", + "resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.8.tgz", + "integrity": "sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==", + "license": "MIT", + "dependencies": { + "delayed-stream": "~1.0.0" + }, + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/core-util-is": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.2.tgz", + "integrity": "sha512-3lqz5YjWTYnW6dlDa5TLaTCcShfar1e40rmcJVwCBJC6mWlFuj0eCHIElmG1g5kyuJ/GD+8Wn4FFCcz4gJPfaQ==", + "license": "MIT" + }, + "node_modules/dashdash": { + "version": "1.14.1", + "resolved": "https://registry.npmjs.org/dashdash/-/dashdash-1.14.1.tgz", + "integrity": "sha512-jRFi8UDGo6j+odZiEpjazZaWqEal3w/basFjQHQEwVtZJGDpxbH1MeYluwCS8Xq5wmLJooDlMgvVarmWfGM44g==", + "license": "MIT", + "dependencies": { + "assert-plus": "^1.0.0" + }, + "engines": { + "node": ">=0.10" + } + }, + "node_modules/delayed-stream": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz", + "integrity": "sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ==", + "license": "MIT", + "engines": { + "node": ">=0.4.0" + } + }, + "node_modules/ecc-jsbn": { + "version": "0.1.2", + "resolved": "https://registry.npmjs.org/ecc-jsbn/-/ecc-jsbn-0.1.2.tgz", + "integrity": "sha512-eh9O+hwRHNbG4BLTjEl3nw044CkGm5X6LoaCf7LPp7UU8Qrt47JYNi6nPX8xjW97TKGKm1ouctg0QSpZe9qrnw==", + "license": "MIT", + "dependencies": { + "jsbn": "~0.1.0", + "safer-buffer": "^2.1.0" + } + }, + "node_modules/extend": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/extend/-/extend-3.0.2.tgz", + "integrity": "sha512-fjquC59cD7CyW6urNXK0FBufkZcoiGG80wTuPujX590cB5Ttln20E2UB4S/WARVqhXffZl2LNgS+gQdPIIim/g==", + "license": "MIT" + }, + "node_modules/extsprintf": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/extsprintf/-/extsprintf-1.3.0.tgz", + "integrity": "sha512-11Ndz7Nv+mvAC1j0ktTa7fAb0vLyGGX+rMHNBYQviQDGU0Hw7lhctJANqbPhu9nV9/izT/IntTgZ7Im/9LJs9g==", + "engines": [ + "node >=0.6.0" + ], + "license": "MIT" + }, + "node_modules/fast-deep-equal": { + "version": "3.1.3", + "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz", + "integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==", + "license": "MIT" + }, + "node_modules/fast-json-stable-stringify": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/fast-json-stable-stringify/-/fast-json-stable-stringify-2.1.0.tgz", + "integrity": "sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw==", + "license": "MIT" + }, + "node_modules/forever-agent": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/forever-agent/-/forever-agent-0.6.1.tgz", + "integrity": "sha512-j0KLYPhm6zeac4lz3oJ3o65qvgQCcPubiyotZrXqEaG4hNagNYO8qdlUrX5vwqv9ohqeT/Z3j6+yW067yWWdUw==", + "license": "Apache-2.0", + "engines": { + "node": "*" + } + }, + "node_modules/form-data": { + "version": "2.3.3", + "resolved": "https://registry.npmjs.org/form-data/-/form-data-2.3.3.tgz", + "integrity": "sha512-1lLKB2Mu3aGP1Q/2eCOx0fNbRMe7XdwktwOruhfqqd0rIJWwN4Dh+E3hrPSlDCXnSR7UtZ1N38rVXm+6+MEhJQ==", + "license": "MIT", + "dependencies": { + "asynckit": "^0.4.0", + "combined-stream": "^1.0.6", + "mime-types": "^2.1.12" + }, + "engines": { + "node": ">= 0.12" + } + }, + "node_modules/getpass": { + "version": "0.1.7", + "resolved": "https://registry.npmjs.org/getpass/-/getpass-0.1.7.tgz", + "integrity": "sha512-0fzj9JxOLfJ+XGLhR8ze3unN0KZCgZwiSSDz168VERjK8Wl8kVSdcu2kspd4s4wtAa1y/qrVRiAA0WclVsu0ng==", + "license": "MIT", + "dependencies": { + "assert-plus": "^1.0.0" + } + }, + "node_modules/har-schema": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/har-schema/-/har-schema-2.0.0.tgz", + "integrity": "sha512-Oqluz6zhGX8cyRaTQlFMPw80bSJVG2x/cFb8ZPhUILGgHka9SsokCCOQgpveePerqidZOrT14ipqfJb7ILcW5Q==", + "license": "ISC", + "engines": { + "node": ">=4" + } + }, + "node_modules/har-validator": { + "version": "5.1.5", + "resolved": "https://registry.npmjs.org/har-validator/-/har-validator-5.1.5.tgz", + "integrity": "sha512-nmT2T0lljbxdQZfspsno9hgrG3Uir6Ks5afism62poxqBM6sDnMEuPmzTq8XN0OEwqKLLdh1jQI3qyE66Nzb3w==", + "deprecated": "this library is no longer supported", + "license": "MIT", + "dependencies": { + "ajv": "^6.12.3", + "har-schema": "^2.0.0" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/http-signature": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/http-signature/-/http-signature-1.2.0.tgz", + "integrity": "sha512-CAbnr6Rz4CYQkLYUtSNXxQPUH2gK8f3iWexVlsnMeD+GjlsQ0Xsy1cOX+mN3dtxYomRy21CiOzU8Uhw6OwncEQ==", + "license": "MIT", + "dependencies": { + "assert-plus": "^1.0.0", + "jsprim": "^1.2.2", + "sshpk": "^1.7.0" + }, + "engines": { + "node": ">=0.8", + "npm": ">=1.3.7" + } + }, + "node_modules/is-typedarray": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-typedarray/-/is-typedarray-1.0.0.tgz", + "integrity": "sha512-cyA56iCMHAh5CdzjJIa4aohJyeO1YbwLi3Jc35MmRU6poroFjIGZzUzupGiRPOjgHg9TLu43xbpwXk523fMxKA==", + "license": "MIT" + }, + "node_modules/isstream": { + "version": "0.1.2", + "resolved": "https://registry.npmjs.org/isstream/-/isstream-0.1.2.tgz", + "integrity": "sha512-Yljz7ffyPbrLpLngrMtZ7NduUgVvi6wG9RJ9IUcyCd59YQ911PBJphODUcbOVbqYfxe1wuYf/LJ8PauMRwsM/g==", + "license": "MIT" + }, + "node_modules/jsbn": { + "version": "0.1.1", + "resolved": "https://registry.npmjs.org/jsbn/-/jsbn-0.1.1.tgz", + "integrity": "sha512-UVU9dibq2JcFWxQPA6KCqj5O42VOmAY3zQUfEKxU0KpTGXwNoCjkX1e13eHNvw/xPynt6pU0rZ1htjWTNTSXsg==", + "license": "MIT" + }, + "node_modules/json-schema": { + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/json-schema/-/json-schema-0.4.0.tgz", + "integrity": "sha512-es94M3nTIfsEPisRafak+HDLfHXnKBhV3vU5eqPcS3flIWqcxJWgXHXiey3YrpaNsanY5ei1VoYEbOzijuq9BA==", + "license": "(AFL-2.1 OR BSD-3-Clause)" + }, + "node_modules/json-schema-traverse": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz", + "integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==", + "license": "MIT" + }, + "node_modules/json-stringify-safe": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/json-stringify-safe/-/json-stringify-safe-5.0.1.tgz", + "integrity": "sha512-ZClg6AaYvamvYEE82d3Iyd3vSSIjQ+odgjaTzRuO3s7toCdFKczob2i0zCh7JE8kWn17yvAWhUVxvqGwUalsRA==", + "license": "ISC" + }, + "node_modules/jsprim": { + "version": "1.4.2", + "resolved": "https://registry.npmjs.org/jsprim/-/jsprim-1.4.2.tgz", + "integrity": "sha512-P2bSOMAc/ciLz6DzgjVlGJP9+BrJWu5UDGK70C2iweC5QBIeFf0ZXRvGjEj2uYgrY2MkAAhsSWHDWlFtEroZWw==", + "license": "MIT", + "dependencies": { + "assert-plus": "1.0.0", + "extsprintf": "1.3.0", + "json-schema": "0.4.0", + "verror": "1.10.0" + }, + "engines": { + "node": ">=0.6.0" + } + }, + "node_modules/mime-db": { + "version": "1.52.0", + "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.52.0.tgz", + "integrity": "sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==", + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/mime-types": { + "version": "2.1.35", + "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.35.tgz", + "integrity": "sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==", + "license": "MIT", + "dependencies": { + "mime-db": "1.52.0" + }, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/moment": { + "version": "2.30.1", + "resolved": "https://registry.npmjs.org/moment/-/moment-2.30.1.tgz", + "integrity": "sha512-uEmtNhbDOrWPFS+hdjFCBfy9f2YoyzRpwcl+DqpC6taX21FzsTLQVbMV/W7PzNSX6x/bhC1zA3c2UQ5NzH6how==", + "license": "MIT", + "engines": { + "node": "*" + } + }, + "node_modules/oauth-sign": { + "version": "0.9.0", + "resolved": "https://registry.npmjs.org/oauth-sign/-/oauth-sign-0.9.0.tgz", + "integrity": "sha512-fexhUFFPTGV8ybAtSIGbV6gOkSv8UtRbDBnAyLQw4QPKkgNlsH2ByPGtMUqdWkos6YCRmAqViwgZrJc/mRDzZQ==", + "license": "Apache-2.0", + "engines": { + "node": "*" + } + }, + "node_modules/performance-now": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/performance-now/-/performance-now-2.1.0.tgz", + "integrity": "sha512-7EAHlyLHI56VEIdK57uwHdHKIaAGbnXPiw0yWbarQZOKaKpvUIgW0jWRVLiatnM+XXlSwsanIBH/hzGMJulMow==", + "license": "MIT" + }, + "node_modules/psl": { + "version": "1.9.0", + "resolved": "https://registry.npmjs.org/psl/-/psl-1.9.0.tgz", + "integrity": "sha512-E/ZsdU4HLs/68gYzgGTkMicWTLPdAftJLfJFlLUAAKZGkStNU72sZjT66SnMDVOfOWY/YAoiD7Jxa9iHvngcag==", + "license": "MIT" + }, + "node_modules/punycode": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.3.1.tgz", + "integrity": "sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg==", + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/qs": { + "version": "6.5.3", + "resolved": "https://registry.npmjs.org/qs/-/qs-6.5.3.tgz", + "integrity": "sha512-qxXIEh4pCGfHICj1mAJQ2/2XVZkjCDTcEgfoSQxc/fYivUZxTkk7L3bDBJSoNrEzXI17oUO5Dp07ktqE5KzczA==", + "license": "BSD-3-Clause", + "engines": { + "node": ">=0.6" + } + }, + "node_modules/request": { + "version": "2.88.2", + "resolved": "https://registry.npmjs.org/request/-/request-2.88.2.tgz", + "integrity": "sha512-MsvtOrfG9ZcrOwAW+Qi+F6HbD0CWXEh9ou77uOb7FM2WPhwT7smM833PzanhJLsgXjN89Ir6V2PczXNnMpwKhw==", + "deprecated": "request has been deprecated, see https://github.com/request/request/issues/3142", + "license": "Apache-2.0", + "dependencies": { + "aws-sign2": "~0.7.0", + "aws4": "^1.8.0", + "caseless": "~0.12.0", + "combined-stream": "~1.0.6", + "extend": "~3.0.2", + "forever-agent": "~0.6.1", + "form-data": "~2.3.2", + "har-validator": "~5.1.3", + "http-signature": "~1.2.0", + "is-typedarray": "~1.0.0", + "isstream": "~0.1.2", + "json-stringify-safe": "~5.0.1", + "mime-types": "~2.1.19", + "oauth-sign": "~0.9.0", + "performance-now": "^2.1.0", + "qs": "~6.5.2", + "safe-buffer": "^5.1.2", + "tough-cookie": "~2.5.0", + "tunnel-agent": "^0.6.0", + "uuid": "^3.3.2" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/safe-buffer": { + "version": "5.2.1", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz", + "integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "license": "MIT" + }, + "node_modules/safer-buffer": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz", + "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==", + "license": "MIT" + }, + "node_modules/sshpk": { + "version": "1.18.0", + "resolved": "https://registry.npmjs.org/sshpk/-/sshpk-1.18.0.tgz", + "integrity": "sha512-2p2KJZTSqQ/I3+HX42EpYOa2l3f8Erv8MWKsy2I9uf4wA7yFIkXRffYdsx86y6z4vHtV8u7g+pPlr8/4ouAxsQ==", + "license": "MIT", + "dependencies": { + "asn1": "~0.2.3", + "assert-plus": "^1.0.0", + "bcrypt-pbkdf": "^1.0.0", + "dashdash": "^1.12.0", + "ecc-jsbn": "~0.1.1", + "getpass": "^0.1.1", + "jsbn": "~0.1.0", + "safer-buffer": "^2.0.2", + "tweetnacl": "~0.14.0" + }, + "bin": { + "sshpk-conv": "bin/sshpk-conv", + "sshpk-sign": "bin/sshpk-sign", + "sshpk-verify": "bin/sshpk-verify" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/tough-cookie": { + "version": "2.5.0", + "resolved": "https://registry.npmjs.org/tough-cookie/-/tough-cookie-2.5.0.tgz", + "integrity": "sha512-nlLsUzgm1kfLXSXfRZMc1KLAugd4hqJHDTvc2hDIwS3mZAfMEuMbc03SujMF+GEcpaX/qboeycw6iO8JwVv2+g==", + "license": "BSD-3-Clause", + "dependencies": { + "psl": "^1.1.28", + "punycode": "^2.1.1" + }, + "engines": { + "node": ">=0.8" + } + }, + "node_modules/tunnel-agent": { + "version": "0.6.0", + "resolved": "https://registry.npmjs.org/tunnel-agent/-/tunnel-agent-0.6.0.tgz", + "integrity": "sha512-McnNiV1l8RYeY8tBgEpuodCC1mLUdbSN+CYBL7kJsJNInOP8UjDDEwdk6Mw60vdLLrr5NHKZhMAOSrR2NZuQ+w==", + "license": "Apache-2.0", + "dependencies": { + "safe-buffer": "^5.0.1" + }, + "engines": { + "node": "*" + } + }, + "node_modules/tweetnacl": { + "version": "0.14.5", + "resolved": "https://registry.npmjs.org/tweetnacl/-/tweetnacl-0.14.5.tgz", + "integrity": "sha512-KXXFFdAbFXY4geFIwoyNK+f5Z1b7swfXABfL7HXCmoIWMKU3dmS26672A4EeQtDzLKy7SXmfBu51JolvEKwtGA==", + "license": "Unlicense" + }, + "node_modules/uri-js": { + "version": "4.4.1", + "resolved": "https://registry.npmjs.org/uri-js/-/uri-js-4.4.1.tgz", + "integrity": "sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg==", + "license": "BSD-2-Clause", + "dependencies": { + "punycode": "^2.1.0" + } + }, + "node_modules/uuid": { + "version": "3.4.0", + "resolved": "https://registry.npmjs.org/uuid/-/uuid-3.4.0.tgz", + "integrity": "sha512-HjSDRw6gZE5JMggctHBcjVak08+KEVhSIiDzFnT9S9aegmp85S/bReBVTb4QTFaRNptJ9kuYaNhnbNEOkbKb/A==", + "deprecated": "Please upgrade to version 7 or higher. Older versions may use Math.random() in certain circumstances, which is known to be problematic. See https://v8.dev/blog/math-random for details.", + "license": "MIT", + "bin": { + "uuid": "bin/uuid" + } + }, + "node_modules/verror": { + "version": "1.10.0", + "resolved": "https://registry.npmjs.org/verror/-/verror-1.10.0.tgz", + "integrity": "sha512-ZZKSmDAEFOijERBLkmYfJ+vmk3w+7hOLYDNkRCuRuMJGEmqYNCNLyBBFwWKVMhfwaEF3WOd0Zlw86U/WC/+nYw==", + "engines": [ + "node >=0.6.0" + ], + "license": "MIT", + "dependencies": { + "assert-plus": "^1.0.0", + "core-util-is": "1.0.2", + "extsprintf": "^1.2.0" + } + } + } +} diff --git a/smappee-api.js b/smappee-api.js index 1c1ce4f..53f818d 100644 --- a/smappee-api.js +++ b/smappee-api.js @@ -387,6 +387,18 @@ function SmappeeAPI(settings) { console.log('Reconnecting to MQTT broker'); }); + client.on('close', () => { + console.log('Clien MQTT received closed event'); + }); + + client.on('disconnect', () => { + console.log('Clien MQTT received closed event'); + }); + + client.on('error', (err) => { + console.log('MQTT error: ',err); + }); + client.on('end', () => { console.log('Connection to MQTT broker ended'); }); @@ -398,7 +410,7 @@ function SmappeeAPI(settings) { console.log('Publishing to mqtt'); } if (err) { - console.error('Failed to publish message:', err); + console.error('Failed to publish message: ', err); } else { if (thisObject.debug) { console.log('Message published with retain flag set to true'); From 284801f14a71153490f7992c30a01eda283ead45 Mon Sep 17 00:00:00 2001 From: Michel Remacle Date: Wed, 10 Jul 2024 14:17:03 +0200 Subject: [PATCH 21/26] add version --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index bed243d..a0a4aa8 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "smappee2mqtt", - "version": "1.0.0", + "version": "1.0.1", "description": "Connect to the Smappee API and push the data into your MQTT server", "main": "./smappee-api.js", "author": "hyzteric", From d371d08bbdf30cd81164de7db32cd1ac88dbb2a5 Mon Sep 17 00:00:00 2001 From: Michel Remacle Date: Tue, 16 Jul 2024 20:10:13 +0200 Subject: [PATCH 22/26] Async fix --- README.md | 28 +++++- package.json | 8 +- smappee-api.js | 253 ++++++++++++++++++++++++++----------------------- 3 files changed, 163 insertions(+), 126 deletions(-) diff --git a/README.md b/README.md index cc43a47..92c0fa3 100644 --- a/README.md +++ b/README.md @@ -14,17 +14,17 @@ Based on https://support.smappee.com/hc/en-us/articles/202153935-Where-can-I-fin ## Installation ```bash -#this below actually installs the original forked project, I don't know yet how to publish mine -#npm install smappee-nodejs --save +npm install smappee2mqtt --save ``` ## Usage ### Create a new file 'my-smappee.js' ```javascript -var SmappeeAPI = require('smappee-nodejs'); +var SmappeeAPI = require('smappee2mqtt'); var smappee = new SmappeeAPI({ debug: false, + noAPIcall: false, clientId: "xxx", clientSecret: "xxx", @@ -90,4 +90,26 @@ smappee.getConsumptions("0000", smappee.AGGREGATION_TYPES.MONTHLY, from, to, fun console.log(output); }) ``` + +### getLatestConsumption(serviceLocationId, callback) +Get a list of latest consumptions + +```javascript +var smappee = require('./my-smappee'); + +smappee.getLatestConsumption(serviceLocationId, function(output) { + console.log("getLatestConsumption (multiply WH by 12 if aggregate by 5 minutes since 5*12 = one hour to get Watts) : ",output); +}) +``` + +### getCurrentChargingSession(serviceLocationId,aggregation, from, to, callback) +Get a list of current charging session (will return -1 if no car is charging) + +```javascript +var smappee = require('./my-smappee'); +smappee.getCurrentChargingSession(chargingStationSN, function(output){ + console.log(output); +}) +``` + TIP: To convert the 5 minute interval from Energy [Wh] to Power [W], like the Smappee Dashboard reports these, you have to do these values times 12 as there are 12 x 5 minute intervals in an hour. diff --git a/package.json b/package.json index a0a4aa8..6bdf752 100644 --- a/package.json +++ b/package.json @@ -1,13 +1,13 @@ { "name": "smappee2mqtt", - "version": "1.0.1", + "version": "1.0.3", "description": "Connect to the Smappee API and push the data into your MQTT server", "main": "./smappee-api.js", "author": "hyzteric", "keywords": [ "smappee", "energy monitoring", - "mqtt" + "async-mqtt" ], "license": "OSL-3.0", "homepage": "https://github.com/hyzteric/Smappee2MQTT/", @@ -19,7 +19,7 @@ "url": "https://github.com/hyzteric/Smappee2MQTT/issues" }, "dependencies": { - "moment": "^2.10.6", - "request": "^2.67.0" + "moment": "latest", + "request": "latest" } } diff --git a/smappee-api.js b/smappee-api.js index 53f818d..f3fb649 100644 --- a/smappee-api.js +++ b/smappee-api.js @@ -3,7 +3,7 @@ var request = require('request'); var querystring = require('querystring'); var moment = require('moment'); const fs = require('node:fs'); -const mqtt = require('mqtt'); +const mqtt = require('async-mqtt'); function SmappeeAPI(settings) { @@ -14,7 +14,11 @@ function SmappeeAPI(settings) { var mqtt_server = settings.mqtt_server; var mqtt_port = settings.mqtt_port; var mqtt_baseTopic = settings.mqtt_baseTopic; + var mqtt_clientID = settings.mqtt_clientID || "smappee2pqtt" ; + var mqtt_protocol = settings.mqtt_protocol || "mqtt" ; + mqtt_protocol + this.noAPIcall = settings.noAPIcall || false; this.debug = settings.debug || false; var thisObject = this; @@ -95,34 +99,6 @@ function SmappeeAPI(settings) { }); }; - this.getLatestConsumption = function(serviceLocationId, handler) { - var url = 'https://app1pub.smappee.net/dev/v3/servicelocation/' + serviceLocationId + '/consumption'; - var fields = { - aggregation: this.AGGREGATION_TYPES.MINUTES, - from: moment().subtract(20, 'minutes').utc().valueOf(), - to: moment().add(5, 'minutes').utc().valueOf() - }; - _get(url, fields, function(output) { - if (thisObject.debug) { - console.log("getConsumptions output : "+ JSON.stringify(output)); - } - - if (output.consumptions.length > 0) { - if (thisObject.debug) { - console.log("publishing getConsumptions output to mqtt"); - } - _publishMQTT(mqtt_baseTopic+"consumptions",JSON.stringify(output.consumptions[output.consumptions.length - 1])); - handler(output.consumptions[output.consumptions.length - 1]); - } else { - if (thisObject.debug) { - console.log("getConsumptions output null"); - } - _publishMQTT(mqtt_baseTopic+"consumptions","no consumptions"); - handler(undefined); - } - }); - }; - this.getMonthlyConsumptionsForLastYear = function(serviceLocationId, handler) { var url = 'https://app1pub.smappee.net/dev/v3/servicelocation/' + serviceLocationId + '/consumption'; var fields = { @@ -154,33 +130,60 @@ function SmappeeAPI(settings) { var url = 'https://app1pub.smappee.net/dev/v3/servicelocation/' + serviceLocationId + '/actuator/' + actuatorId + '/off'; _post(url, "{'duration': " + duration + "}", handler); }; + + this.getLatestConsumption = function(serviceLocationId, handler) { + var url = 'https://app1pub.smappee.net/dev/v3/servicelocation/' + serviceLocationId + '/consumption'; + var fields = { + aggregation: this.AGGREGATION_TYPES.MINUTES, + from: moment().subtract(20, 'minutes').utc().valueOf(), + to: moment().add(5, 'minutes').utc().valueOf() + }; + _get(url, fields, function(output) { + if (thisObject.debug) { + console.log("getConsumptions output : "+ JSON.stringify(output)); + } + //if (output.consumptions.length > 0) { + if (output !=null) { + if (thisObject.debug) { + console.log("publishing getConsumptions output to mqtt"); + } + _publishMQTT(mqtt_baseTopic+"consumptions",JSON.stringify(output.consumptions[output.consumptions.length - 1])); + handler(output.consumptions[output.consumptions.length - 1]); + } else { + if (thisObject.debug) { + console.log("getConsumptions output null"); + } + _publishMQTT(mqtt_baseTopic+"consumptions","no consumptions"); + handler(undefined); + } + }); + }; this.getCurrentChargingSession = function(chargingStationSN, handler) { var url = 'https://app1pub.smappee.net/dev/v3/chargingstations/' + chargingStationSN + '/sessions'; - - if (thisObject.debug) { - console.log("getCurrentChargingSession url : "+ url); - } - var fields = { active: true, range:"1635721200000" }; _get(url, fields, function(output) { - if (output.length > 0) { + let timestampNow = Date.now(); + var timestampNowSeconds = timestampNow/1000; + var emptyResponse = "{\"id\":-1,\"timestamp\":"+timestampNowSeconds+"}"; + if (output!=null) { var apiResponse = JSON.stringify(output); - if (apiResponse.startsWith("[")){ + if (apiResponse.startsWith("[")){ apiResponse = apiResponse.substring(1, apiResponse.length-1);//remove invalid [ ] around response } - let timestampNow = Date.now(); - var timestampNowSeconds = timestampNow/1000; - apiResponse=apiResponse.substring(0, apiResponse.length-1); - apiResponse+=",\"timestamp\":"+timestampNowSeconds.toString()+"}"; - console.log("apiResponse with timestamp : "+ apiResponse); + if (apiResponse.length>0){ + //apiResponse=apiResponse.substring(0, apiResponse.length-1); + apiResponse+=",\"timestamp\":"+timestampNowSeconds.toString()+"}"; + } else { + apiResponse=emptyResponse; + } _publishMQTT(mqtt_baseTopic+"currentChargingSession",apiResponse); handler(apiResponse); } else { - _publishMQTT(mqtt_baseTopic+"currentChargingSession","{\"id\":-1}"); + _publishMQTT(mqtt_baseTopic+"currentChargingSession",emptyResponse); handler(undefined); } }); @@ -300,7 +303,6 @@ function SmappeeAPI(settings) { }); }); } - if (accessToken!=null){ handler(accessToken); } else { @@ -337,91 +339,104 @@ function SmappeeAPI(settings) { }; var _get = function(url, fields, handler) { - _getAccessToken(function(accessToken) { - var query = querystring.stringify(fields); - if (thisObject.debug) { - console.log("Request to " + url); - console.log("With parameters: " + query); - } - - var options = { - url: url + "?" + query, - headers: { - 'Authorization': 'Bearer ' + accessToken.access_token - } - }; - - request.get(options, function (err, httpResponse, body) { - if (err) { - return console.error('Request failed:', err); - } - if (thisObject.debug) { - //console.log('Server responded with:', body); - } - var output = JSON.parse(body); - handler(output); - }); //end of GET request - }); //end of access token request + if (thisObject.noAPIcall){ + console.log('Did not call API as noAPIcall is true'); + handler(); + } else { + _getAccessToken(function(accessToken) { + var query = querystring.stringify(fields); + if (thisObject.debug) { + console.log("Request to " + url); + console.log("With parameters: " + query); + } + + var options = { + url: url + "?" + query, + headers: { + 'Authorization': 'Bearer ' + accessToken.access_token + } + }; + + request.get(options, function (err, httpResponse, body) { + if (err) { + return console.error('Request failed:', err); + } + if (thisObject.debug) { + //console.log('Server responded with:', body); + } + var output = JSON.parse(body); + handler(output); + }); //end of GET request + }); //end of access token request + } }; - var _publishMQTT = function(topic, value){ + //var _publishMQTT = function(topic, value){ + async function _publishMQTT(topic, value){ const options = { - protocol: 'mqtt', + protocol: mqtt_protocol, host: mqtt_server, - port: mqtt_port - }; + port: mqtt_port, + clientId: mqtt_clientID + }; if (thisObject.debug) { - console.log('Connecting to mqtt'); - } - const client = mqtt.connect(options); - - - - client.on('offline', () => { - console.log('Client is offline'); - }); - - client.on('reconnect', () => { - console.log('Reconnecting to MQTT broker'); - }); - - client.on('close', () => { - console.log('Clien MQTT received closed event'); - }); - - client.on('disconnect', () => { - console.log('Clien MQTT received closed event'); - }); - - client.on('error', (err) => { - console.log('MQTT error: ',err); - }); - - client.on('end', () => { - console.log('Connection to MQTT broker ended'); - }); + console.log('Connecting to mqtt : ',mqtt_server,":",mqtt_port); + } - client.on('connect', () => { - console.log('Connected to MQTT broker'); - client.publish(topic, value, { retain: true }, (err) => { - if (thisObject.debug) { - console.log('Publishing to mqtt'); - } - if (err) { - console.error('Failed to publish message: ', err); - } else { - if (thisObject.debug) { - console.log('Message published with retain flag set to true'); - } - } - }); - client.end(); - }); - + try { + //const client = mqtt.connect(options); + const client = await mqtt.connect(options); + if (thisObject.debug) { + console.log('After connection attempt ',client.connected); + } + + client.on('offline', () => { + console.log('Client is offline'); + }); + + client.on('reconnect', () => { + console.log('Reconnecting to MQTT broker'); + }); + + client.on('close', () => { + console.log('Client MQTT received closed event'); + }); + + client.on('disconnect', () => { + console.log('Client MQTT received closed event'); + }); + + client.on('error', (err) => { + console.log('MQTT error: ',err); + }); + + client.on('end', () => { + console.log('Connection to MQTT broker ended'); + process.exit(); + }); + + await client.publish(topic, value, { retain: true }, (err) => { + if (thisObject.debug) { + console.log('Publishing to mqtt'); + } + if (err) { + console.error('Failed to publish message: ', err); + } else { + if (thisObject.debug) { + console.log('Message published with retain flag set to true'); + } + client.end(); + } + }); + client.end(); + } catch (e){ + // Do something about it! + console.log(e.stack); + process.exit(); + } } - } module.exports = SmappeeAPI; \ No newline at end of file From eb4342af02a738e1bba70c74b132f572fad332c2 Mon Sep 17 00:00:00 2001 From: Michel Remacle Date: Tue, 16 Jul 2024 20:15:04 +0200 Subject: [PATCH 23/26] update readme on node.js --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 6bdf752..049c167 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "smappee2mqtt", - "version": "1.0.3", + "version": "1.0.4", "description": "Connect to the Smappee API and push the data into your MQTT server", "main": "./smappee-api.js", "author": "hyzteric", From 456183e8d0fb87f75bc0f53ae57ac74676c39f23 Mon Sep 17 00:00:00 2001 From: Michel Remacle Date: Thu, 18 Jul 2024 20:18:53 +0200 Subject: [PATCH 24/26] Better error handling --- package.json | 2 +- smappee-api.js | 152 +++++++++++++++++++++++++++++-------------------- 2 files changed, 91 insertions(+), 63 deletions(-) diff --git a/package.json b/package.json index 049c167..6007a9e 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "smappee2mqtt", - "version": "1.0.4", + "version": "1.0.5", "description": "Connect to the Smappee API and push the data into your MQTT server", "main": "./smappee-api.js", "author": "hyzteric", diff --git a/smappee-api.js b/smappee-api.js index f3fb649..725d15f 100644 --- a/smappee-api.js +++ b/smappee-api.js @@ -79,23 +79,68 @@ function SmappeeAPI(settings) { }; //_get(url, fields, handler); _get(url, fields, function(output) { - if (thisObject.debug) { - console.log("getConsumptions output : "+ JSON.stringify(output)); - } + try { + var strOutput = JSON.stringify(output); + if (thisObject.debug) { + console.log("getConsumptions output : "+ strOutput); + } + if (strOutput.length>0) { + if (thisObject.debug) { + console.log("publishing getConsumptions output to mqtt"); + } + _publishMQTT(mqtt_baseTopic+"consumptions",JSON.stringify(output)); + handler(output); + } else { + if (thisObject.debug) { + console.log("getConsumptions output null"); + } + _publishMQTT(mqtt_baseTopic+"consumptions","no consumptions"); + handler(undefined); + } + } catch (e) { + if (thisObject.debug) { + console.log("getConsumptions output null"); + } + _publishMQTT(mqtt_baseTopic+"consumptions","no consumptions"); + handler(undefined); + } + }); + }; - if (output!=null) { - if (thisObject.debug) { - console.log("publishing getConsumptions output to mqtt"); - } - _publishMQTT(mqtt_baseTopic+"consumptions",JSON.stringify(output)); - handler(output); - } else { - if (thisObject.debug) { - console.log("getConsumptions output null"); - } - _publishMQTT(mqtt_baseTopic+"consumptions","no consumptions"); - handler(undefined); - } + this.getLatestConsumption = function(serviceLocationId, handler) { + var url = 'https://app1pub.smappee.net/dev/v3/servicelocation/' + serviceLocationId + '/consumption'; + var fields = { + aggregation: this.AGGREGATION_TYPES.MINUTES, + from: moment().subtract(20, 'minutes').utc().valueOf(), + to: moment().add(5, 'minutes').utc().valueOf() + }; + _get(url, fields, function(output) { + try { + var strOutput = JSON.stringify(output.consumptions[output.consumptions.length - 1]); + if (thisObject.debug) { + console.log("getConsumptions output 1 : "+ JSON.stringify(output)); + } + if (strOutput.length > 0) { + if (thisObject.debug) { + console.log("publishing getConsumptions output to mqtt"); + } + _publishMQTT(mqtt_baseTopic+"consumptions",strOutput); + handler(output.consumptions[output.consumptions.length - 1]); + } else { + if (thisObject.debug) { + console.log("getConsumptions output is null"); + } + console.log("getConsumptions avant publish "); + _publishMQTT(mqtt_baseTopic+"consumptions","no consumptions"); + handler(undefined); + } + } catch (e) { + if (thisObject.debug) { + console.log("getConsumptions output null"); + } + _publishMQTT(mqtt_baseTopic+"consumptions","no consumptions"); + handler(undefined); + } }); }; @@ -130,62 +175,41 @@ function SmappeeAPI(settings) { var url = 'https://app1pub.smappee.net/dev/v3/servicelocation/' + serviceLocationId + '/actuator/' + actuatorId + '/off'; _post(url, "{'duration': " + duration + "}", handler); }; - - this.getLatestConsumption = function(serviceLocationId, handler) { - var url = 'https://app1pub.smappee.net/dev/v3/servicelocation/' + serviceLocationId + '/consumption'; - var fields = { - aggregation: this.AGGREGATION_TYPES.MINUTES, - from: moment().subtract(20, 'minutes').utc().valueOf(), - to: moment().add(5, 'minutes').utc().valueOf() - }; - _get(url, fields, function(output) { - if (thisObject.debug) { - console.log("getConsumptions output : "+ JSON.stringify(output)); - } - //if (output.consumptions.length > 0) { - if (output !=null) { - if (thisObject.debug) { - console.log("publishing getConsumptions output to mqtt"); - } - _publishMQTT(mqtt_baseTopic+"consumptions",JSON.stringify(output.consumptions[output.consumptions.length - 1])); - handler(output.consumptions[output.consumptions.length - 1]); - } else { - if (thisObject.debug) { - console.log("getConsumptions output null"); - } - _publishMQTT(mqtt_baseTopic+"consumptions","no consumptions"); - handler(undefined); - } - }); - }; this.getCurrentChargingSession = function(chargingStationSN, handler) { var url = 'https://app1pub.smappee.net/dev/v3/chargingstations/' + chargingStationSN + '/sessions'; + + if (thisObject.debug) { + console.log("getCurrentChargingSession url : "+ url); + } + var fields = { active: true, range:"1635721200000" }; _get(url, fields, function(output) { - let timestampNow = Date.now(); - var timestampNowSeconds = timestampNow/1000; - var emptyResponse = "{\"id\":-1,\"timestamp\":"+timestampNowSeconds+"}"; - if (output!=null) { - var apiResponse = JSON.stringify(output); - if (apiResponse.startsWith("[")){ - apiResponse = apiResponse.substring(1, apiResponse.length-1);//remove invalid [ ] around response - } - if (apiResponse.length>0){ - //apiResponse=apiResponse.substring(0, apiResponse.length-1); + try { + var strOutput = JSON.stringify(output); + if (strOutput.length>0) { + var apiResponse = strOutput; + if (apiResponse.startsWith("[")){ + apiResponse = apiResponse.substring(1, apiResponse.length-1);//remove invalid [ ] around response + } + let timestampNow = Date.now(); + var timestampNowSeconds = timestampNow/1000; + apiResponse=apiResponse.substring(0, apiResponse.length-1); apiResponse+=",\"timestamp\":"+timestampNowSeconds.toString()+"}"; + console.log("apiResponse with timestamp : "+ apiResponse); + _publishMQTT(mqtt_baseTopic+"currentChargingSession",apiResponse); + handler(apiResponse); } else { - apiResponse=emptyResponse; - } - _publishMQTT(mqtt_baseTopic+"currentChargingSession",apiResponse); - handler(apiResponse); - } else { - _publishMQTT(mqtt_baseTopic+"currentChargingSession",emptyResponse); - handler(undefined); - } + _publishMQTT(mqtt_baseTopic+"currentChargingSession","{\"id\":-1}"); + handler(undefined); + } + } catch (e) { + _publishMQTT(mqtt_baseTopic+"currentChargingSession","{\"id\":-1}"); + handler(undefined); + } }); }; @@ -303,6 +327,7 @@ function SmappeeAPI(settings) { }); }); } + if (accessToken!=null){ handler(accessToken); } else { @@ -369,6 +394,7 @@ function SmappeeAPI(settings) { }); //end of GET request }); //end of access token request } + }; @@ -417,6 +443,8 @@ function SmappeeAPI(settings) { process.exit(); }); + + console.log('Connected to MQTT broker'); await client.publish(topic, value, { retain: true }, (err) => { if (thisObject.debug) { console.log('Publishing to mqtt'); From f57b5254c32f2953e05082880f6982e479b601e5 Mon Sep 17 00:00:00 2001 From: Michel Remacle Date: Thu, 18 Jul 2024 20:28:07 +0200 Subject: [PATCH 25/26] Update README.md --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 92c0fa3..c836a05 100644 --- a/README.md +++ b/README.md @@ -1,4 +1,4 @@ -Please note : this fork is probably not working at this stage. I'm trying to edit it to get the refresh token working and add the EV Line charging session stuff. I did never code in Node JS so I'll see what I can do... +Please note : this fork is just sending the last 5 min consumption and current charging session to mqtt at this stage. I did never code in Node JS so I'll see what I can do... Todo list : - Store token in file ✅ - Read token from file ✅ From 88d8429ab61cc7d5b6b51972aa25da985c345aaa Mon Sep 17 00:00:00 2001 From: Michel Remacle Date: Wed, 11 Sep 2024 10:05:46 +0200 Subject: [PATCH 26/26] Fix getCurrentChargingSession return when charging session is over --- smappee-api.js | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/smappee-api.js b/smappee-api.js index 725d15f..93168f0 100644 --- a/smappee-api.js +++ b/smappee-api.js @@ -190,7 +190,7 @@ function SmappeeAPI(settings) { _get(url, fields, function(output) { try { var strOutput = JSON.stringify(output); - if (strOutput.length>0) { + if (strOutput.length>0 && strOutput!="[]") { var apiResponse = strOutput; if (apiResponse.startsWith("[")){ apiResponse = apiResponse.substring(1, apiResponse.length-1);//remove invalid [ ] around response @@ -199,7 +199,9 @@ function SmappeeAPI(settings) { var timestampNowSeconds = timestampNow/1000; apiResponse=apiResponse.substring(0, apiResponse.length-1); apiResponse+=",\"timestamp\":"+timestampNowSeconds.toString()+"}"; - console.log("apiResponse with timestamp : "+ apiResponse); + if (thisObject.debug) { + console.log("apiResponse with timestamp : "+ apiResponse); + } _publishMQTT(mqtt_baseTopic+"currentChargingSession",apiResponse); handler(apiResponse); } else {