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 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.0",
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": {
@@ -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.common['Content-Type'] = "application/json";
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.common['authorization'] = apiLoginToken;
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
- this.doState(pfx + systemId + "." + key2, JSON.stringify(value2), "", "", false);
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
- var interval = this.config.api_interval * 60000;
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
- var url = apiSystemsUrl + "/" + apiKnownSystems[i] + "/dashboard";
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
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "iobroker.senec",
3
- "version": "1.6.0",
3
+ "version": "1.6.2",
4
4
  "description": "Senec Home",
5
5
  "author": {
6
6
  "name": "NoBl",