iobroker.senec 1.6.0 → 1.6.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +6 -0
- package/io-package.json +27 -27
- package/lib/api_trans.js +29 -0
- package/main.js +61 -7
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -366,6 +366,12 @@ This channel contains calculated values. Currently these are day/week/month/year
|
|
|
366
366
|
*Read-only number, which designates the number of wallbox [0..3]. This is only available on systems with configured wallboxes.*
|
|
367
367
|
|
|
368
368
|
## Changelog
|
|
369
|
+
### 1.6.2 (NoBl)
|
|
370
|
+
* Added statistics values from API along with some own calculations.
|
|
371
|
+
|
|
372
|
+
### 1.6.1 (NoBl)
|
|
373
|
+
* Bugfixes
|
|
374
|
+
|
|
369
375
|
### 1.6.0 (NoBl)
|
|
370
376
|
* Added option to also poll SENEC App API. This requires user credentials for mein-senec.de
|
|
371
377
|
* We are starting with just some information - more to follow. But with Dashboard we at least have current values and day statistics back.
|
package/io-package.json
CHANGED
|
@@ -1,8 +1,34 @@
|
|
|
1
1
|
{
|
|
2
2
|
"common": {
|
|
3
3
|
"name": "senec",
|
|
4
|
-
"version": "1.6.
|
|
4
|
+
"version": "1.6.2",
|
|
5
5
|
"news": {
|
|
6
|
+
"1.6.2": {
|
|
7
|
+
"en": "Added statistics values from API along with some own calculations.",
|
|
8
|
+
"de": "Statistikwerte von API zusammen mit einigen eigenen Berechnungen hinzugefügt.",
|
|
9
|
+
"ru": "Добавлены статистические значения из API наряду с некоторыми собственными расчетами.",
|
|
10
|
+
"pt": "Adicionados valores estatísticos da API junto com alguns próprios cálculos.",
|
|
11
|
+
"nl": "Voeg statistieken toe van API samen met wat eigen berekeningen.",
|
|
12
|
+
"fr": "Ajout des valeurs statistiques de l'API avec certains propres calculs.",
|
|
13
|
+
"it": "Aggiunti i valori statistici da API con alcuni propri calcoli.",
|
|
14
|
+
"es": "Valores estadísticos añadidos de API junto con algunos cálculos propios.",
|
|
15
|
+
"pl": "Wstępne dane z API wraz z kilkoma własnymi obliczeniami.",
|
|
16
|
+
"uk": "Додано значення статистичних даних з API разом з деякими власними підрахунками.",
|
|
17
|
+
"zh-cn": "加上非传染性疾病的统计价值以及一些本身的计算。."
|
|
18
|
+
},
|
|
19
|
+
"1.6.1": {
|
|
20
|
+
"en": "Bugfixes for API reading.",
|
|
21
|
+
"de": "Bugfixes im API Handling.",
|
|
22
|
+
"ru": "Bugfixes для чтения API.",
|
|
23
|
+
"pt": "Bugfixes para leitura de API.",
|
|
24
|
+
"nl": "Bugfixes voor API-lezing.",
|
|
25
|
+
"fr": "Bugfixes pour la lecture de l'API.",
|
|
26
|
+
"it": "Bugfixes per la lettura API.",
|
|
27
|
+
"es": "Bugfixes para la lectura de API.",
|
|
28
|
+
"pl": "Bugfixes for API reading (ang.).",
|
|
29
|
+
"uk": "Виправлення помилок для читання API.",
|
|
30
|
+
"zh-cn": "评 注."
|
|
31
|
+
},
|
|
6
32
|
"1.6.0": {
|
|
7
33
|
"en": "You can now configure to request SENEC App API.",
|
|
8
34
|
"de": "Sie können nun die SENEC App API abfragen.",
|
|
@@ -80,32 +106,6 @@
|
|
|
80
106
|
"pl": "Dodana opcja do używania https dla połączenia z SENEC (w sposób aktywowana, jeśli dana osoba obsługuje / wymaga to)",
|
|
81
107
|
"uk": "Додано можливість використовувати HTTPS для підключення до SENEC (тільки активуйте, якщо підтримує додаток / вимагає цього)",
|
|
82
108
|
"zh-cn": "增加使用连接到SENEC的网站的选择(如果你的可靠支持/要求这样做)"
|
|
83
|
-
},
|
|
84
|
-
"1.4.1": {
|
|
85
|
-
"en": "Autarky calculations are working again.",
|
|
86
|
-
"de": "Autarkieberechnungen funktionieren wieder.",
|
|
87
|
-
"ru": "Расчеты Autarky снова работают.",
|
|
88
|
-
"pt": "Cálculos autarcos estão a funcionar outra vez.",
|
|
89
|
-
"nl": "Autarische berekeningen werken weer.",
|
|
90
|
-
"fr": "Les calculs Autarky fonctionnent à nouveau.",
|
|
91
|
-
"it": "I calcoli autarky funzionano di nuovo.",
|
|
92
|
-
"es": "Los cálculos autárquicos están funcionando de nuevo.",
|
|
93
|
-
"pl": "Ponowne obliczenia autarktyczne działają ponownie.",
|
|
94
|
-
"uk": "Аутарки знову працюють.",
|
|
95
|
-
"zh-cn": "Autarky的计算正再次工作。."
|
|
96
|
-
},
|
|
97
|
-
"1.4.0": {
|
|
98
|
-
"en": "Added object caching and minor code updates. Due to the amount of objects we deal with caching is about mandatory.",
|
|
99
|
-
"de": "Objekt-Caching und kleinere Code-Updates hinzugefügt. Aufgrund der Menge an Objekten, die der Adapter bearbeitet, ist Caching zwingend notwendig.",
|
|
100
|
-
"ru": "Добавлена кэшировка объекта и незначительные обновления кода. В связи с количеством объектов, с которыми мы занимаемся кэшированием, является обязательным.",
|
|
101
|
-
"pt": "Adicionado cache de objeto e pequenas atualizações de código. Devido à quantidade de objetos que lidamos com caching é sobre obrigatória.",
|
|
102
|
-
"nl": "Toegevoegd voorwerp caching en kleine code updates. Gezien de hoeveelheid objecten die we met caching hebben, gaat het over verplichting.",
|
|
103
|
-
"fr": "Ajout de cache d'objet et de mises à jour de code mineur. En raison de la quantité d'objets que nous traitons avec le cache est environ obligatoire.",
|
|
104
|
-
"it": "Aggiunti caching oggetto e aggiornamenti di codice minori. A causa della quantità di oggetti che abbiamo a che fare con il caching è circa obbligatorio.",
|
|
105
|
-
"es": "Añadido objeto caching y actualizaciones de código menor. Debido a la cantidad de objetos que tratamos con el caché es sobre obligatorio.",
|
|
106
|
-
"pl": "Dodany obiekt caching i drobnych aktualizacji kodu. Ze względu na ilość przedmiotów, które traktują z cachingiem, dotyczy obowiązkowych.",
|
|
107
|
-
"uk": "Додано кешування об'єкта та оновлення коду неповного розміру. У зв'язку з кількістю об'єктів, які ми маємо справу з кешуванням, є обов'язковим.",
|
|
108
|
-
"zh-cn": "添加了目标包和小法典的更新。 由于我们所处理的物品数量是强制性的。."
|
|
109
109
|
}
|
|
110
110
|
},
|
|
111
111
|
"docs": {
|
package/lib/api_trans.js
ADDED
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
const api_trans = {
|
|
2
|
+
'THIS_DAY': {
|
|
3
|
+
api: 'TAG',
|
|
4
|
+
dp: 'Today'
|
|
5
|
+
},
|
|
6
|
+
'LAST_DAY': {
|
|
7
|
+
api: 'TAG',
|
|
8
|
+
dp: 'Yesterday'
|
|
9
|
+
},
|
|
10
|
+
'THIS_MONTH': {
|
|
11
|
+
api: 'MONAT',
|
|
12
|
+
dp: 'This Month'
|
|
13
|
+
},
|
|
14
|
+
'LAST_MONTH': {
|
|
15
|
+
api: 'MONAT',
|
|
16
|
+
dp: 'Last Month'
|
|
17
|
+
},
|
|
18
|
+
'THIS_YEAR': {
|
|
19
|
+
api: 'JAHR',
|
|
20
|
+
dp: 'This Year'
|
|
21
|
+
},
|
|
22
|
+
'LAST_YEAR': {
|
|
23
|
+
api: 'JAHR',
|
|
24
|
+
dp: 'Last Year'
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
module.exports = api_trans;
|
package/main.js
CHANGED
|
@@ -10,10 +10,13 @@ const agent = new https.Agent({
|
|
|
10
10
|
const utils = require('@iobroker/adapter-core');
|
|
11
11
|
|
|
12
12
|
const axios = require('axios').default;
|
|
13
|
-
axios.defaults.headers.
|
|
13
|
+
axios.defaults.headers.post['Content-Type'] = "application/json";
|
|
14
14
|
|
|
15
15
|
const state_attr = require(__dirname + '/lib/state_attr.js');
|
|
16
16
|
const state_trans = require(__dirname + '/lib/state_trans.js');
|
|
17
|
+
const api_trans = require(__dirname + '/lib/api_trans.js');
|
|
18
|
+
const kiloList = ["W", "Wh"];
|
|
19
|
+
|
|
17
20
|
const apiUrl = "https://app-gateway-prod.senecops.com/v1/senec";
|
|
18
21
|
const apiLoginUrl = apiUrl + "/login";
|
|
19
22
|
const apiSystemsUrl = apiUrl + "/anlagen";
|
|
@@ -26,6 +29,7 @@ let retryLowPrio = 0; // retry-counter
|
|
|
26
29
|
let connectVia = "http://";
|
|
27
30
|
|
|
28
31
|
const allKnownObjects = new Set(["BAT1","BAT1OBJ1","BAT1OBJ2","BAT1OBJ3","BMS","BMS_PARA","CASC","DEBUG","DISPLAY","ENERGY","FACTORY","FEATURES","FILE","GRIDCONFIG","ISKRA","LOG","PM1","PM1OBJ1","PM1OBJ2","PV1","PWR_UNIT","RTC","SELFTEST_RESULTS","SOCKETS","STATISTIC","STECA","SYS_UPDATE","TEMPMEASURE","TEST","UPDATE","WALLBOX","WIZARD"]);
|
|
32
|
+
// STATISTICS is faded out by senec. Keeping it while we still have machines getting it. This will also deprecate all calculated valued in the "_calc" branch.
|
|
29
33
|
|
|
30
34
|
const highPrioObjects = new Map;
|
|
31
35
|
let lowPrioForm = "";
|
|
@@ -127,6 +131,7 @@ class Senec extends utils.Adapter {
|
|
|
127
131
|
if (this.config.disclaimer && this.config.highPrio_PM1OBJ2_active) this.addUserDps(value, objectsSet, this.config.highPrio_PM1OBJ2);
|
|
128
132
|
break;
|
|
129
133
|
case "STATISTIC":
|
|
134
|
+
// soon to be deprecated
|
|
130
135
|
["LIVE_GRID_EXPORT","LIVE_GRID_IMPORT","LIVE_HOUSE_CONS","LIVE_PV_GEN","LIVE_BAT_CHARGE_MASTER","LIVE_BAT_DISCHARGE_MASTER"].forEach(item => objectsSet.add(item));
|
|
131
136
|
if (this.config.disclaimer && this.config.highPrio_STATISTIC_active) this.addUserDps(value, objectsSet, this.config.highPrio_STATISTIC);
|
|
132
137
|
break;
|
|
@@ -260,7 +265,7 @@ class Senec extends utils.Adapter {
|
|
|
260
265
|
this.log.info('connected to Senec AppAPI.');
|
|
261
266
|
apiLoginToken = JSON.parse(body).token;
|
|
262
267
|
apiConnected = true;
|
|
263
|
-
axios.defaults.headers.
|
|
268
|
+
axios.defaults.headers.get['authorization'] = apiLoginToken;
|
|
264
269
|
} catch (error) {
|
|
265
270
|
throw new Error("Error connecting to Senec AppAPI. Exiting! (" + error + ").");
|
|
266
271
|
}
|
|
@@ -285,7 +290,10 @@ class Senec extends utils.Adapter {
|
|
|
285
290
|
const systemId = value.id;
|
|
286
291
|
apiKnownSystems.push(systemId);
|
|
287
292
|
for (const[key2, value2] of Object.entries(value)) {
|
|
288
|
-
|
|
293
|
+
if (typeof value2 === "object")
|
|
294
|
+
this.doState(pfx + systemId + "." + key2, JSON.stringify(value2), "", "", false);
|
|
295
|
+
else
|
|
296
|
+
this.doState(pfx + systemId + "." + key2, value2, "", "", false);
|
|
289
297
|
}
|
|
290
298
|
}
|
|
291
299
|
this.doState(pfx + 'IDs', JSON.stringify(apiKnownSystems), "Anlagen IDs", "", false);
|
|
@@ -382,15 +390,37 @@ class Senec extends utils.Adapter {
|
|
|
382
390
|
* Read values from Senec App API
|
|
383
391
|
*/
|
|
384
392
|
async pollSenecAppApi(retry) {
|
|
385
|
-
|
|
393
|
+
const interval = this.config.api_interval * 60000;
|
|
394
|
+
const dates = new Map([
|
|
395
|
+
["THIS_DAY", new Date().toISOString().split('T')[0]],
|
|
396
|
+
["LAST_DAY", new Date(new Date().setDate(new Date().getDate()-1)).toISOString().split('T')[0]],
|
|
397
|
+
["THIS_MONTH", new Date().toISOString().split('T')[0]],
|
|
398
|
+
["LAST_MONTH", new Date(new Date().setDate(0)).toISOString().split('T')[0]],
|
|
399
|
+
["THIS_YEAR", new Date().toISOString().split('T')[0]],
|
|
400
|
+
["LAST_YEAR", new Date(new Date().getFullYear() - 1, 1, 1).toISOString().split('T')[0]]
|
|
401
|
+
]);
|
|
402
|
+
|
|
386
403
|
this.log.debug("Polling API ...");
|
|
387
404
|
var body = "";
|
|
388
405
|
try {
|
|
389
406
|
for (let i = 0; i < apiKnownSystems.length; i++) {
|
|
407
|
+
const baseUrl = apiSystemsUrl + "/" + apiKnownSystems[i]
|
|
408
|
+
var url = "";
|
|
409
|
+
const tzObj = await this.getStateAsync("_api.Anlagen." + apiKnownSystems[i] + ".zeitzone");
|
|
410
|
+
const tz = tzObj ? encodeURIComponent(tzObj.val) : encodeURIComponent("Europe/Berlin");
|
|
411
|
+
|
|
390
412
|
// dashboard
|
|
391
|
-
|
|
413
|
+
url = baseUrl + "/dashboard";
|
|
392
414
|
body = await this.doGet(url, "", this, this.config.pollingTimeout, false);
|
|
393
415
|
await this.decodeDashboard(apiKnownSystems[i], JSON.parse(body));
|
|
416
|
+
|
|
417
|
+
for (let[key, value] of dates.entries()) {
|
|
418
|
+
// statistik today
|
|
419
|
+
url = baseUrl + "/statistik?periode=" + api_trans[key].api + "&datum=" + value + "&locale=de_DE&timezone=" + tz;
|
|
420
|
+
body = await this.doGet(url, "", this, this.config.pollingTimeout, false);
|
|
421
|
+
await this.decodeStatistik(apiKnownSystems[i], JSON.parse(body), api_trans[key].dp);
|
|
422
|
+
}
|
|
423
|
+
|
|
394
424
|
// // wallboxes - only if wallbox exists? - Without: error 500
|
|
395
425
|
//var url = apiSystemsUrl + "/" + apiKnownSystems[i] + "/wallboxes/1";
|
|
396
426
|
//body = await this.doGet(url, "", this, this.config.pollingTimeout, false);
|
|
@@ -419,12 +449,32 @@ class Senec extends utils.Adapter {
|
|
|
419
449
|
this.doState(pfx + key, value, "", "", false);
|
|
420
450
|
} else {
|
|
421
451
|
for (const[key2, value2] of Object.entries(value)) {
|
|
422
|
-
this.doState(pfx + key + "." + key2, value2.wert, "", value2.einheit, false);
|
|
452
|
+
this.doState(pfx + key + "." + key2, (value2.wert).toFixed(2), "", value2.einheit, false);
|
|
453
|
+
if (kiloList.includes(value2.einheit)) {
|
|
454
|
+
this.doState(pfx + key + "." + key2 + " (k" + value2.einheit + ")", (value2.wert / 1000).toFixed(2), "", "k" + value2.einheit, false);
|
|
455
|
+
}
|
|
423
456
|
}
|
|
424
457
|
}
|
|
425
458
|
}
|
|
426
459
|
|
|
427
460
|
}
|
|
461
|
+
|
|
462
|
+
async decodeStatistik(system, obj, period) {
|
|
463
|
+
const pfx = "_api.Anlagen." + system + ".Statistik." + period + ".";
|
|
464
|
+
for (const[key, value] of Object.entries(obj.aggregation)) {
|
|
465
|
+
// only reading 'aggregation' - no interest in fine granular information
|
|
466
|
+
if (key == "startzeitpunkt") {
|
|
467
|
+
this.doState(pfx + key, value, "", "", false);
|
|
468
|
+
} else {
|
|
469
|
+
this.doState(pfx + key, (value.wert).toFixed(2), "", value.einheit, false);
|
|
470
|
+
if (kiloList.includes(value.einheit)) {
|
|
471
|
+
this.doState(pfx + key + " (k"+ value.einheit + ")", (value.wert / 1000).toFixed(2), "", "k" + value.einheit, false);
|
|
472
|
+
}
|
|
473
|
+
}
|
|
474
|
+
}
|
|
475
|
+
const autarky = (((obj.aggregation.stromerzeugung.wert - obj.aggregation.netzeinspeisung.wert - obj.aggregation.speicherbeladung.wert + obj.aggregation.speicherentnahme.wert) / obj.aggregation.stromverbrauch.wert) * 100).toFixed(2);
|
|
476
|
+
this.doState(pfx + "Autarkie", autarky, "", "%", false);
|
|
477
|
+
}
|
|
428
478
|
|
|
429
479
|
/**
|
|
430
480
|
* sets a state's value and creates the state if it doesn't exist yet
|
|
@@ -481,7 +531,7 @@ class Senec extends utils.Adapter {
|
|
|
481
531
|
val: value,
|
|
482
532
|
ack: true
|
|
483
533
|
});
|
|
484
|
-
await this.checkUpdateSelfStat(name);
|
|
534
|
+
await this.checkUpdateSelfStat(name); // soon to be deprecated
|
|
485
535
|
await this.doDecode(name, value);
|
|
486
536
|
}
|
|
487
537
|
|
|
@@ -512,6 +562,7 @@ class Senec extends utils.Adapter {
|
|
|
512
562
|
* Helper routine
|
|
513
563
|
*/
|
|
514
564
|
async checkUpdateSelfStat(name) {
|
|
565
|
+
// soon to be deprecated
|
|
515
566
|
if (name === "STATISTIC.LIVE_GRID_EXPORT" || name === "STATISTIC.LIVE_GRID_IMPORT" || name === "STATISTIC.LIVE_HOUSE_CONS" || name === "STATISTIC.LIVE_PV_GEN" || name === "STATISTIC.LIVE_BAT_CHARGE_MASTER" || name === "STATISTIC.LIVE_BAT_DISCHARGE_MASTER") {
|
|
516
567
|
await this.updateSelfStat(name);
|
|
517
568
|
}
|
|
@@ -546,6 +597,7 @@ class Senec extends utils.Adapter {
|
|
|
546
597
|
}
|
|
547
598
|
|
|
548
599
|
async updateSelfStat(name, value) {
|
|
600
|
+
// soon to be deprecated
|
|
549
601
|
await this.updateSelfStatHelper(name, value, ".today", ".yesterday", ".refValue", "Day", getCurDay());
|
|
550
602
|
await this.updateSelfStatHelper(name, value, ".week", ".lastWeek", ".refValueWeek", "Week", getCurWeek());
|
|
551
603
|
await this.updateSelfStatHelper(name, value, ".month", ".lastMonth", ".refValueMonth", "Month", getCurMonth());
|
|
@@ -554,6 +606,7 @@ class Senec extends utils.Adapter {
|
|
|
554
606
|
}
|
|
555
607
|
|
|
556
608
|
async updateSelfStatHelper(name, value, today, yesterday, refValue, day, curDay) {
|
|
609
|
+
// soon to be deprecated
|
|
557
610
|
const key = "_calc." + name.substring(10);
|
|
558
611
|
|
|
559
612
|
const refDayObj = await this.getStateAsync(key + ".ref" + day);
|
|
@@ -598,6 +651,7 @@ class Senec extends utils.Adapter {
|
|
|
598
651
|
}
|
|
599
652
|
|
|
600
653
|
async updateAutarkyHelper(today, yesterday, day, curDay) {
|
|
654
|
+
// soon to be deprecated
|
|
601
655
|
const key = "_calc.Autarky";
|
|
602
656
|
|
|
603
657
|
// reference object to decide on change of day
|