iobroker.senec 1.6.1 → 1.6.3
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 +65 -6
- 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.3 (NoBl)
|
|
370
|
+
* Code optimization
|
|
371
|
+
|
|
372
|
+
### 1.6.2 (NoBl)
|
|
373
|
+
* Added statistics values from API along with some own calculations.
|
|
374
|
+
|
|
369
375
|
### 1.6.1 (NoBl)
|
|
370
376
|
* Bugfixes
|
|
371
377
|
|
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.3",
|
|
5
5
|
"news": {
|
|
6
|
+
"1.6.3": {
|
|
7
|
+
"en": "Bugfixes for API reading.",
|
|
8
|
+
"de": "Bugfixes im API Handling.",
|
|
9
|
+
"ru": "Bugfixes для чтения API.",
|
|
10
|
+
"pt": "Bugfixes para leitura de API.",
|
|
11
|
+
"nl": "Bugfixes voor API-lezing.",
|
|
12
|
+
"fr": "Bugfixes pour la lecture de l'API.",
|
|
13
|
+
"it": "Bugfixes per la lettura API.",
|
|
14
|
+
"es": "Bugfixes para la lectura de API.",
|
|
15
|
+
"pl": "Bugfixes for API reading (ang.).",
|
|
16
|
+
"uk": "Виправлення помилок для читання API.",
|
|
17
|
+
"zh-cn": "评 注."
|
|
18
|
+
},
|
|
19
|
+
"1.6.2": {
|
|
20
|
+
"en": "Added statistics values from API along with some own calculations.",
|
|
21
|
+
"de": "Statistikwerte von API zusammen mit einigen eigenen Berechnungen hinzugefügt.",
|
|
22
|
+
"ru": "Добавлены статистические значения из API наряду с некоторыми собственными расчетами.",
|
|
23
|
+
"pt": "Adicionados valores estatísticos da API junto com alguns próprios cálculos.",
|
|
24
|
+
"nl": "Voeg statistieken toe van API samen met wat eigen berekeningen.",
|
|
25
|
+
"fr": "Ajout des valeurs statistiques de l'API avec certains propres calculs.",
|
|
26
|
+
"it": "Aggiunti i valori statistici da API con alcuni propri calcoli.",
|
|
27
|
+
"es": "Valores estadísticos añadidos de API junto con algunos cálculos propios.",
|
|
28
|
+
"pl": "Wstępne dane z API wraz z kilkoma własnymi obliczeniami.",
|
|
29
|
+
"uk": "Додано значення статистичних даних з API разом з деякими власними підрахунками.",
|
|
30
|
+
"zh-cn": "加上非传染性疾病的统计价值以及一些本身的计算。."
|
|
31
|
+
},
|
|
6
32
|
"1.6.1": {
|
|
7
33
|
"en": "Bugfixes for API reading.",
|
|
8
34
|
"de": "Bugfixes im API Handling.",
|
|
@@ -93,32 +119,6 @@
|
|
|
93
119
|
"pl": "Dodana opcja do używania https dla połączenia z SENEC (w sposób aktywowana, jeśli dana osoba obsługuje / wymaga to)",
|
|
94
120
|
"uk": "Додано можливість використовувати HTTPS для підключення до SENEC (тільки активуйте, якщо підтримує додаток / вимагає цього)",
|
|
95
121
|
"zh-cn": "增加使用连接到SENEC的网站的选择(如果你的可靠支持/要求这样做)"
|
|
96
|
-
},
|
|
97
|
-
"1.4.1": {
|
|
98
|
-
"en": "Autarky calculations are working again.",
|
|
99
|
-
"de": "Autarkieberechnungen funktionieren wieder.",
|
|
100
|
-
"ru": "Расчеты Autarky снова работают.",
|
|
101
|
-
"pt": "Cálculos autarcos estão a funcionar outra vez.",
|
|
102
|
-
"nl": "Autarische berekeningen werken weer.",
|
|
103
|
-
"fr": "Les calculs Autarky fonctionnent à nouveau.",
|
|
104
|
-
"it": "I calcoli autarky funzionano di nuovo.",
|
|
105
|
-
"es": "Los cálculos autárquicos están funcionando de nuevo.",
|
|
106
|
-
"pl": "Ponowne obliczenia autarktyczne działają ponownie.",
|
|
107
|
-
"uk": "Аутарки знову працюють.",
|
|
108
|
-
"zh-cn": "Autarky的计算正再次工作。."
|
|
109
|
-
},
|
|
110
|
-
"1.4.0": {
|
|
111
|
-
"en": "Added object caching and minor code updates. Due to the amount of objects we deal with caching is about mandatory.",
|
|
112
|
-
"de": "Objekt-Caching und kleinere Code-Updates hinzugefügt. Aufgrund der Menge an Objekten, die der Adapter bearbeitet, ist Caching zwingend notwendig.",
|
|
113
|
-
"ru": "Добавлена кэшировка объекта и незначительные обновления кода. В связи с количеством объектов, с которыми мы занимаемся кэшированием, является обязательным.",
|
|
114
|
-
"pt": "Adicionado cache de objeto e pequenas atualizações de código. Devido à quantidade de objetos que lidamos com caching é sobre obrigatória.",
|
|
115
|
-
"nl": "Toegevoegd voorwerp caching en kleine code updates. Gezien de hoeveelheid objecten die we met caching hebben, gaat het over verplichting.",
|
|
116
|
-
"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.",
|
|
117
|
-
"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.",
|
|
118
|
-
"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.",
|
|
119
|
-
"pl": "Dodany obiekt caching i drobnych aktualizacji kodu. Ze względu na ilość przedmiotów, które traktują z cachingiem, dotyczy obowiązkowych.",
|
|
120
|
-
"uk": "Додано кешування об'єкта та оновлення коду неповного розміру. У зв'язку з кількістю об'єктів, які ми маємо справу з кешуванням, є обов'язковим.",
|
|
121
|
-
"zh-cn": "添加了目标包和小法典的更新。 由于我们所处理的物品数量是强制性的。."
|
|
122
122
|
}
|
|
123
123
|
},
|
|
124
124
|
"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
|
@@ -14,6 +14,9 @@ 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 = "";
|
|
@@ -65,7 +69,7 @@ class Senec extends utils.Adapter {
|
|
|
65
69
|
if (apiConnected) await this.getApiSystems();
|
|
66
70
|
await this.pollSenec(true, 0); // highPrio
|
|
67
71
|
await this.pollSenec(false, 0); // lowPrio
|
|
68
|
-
await this.pollSenecAppApi(0); // App API
|
|
72
|
+
if (apiConnected) await this.pollSenecAppApi(0); // App API
|
|
69
73
|
this.setState('info.connection', true, true);
|
|
70
74
|
} catch (error) {
|
|
71
75
|
this.log.error(error);
|
|
@@ -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;
|
|
@@ -262,6 +267,7 @@ class Senec extends utils.Adapter {
|
|
|
262
267
|
apiConnected = true;
|
|
263
268
|
axios.defaults.headers.get['authorization'] = apiLoginToken;
|
|
264
269
|
} catch (error) {
|
|
270
|
+
apiConnected = false;
|
|
265
271
|
throw new Error("Error connecting to Senec AppAPI. Exiting! (" + error + ").");
|
|
266
272
|
}
|
|
267
273
|
}
|
|
@@ -285,7 +291,10 @@ class Senec extends utils.Adapter {
|
|
|
285
291
|
const systemId = value.id;
|
|
286
292
|
apiKnownSystems.push(systemId);
|
|
287
293
|
for (const[key2, value2] of Object.entries(value)) {
|
|
288
|
-
|
|
294
|
+
if (typeof value2 === "object")
|
|
295
|
+
this.doState(pfx + systemId + "." + key2, JSON.stringify(value2), "", "", false);
|
|
296
|
+
else
|
|
297
|
+
this.doState(pfx + systemId + "." + key2, value2, "", "", false);
|
|
289
298
|
}
|
|
290
299
|
}
|
|
291
300
|
this.doState(pfx + 'IDs', JSON.stringify(apiKnownSystems), "Anlagen IDs", "", false);
|
|
@@ -382,15 +391,41 @@ class Senec extends utils.Adapter {
|
|
|
382
391
|
* Read values from Senec App API
|
|
383
392
|
*/
|
|
384
393
|
async pollSenecAppApi(retry) {
|
|
385
|
-
|
|
394
|
+
if (!this.config.api_use || !apiConnected) {
|
|
395
|
+
this.log.info('Usage of SENEC App API not configured or not connected.');
|
|
396
|
+
return;
|
|
397
|
+
}
|
|
398
|
+
const interval = this.config.api_interval * 60000;
|
|
399
|
+
const dates = new Map([
|
|
400
|
+
["THIS_DAY", new Date().toISOString().split('T')[0]],
|
|
401
|
+
["LAST_DAY", new Date(new Date().setDate(new Date().getDate()-1)).toISOString().split('T')[0]],
|
|
402
|
+
["THIS_MONTH", new Date().toISOString().split('T')[0]],
|
|
403
|
+
["LAST_MONTH", new Date(new Date().setDate(0)).toISOString().split('T')[0]],
|
|
404
|
+
["THIS_YEAR", new Date().toISOString().split('T')[0]],
|
|
405
|
+
["LAST_YEAR", new Date(new Date().getFullYear() - 1, 1, 1).toISOString().split('T')[0]]
|
|
406
|
+
]);
|
|
407
|
+
|
|
386
408
|
this.log.debug("Polling API ...");
|
|
387
409
|
var body = "";
|
|
388
410
|
try {
|
|
389
411
|
for (let i = 0; i < apiKnownSystems.length; i++) {
|
|
412
|
+
const baseUrl = apiSystemsUrl + "/" + apiKnownSystems[i]
|
|
413
|
+
var url = "";
|
|
414
|
+
const tzObj = await this.getStateAsync("_api.Anlagen." + apiKnownSystems[i] + ".zeitzone");
|
|
415
|
+
const tz = tzObj ? encodeURIComponent(tzObj.val) : encodeURIComponent("Europe/Berlin");
|
|
416
|
+
|
|
390
417
|
// dashboard
|
|
391
|
-
|
|
418
|
+
url = baseUrl + "/dashboard";
|
|
392
419
|
body = await this.doGet(url, "", this, this.config.pollingTimeout, false);
|
|
393
420
|
await this.decodeDashboard(apiKnownSystems[i], JSON.parse(body));
|
|
421
|
+
|
|
422
|
+
for (let[key, value] of dates.entries()) {
|
|
423
|
+
// statistik today
|
|
424
|
+
url = baseUrl + "/statistik?periode=" + api_trans[key].api + "&datum=" + value + "&locale=de_DE&timezone=" + tz;
|
|
425
|
+
body = await this.doGet(url, "", this, this.config.pollingTimeout, false);
|
|
426
|
+
await this.decodeStatistik(apiKnownSystems[i], JSON.parse(body), api_trans[key].dp);
|
|
427
|
+
}
|
|
428
|
+
|
|
394
429
|
// // wallboxes - only if wallbox exists? - Without: error 500
|
|
395
430
|
//var url = apiSystemsUrl + "/" + apiKnownSystems[i] + "/wallboxes/1";
|
|
396
431
|
//body = await this.doGet(url, "", this, this.config.pollingTimeout, false);
|
|
@@ -419,12 +454,32 @@ class Senec extends utils.Adapter {
|
|
|
419
454
|
this.doState(pfx + key, value, "", "", false);
|
|
420
455
|
} else {
|
|
421
456
|
for (const[key2, value2] of Object.entries(value)) {
|
|
422
|
-
this.doState(pfx + key + "." + key2, value2.wert, "", value2.einheit, false);
|
|
457
|
+
this.doState(pfx + key + "." + key2, (value2.wert).toFixed(2), "", value2.einheit, false);
|
|
458
|
+
if (kiloList.includes(value2.einheit)) {
|
|
459
|
+
this.doState(pfx + key + "." + key2 + " (k" + value2.einheit + ")", (value2.wert / 1000).toFixed(2), "", "k" + value2.einheit, false);
|
|
460
|
+
}
|
|
423
461
|
}
|
|
424
462
|
}
|
|
425
463
|
}
|
|
426
464
|
|
|
427
465
|
}
|
|
466
|
+
|
|
467
|
+
async decodeStatistik(system, obj, period) {
|
|
468
|
+
const pfx = "_api.Anlagen." + system + ".Statistik." + period + ".";
|
|
469
|
+
for (const[key, value] of Object.entries(obj.aggregation)) {
|
|
470
|
+
// only reading 'aggregation' - no interest in fine granular information
|
|
471
|
+
if (key == "startzeitpunkt") {
|
|
472
|
+
this.doState(pfx + key, value, "", "", false);
|
|
473
|
+
} else {
|
|
474
|
+
this.doState(pfx + key, (value.wert).toFixed(2), "", value.einheit, false);
|
|
475
|
+
if (kiloList.includes(value.einheit)) {
|
|
476
|
+
this.doState(pfx + key + " (k"+ value.einheit + ")", (value.wert / 1000).toFixed(2), "", "k" + value.einheit, false);
|
|
477
|
+
}
|
|
478
|
+
}
|
|
479
|
+
}
|
|
480
|
+
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);
|
|
481
|
+
this.doState(pfx + "Autarkie", autarky, "", "%", false);
|
|
482
|
+
}
|
|
428
483
|
|
|
429
484
|
/**
|
|
430
485
|
* sets a state's value and creates the state if it doesn't exist yet
|
|
@@ -481,7 +536,7 @@ class Senec extends utils.Adapter {
|
|
|
481
536
|
val: value,
|
|
482
537
|
ack: true
|
|
483
538
|
});
|
|
484
|
-
await this.checkUpdateSelfStat(name);
|
|
539
|
+
await this.checkUpdateSelfStat(name); // soon to be deprecated
|
|
485
540
|
await this.doDecode(name, value);
|
|
486
541
|
}
|
|
487
542
|
|
|
@@ -512,6 +567,7 @@ class Senec extends utils.Adapter {
|
|
|
512
567
|
* Helper routine
|
|
513
568
|
*/
|
|
514
569
|
async checkUpdateSelfStat(name) {
|
|
570
|
+
// soon to be deprecated
|
|
515
571
|
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
572
|
await this.updateSelfStat(name);
|
|
517
573
|
}
|
|
@@ -546,6 +602,7 @@ class Senec extends utils.Adapter {
|
|
|
546
602
|
}
|
|
547
603
|
|
|
548
604
|
async updateSelfStat(name, value) {
|
|
605
|
+
// soon to be deprecated
|
|
549
606
|
await this.updateSelfStatHelper(name, value, ".today", ".yesterday", ".refValue", "Day", getCurDay());
|
|
550
607
|
await this.updateSelfStatHelper(name, value, ".week", ".lastWeek", ".refValueWeek", "Week", getCurWeek());
|
|
551
608
|
await this.updateSelfStatHelper(name, value, ".month", ".lastMonth", ".refValueMonth", "Month", getCurMonth());
|
|
@@ -554,6 +611,7 @@ class Senec extends utils.Adapter {
|
|
|
554
611
|
}
|
|
555
612
|
|
|
556
613
|
async updateSelfStatHelper(name, value, today, yesterday, refValue, day, curDay) {
|
|
614
|
+
// soon to be deprecated
|
|
557
615
|
const key = "_calc." + name.substring(10);
|
|
558
616
|
|
|
559
617
|
const refDayObj = await this.getStateAsync(key + ".ref" + day);
|
|
@@ -598,6 +656,7 @@ class Senec extends utils.Adapter {
|
|
|
598
656
|
}
|
|
599
657
|
|
|
600
658
|
async updateAutarkyHelper(today, yesterday, day, curDay) {
|
|
659
|
+
// soon to be deprecated
|
|
601
660
|
const key = "_calc.Autarky";
|
|
602
661
|
|
|
603
662
|
// reference object to decide on change of day
|