diff --git a/README.md b/README.md index 6e469aa0..8b83fef6 100644 --- a/README.md +++ b/README.md @@ -24,17 +24,15 @@ pass = demo ## To-Do -Core-Functionality -* Adjustable polling invervall -* Better interface for adapter configuration * Translations -Backend -* store password encrypted -* better handling of state creation and updates - ## Changelog +### 0.3.0 +* Implemented encrypted password storage +* Fixed issue with polling of states (this should fix "connection failed" of version 0.2.9) +* Correct error message in LOG when credentials are missing + ### 0.2.9 * implemented intervall short and long, only relevant information (current consumption) is pulled short alle other (totals) on interval Long * Implemented additional datapoints for Power, Power_x_Consumption and Power_x_Delivery, the power value can have a positive and negative number depending of if u consumer or produce for the network. Seperate - and + values to seperated datapoints. diff --git a/admin/index_m.html b/admin/index_m.html index 31222643..0750137b 100644 --- a/admin/index_m.html +++ b/admin/index_m.html @@ -18,45 +18,85 @@ @@ -75,11 +115,11 @@

discovergy adapter settings

- +
- +
diff --git a/io-package.json b/io-package.json index 6be7f6a0..f5e1d1e5 100644 --- a/io-package.json +++ b/io-package.json @@ -1,9 +1,9 @@ { "common": { "name": "discovergy", - "version": "0.2.9", + "version": "0.3.0", "news": { - "0.3.1": { + "0.3.0": { "en": "Beta", "de": "Beta", "ru": "Начальная версия", @@ -70,5 +70,17 @@ "pull_Long": "1" }, "objects": [], - "instanceObjects": [] + "instanceObjects": [{ + "_id": "info.connection", + "type": "state", + "common": { + "role": "indicator.connected", + "name": "If communication with Discovergy works", + "type": "boolean", + "read": true, + "write": false, + "def": false + }, + "native": {} + }] } \ No newline at end of file diff --git a/main.js b/main.js index 08000a26..e9fdecbb 100644 --- a/main.js +++ b/main.js @@ -1,13 +1,13 @@ const utils = require("@iobroker/adapter-core"); const http_request = require("request"); let checkRestart_adapter ,checkInterval_short, checkInterval_long, user, pass, pull_Long, pull_Short; -let running = false; // Create the adapter and define its methods const adapter = utils.adapter({ name: "discovergy", // start here! - ready: main, // Main method defined below for readability + //ready: main, // Main method defined below for readability + ready: onReady, // is called when adapter shuts down - callback has to be called under any circumstances! unload: (callback) => { if (checkInterval_short != undefined) clearTimeout(checkInterval_short); @@ -22,170 +22,184 @@ const adapter = utils.adapter({ }, }); +// Decrypt stored password when adapter is ready +function onReady() { +// adapter.setState("info.connection", false, true); +// adapter.log.debug("ready - Adapter: databases are connected and adapter received configuration"); +// adapter.log.silly("config.client_secret verschlüsselt: " + adapter.config.Password); + + adapter.getForeignObject("system.config", (err, obj) => { + if (obj && obj.native && obj.native.secret) { + //noinspection JSUnresolvedVariable + pass = decrypt(obj.native.secret, adapter.config.Password); + } else { + //noinspection JSUnresolvedVariable + pass = decrypt("Zgfr56gFe87jJOM", adapter.config.Password); + } + main(); + }); +} + +// Main funtion to handle intervalls for polling function main() { // Load configuration user = adapter.config.Username; - pass = adapter.config.Password; // adapter.log.info(user) - // adapter.log.info(pass) + // adapter.log.info("Password : " + pass); //@ts-ignoreTS-ignore pull_Long = (adapter.config.pull_Long * 60000); //@ts-ignoreTS-ignore pull_Short = (adapter.config.pull_Short * 1000); - // if (user === null || pass === null){ + // Check if credentials are not empty + if (user !== "" && pass !== ""){ - // Lets first ensure all data is read 1 time and all channels, devices etc are created. - // After initialisation is finished the timers will start for short and long pulling - doDiscovergyCall(user, pass, "meters", "","initialize"); - - checkInterval_short = setInterval(function () { - doDiscovergyCall(user, pass, "meters", "","short"); - }, pull_Short); - - checkInterval_long = setInterval(function () { - doDiscovergyCall(user, pass, "meters", "","long"); - }, pull_Long); -/* } else { - adapter.log.error("*** Please enter Discovergy credentials in adapter settings ***"); - clearTimeout(checkRestart_adapter); - checkRestart_adapter = setTimeout(function () { - main(); - }, 30000); - }*/ + // Lets first ensure all data is read 1 time and all channels, devices etc are created. + // After initialisation is finished the timers will start for short and long pulling + doDiscovergyCall(user, pass, "meters", "","initialize"); + + checkInterval_short = setInterval(function () { + doDiscovergyCall(user, pass, "meters", "","short"); + }, pull_Short); + + checkInterval_long = setInterval(function () { + doDiscovergyCall(user, pass, "meters", "","long"); + }, pull_Long); + } else { + adapter.log.error("*** Adapter deactivated, credentials are missing in Adaptper Settings ***"); + adapter.setState("info.connection", false, true); + adapter.stop; + } } // Call Discovergy API en get an oerview of all meters present in your account and create objects with basic information function doDiscovergyCall(username, password, endpoint, urlencoded_parameters, pulltype) { - if (!running) { - running = true; - - const requestUrl = `https://${username}:${password}@api.discovergy.com/public/v1/${endpoint}?${urlencoded_parameters}`; - http_request(requestUrl, (error, response, body) => { - - if (!error && response.statusCode === 200) { - // we got a response - - // Retrieve all meter objects from Discovergy API - /** @type {Record[]} */ - const objArray = JSON.parse(body); - // adapter.log.info(myStringArray[allobjects]) - for (const meterobjects of objArray) { - - // adapter.log.info(allobjects + " : " + myStringArray[allobjects]) - - // We dont use all values currenlty, some of them are not needed (if yes please create a pull request or issue) - // const administrationNumber = meterobjects.administrationNumber; - // const currentScalingFactor = meterobjects.currentScalingFactor; - const firstMeasurementTime = meterobjects.firstMeasurementTime; - // const internalMeters = meterobjects.internalMeters; - // const lastMeasurementTime = meterobjects.lastMeasurementTime; - const location = meterobjects.location; - const measurementType = meterobjects.measurementType; - // const scalingFactor = meterobjects.scalingFactor; - const serialNumber = meterobjects.serialNumber; - const meterId = meterobjects.meterId; - const type = meterobjects.type; - // const voltageScalingFactor = meterobjects.voltageScalingFactor; - - // Only handle routine below wen adapter runs its first initalisation - if (pulltype == "initialize"){ - // Create Device Channel for Each found serialnumber - adapter.setObjectNotExists(serialNumber, { - type: "device", - common: { - name: serialNumber, - }, - native: {}, - }); - - // Create all objects for basic information seperated by serialnumber of device - doStateCreate(serialNumber + ".info" + ".administrationNumber", "Administrationsnummer", "number", ""); - adapter.setState(serialNumber + ".info" + ".administrationNumber", { val: "123456789", ack: true }); - - //doStateCreate(serialNumber + ".info" + ".currentScalingFactor","Jetziger skalierungsfactor","number","") - //adapter.setState(serialNumber + ".info" + ".currentScalingFactor", { val: currentScalingFactor, ack: true }); - - doStateCreate(serialNumber + ".info" + ".firstMeasurementTime", "Erste Messung", "number", ""); - adapter.setState(serialNumber + ".info" + ".firstMeasurementTime", { val: firstMeasurementTime, ack: true }); - - //doStateCreate(serialNumber + ".info" + ".internalMeters","Anzahl interner Messgeraete","number","") - //adapter.setState(serialNumber + ".info" + ".internalMeters", { val: internalMeters, ack: true }); - //doStateCreate(serialNumber + ".info" + ".Last_Timestamp","Letzte aktualisierung","number","") - //adapter.setState(serialNumber + ".info" + ".Last_Timestamp", { val: lastMeasurementTime, ack: true }); - - // Locations are multiple values in an object and must be threated diffferently - doStateCreate(serialNumber + ".info" + ".location.street", "Strasse", "string", ""); - adapter.setState(serialNumber + ".info" + ".location.street", { val: location.street, ack: true }); - - doStateCreate(serialNumber + ".info" + ".location.streetNumber", "Hausnummer", "number", ""); - adapter.setState(serialNumber + ".info" + ".location.streetNumber", { val: location.streetNumber, ack: true }); - - doStateCreate(serialNumber + ".info" + ".location.zip", "Ort", "Postleitzahl", ""); - adapter.setState(serialNumber + ".info" + ".location.zip", { val: location.zip, ack: true }); - - doStateCreate(serialNumber + ".info" + ".location.city", "Ort", "string", ""); - adapter.setState(serialNumber + ".info" + ".location.city", { val: location.city, ack: true }); - - doStateCreate(serialNumber + ".info" + ".location.country", "Land", "string", ""); - adapter.setState(serialNumber + ".info" + ".location.country", { val: location.country, ack: true }); - - doStateCreate(serialNumber + ".info" + ".measurementType", "Energy Type", "string", ""); - adapter.setState(serialNumber + ".info" + ".measurementType", { val: measurementType, ack: true }); - - //doStateCreate(serialNumber + ".info" + ".scalingFactor","Skalierungsfactor","number","") - //adapter.setState(serialNumber + ".info" + ".scalingFactor", { val: scalingFactor, ack: true }); - - doStateCreate(serialNumber + ".info" + ".serialNumber", "Seriennummer", "number", ""); - adapter.setState(serialNumber + ".info" + ".serialNumber", { val: serialNumber, ack: true }); - - doStateCreate(serialNumber + ".info" + ".meterId", "Id des Messgeraetes", "number", ""); - adapter.setState(serialNumber + ".info" + ".meterId", { val: meterId, ack: true }); - - doStateCreate(serialNumber + ".info" + ".type", "Device Type", "number", ""); - adapter.setState(serialNumber + ".info" + ".type", { val: type, ack: true }); - - //doStateCreate(serialNumber + ".info" + ".voltageScalingFactor","Voltage Skalierungsfactor","number","") - //adapter.setState(serialNumber + ".info" + ".voltageScalingFactor", { val: voltageScalingFactor, ack: true }); - } + const requestUrl = `https://${username}:${password}@api.discovergy.com/public/v1/${endpoint}?${urlencoded_parameters}`; + http_request(requestUrl, (error, response, body) => { + + if (!error && response.statusCode === 200) { + adapter.setState("info.connection", true, true); + // we got a response - // Loop on all objects to Verify if JSON contains values which are not yet implemented in Adapter, if implemented do nothing if not return error message - for (const x in meterobjects) { - // adapter.log.info(x + " : " + meterobjects[x]) - switch (x) { - case "administrationNumber": - case "currentScalingFactor": - case "firstMeasurementTime": - case "internalMeters": - case "lastMeasurementTime": - case "location": - case "measurementType": - case "meterId": - case "scalingFactor": - case "serialNumber": - case "type": - case "voltageScalingFactor": - break; - - default: - - adapter.log.error("Information received from Discovergy which is not yet part of this adapter"); - adapter.log.error("Send this information to developer : " + x + " : " + meterobjects[x]); - } - - // Do not handle meter type RLM yet, unclear what kind of device this is and values provided - if (type != "RLM") { - doDiscovergyMeter(user, pass, "last_reading", meterId, serialNumber,pulltype); - } + // Retrieve all meter objects from Discovergy API + /** @type {Record[]} */ + const objArray = JSON.parse(body); + // adapter.log.info(myStringArray[allobjects]) + for (const meterobjects of objArray) { + + // adapter.log.info(allobjects + " : " + myStringArray[allobjects]) + + // We dont use all values currenlty, some of them are not needed (if yes please create a pull request or issue) + // const administrationNumber = meterobjects.administrationNumber; + // const currentScalingFactor = meterobjects.currentScalingFactor; + const firstMeasurementTime = meterobjects.firstMeasurementTime; + // const internalMeters = meterobjects.internalMeters; + // const lastMeasurementTime = meterobjects.lastMeasurementTime; + const location = meterobjects.location; + const measurementType = meterobjects.measurementType; + // const scalingFactor = meterobjects.scalingFactor; + const serialNumber = meterobjects.serialNumber; + const meterId = meterobjects.meterId; + const type = meterobjects.type; + // const voltageScalingFactor = meterobjects.voltageScalingFactor; + + // Only handle routine below wen adapter runs its first initalisation + if (pulltype == "initialize"){ + // Create Device Channel for Each found serialnumber + adapter.setObjectNotExists(serialNumber, { + type: "device", + common: { + name: serialNumber, + }, + native: {}, + }); + + // Create all objects for basic information seperated by serialnumber of device + doStateCreate(serialNumber + ".info" + ".administrationNumber", "Administrationsnummer", "number", ""); + adapter.setState(serialNumber + ".info" + ".administrationNumber", { val: "123456789", ack: true }); + + //doStateCreate(serialNumber + ".info" + ".currentScalingFactor","Jetziger skalierungsfactor","number","") + //adapter.setState(serialNumber + ".info" + ".currentScalingFactor", { val: currentScalingFactor, ack: true }); + + doStateCreate(serialNumber + ".info" + ".firstMeasurementTime", "Erste Messung", "number", ""); + adapter.setState(serialNumber + ".info" + ".firstMeasurementTime", { val: firstMeasurementTime, ack: true }); + + //doStateCreate(serialNumber + ".info" + ".internalMeters","Anzahl interner Messgeraete","number","") + //adapter.setState(serialNumber + ".info" + ".internalMeters", { val: internalMeters, ack: true }); + //doStateCreate(serialNumber + ".info" + ".Last_Timestamp","Letzte aktualisierung","number","") + //adapter.setState(serialNumber + ".info" + ".Last_Timestamp", { val: lastMeasurementTime, ack: true }); + + // Locations are multiple values in an object and must be threated diffferently + doStateCreate(serialNumber + ".info" + ".location.street", "Strasse", "string", ""); + adapter.setState(serialNumber + ".info" + ".location.street", { val: location.street, ack: true }); + + doStateCreate(serialNumber + ".info" + ".location.streetNumber", "Hausnummer", "number", ""); + adapter.setState(serialNumber + ".info" + ".location.streetNumber", { val: location.streetNumber, ack: true }); + + doStateCreate(serialNumber + ".info" + ".location.zip", "Ort", "Postleitzahl", ""); + adapter.setState(serialNumber + ".info" + ".location.zip", { val: location.zip, ack: true }); + + doStateCreate(serialNumber + ".info" + ".location.city", "Ort", "string", ""); + adapter.setState(serialNumber + ".info" + ".location.city", { val: location.city, ack: true }); + + doStateCreate(serialNumber + ".info" + ".location.country", "Land", "string", ""); + adapter.setState(serialNumber + ".info" + ".location.country", { val: location.country, ack: true }); + + doStateCreate(serialNumber + ".info" + ".measurementType", "Energy Type", "string", ""); + adapter.setState(serialNumber + ".info" + ".measurementType", { val: measurementType, ack: true }); + + //doStateCreate(serialNumber + ".info" + ".scalingFactor","Skalierungsfactor","number","") + //adapter.setState(serialNumber + ".info" + ".scalingFactor", { val: scalingFactor, ack: true }); + + doStateCreate(serialNumber + ".info" + ".serialNumber", "Seriennummer", "number", ""); + adapter.setState(serialNumber + ".info" + ".serialNumber", { val: serialNumber, ack: true }); + + doStateCreate(serialNumber + ".info" + ".meterId", "Id des Messgeraetes", "number", ""); + adapter.setState(serialNumber + ".info" + ".meterId", { val: meterId, ack: true }); + + doStateCreate(serialNumber + ".info" + ".type", "Device Type", "number", ""); + adapter.setState(serialNumber + ".info" + ".type", { val: type, ack: true }); + + //doStateCreate(serialNumber + ".info" + ".voltageScalingFactor","Voltage Skalierungsfactor","number","") + //adapter.setState(serialNumber + ".info" + ".voltageScalingFactor", { val: voltageScalingFactor, ack: true }); + } + + // Loop on all objects to Verify if JSON contains values which are not yet implemented in Adapter, if implemented do nothing if not return error message + for (const x in meterobjects) { + // adapter.log.info(x + " : " + meterobjects[x]) + switch (x) { + case "administrationNumber": + case "currentScalingFactor": + case "firstMeasurementTime": + case "internalMeters": + case "lastMeasurementTime": + case "location": + case "measurementType": + case "meterId": + case "scalingFactor": + case "serialNumber": + case "type": + case "voltageScalingFactor": + break; + + default: + + adapter.log.error("Information received from Discovergy which is not yet part of this adapter"); + adapter.log.error("Send this information to developer : " + x + " : " + meterobjects[x]); } } - } else { // error or non-200 status code - adapter.log.error("Connection_Failed : " + error); + + // Do not handle meter type RLM yet, unclear what kind of device this is and values provided + if (type != "RLM") { + doDiscovergyMeter(user, pass, "last_reading", meterId, serialNumber,pulltype); + } } - }); - running = false; - } + } else { // error or non-200 status code + adapter.log.error("Connection_Failed, check your credentials !"); + adapter.setState("info.connection", false, true); + } + }); } // Function to receive values from specific meter @@ -367,4 +381,14 @@ function doStateCreate(id, name, type, unit) { }, native: {}, }); +} + +// Function to decrypt passwords +function decrypt(key, value) { + let result = ""; + for (let i = 0; i < value.length; ++i) { + result += String.fromCharCode(key[i % key.length].charCodeAt(0) ^ value.charCodeAt(i)); + } + adapter.log.debug("client_secret decrypt ready"); + return result; } \ No newline at end of file diff --git a/package-lock.json b/package-lock.json index bbc4de44..afe05079 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,6 +1,6 @@ { "name": "iobroker.discovergy", - "version": "0.2.9", + "version": "0.3.0", "lockfileVersion": 1, "requires": true, "dependencies": { diff --git a/package.json b/package.json index e2c64620..c695c4b4 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "iobroker.discovergy", - "version": "0.2.9", + "version": "0.3.0", "description": "Discovergy", "author": { "name": "Dutchman",