iobroker.senec 1.6.3 → 1.6.5

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,13 @@ 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.5 (NoBl)
370
+ * Added AllTime Statistics (trigger initial calculations in adapter settings)
371
+ * https is now default for new instances
372
+
373
+ ### 1.6.4 (NoBl)
374
+ * Bugfix (numbers are numbers again)
375
+
369
376
  ### 1.6.3 (NoBl)
370
377
  * Code optimization
371
378
 
@@ -83,7 +83,7 @@
83
83
  "uk": "Чи потрібен HTTPS для підключення до SENEC? Тільки перевірте, чи підтримує SENEC HTTPS вже!",
84
84
  "zh-cn": "是否需要与ENEC联系? 只有检查有无国界协会支持已经!"
85
85
  },
86
- "default": false,
86
+ "default": true,
87
87
  "newLine": false
88
88
  },
89
89
  "interval": {
@@ -1242,6 +1242,40 @@
1242
1242
  "max": 1440,
1243
1243
  "newLine": true
1244
1244
  },
1245
+ "api_alltimeRebuild": {
1246
+ "type": "checkbox",
1247
+ "sm": 12,
1248
+ "md": 6,
1249
+ "lg": 6,
1250
+ "label": {
1251
+ "en": "Rebuild AllTime history (one-time)",
1252
+ "de": "Gesamthistorie neu berechnen (einmalig)",
1253
+ "ru": "Восстановление истории AllTime (один раз)",
1254
+ "pt": "Reconstruir história AllTime (um tempo)",
1255
+ "nl": "Herbouw AllTime geschiedenis (1-time)",
1256
+ "fr": "Rebuild AllTime history (une fois)",
1257
+ "it": "Ricostruire la storia di AllTime (una volta)",
1258
+ "es": "Recuperar historia de AllTime (una sola vez)",
1259
+ "pl": "Rebuild All Time history (ang.)",
1260
+ "uk": "Історія (один раз)",
1261
+ "zh-cn": "废除所有制度的历史(一次性)"
1262
+ },
1263
+ "help": {
1264
+ "en": "Check this if you want the adapter to rebuild history values back to date of installation.",
1265
+ "de": "Aktivieren, falls der Adapter die Historienwerte seit Installation neu berechnen soll.",
1266
+ "ru": "Проверьте это, если вы хотите, чтобы адаптер восстановить значения истории обратно к дате установки.",
1267
+ "pt": "Verifique se deseja que o adaptador reconstrua os valores do histórico de volta à data da instalação.",
1268
+ "nl": "Controleer dit als je wilt dat de adapter de geschiedeniswaarden herbouwt tot datum van installatie.",
1269
+ "fr": "Vérifiez ceci si vous voulez que l'adaptateur reconstruise les valeurs d'histoire à la date d'installation.",
1270
+ "it": "Controllare questo se si desidera che l'adattatore per ricostruire i valori storici fino alla data di installazione.",
1271
+ "es": "Revise esto si desea que el adaptador reconstruya los valores de historia hasta la fecha de instalación.",
1272
+ "pl": "Umożliwia to, czy chcesz adapterowi odbudować wartości historyczne do daty instalacji.",
1273
+ "uk": "Перевірте це, якщо ви хочете перебудувати значення історії назад до дати установки.",
1274
+ "zh-cn": "如果你想适应者重建历史价值,那么我们就会感到惊讶。."
1275
+ },
1276
+ "default": false,
1277
+ "newLine": true
1278
+ },
1245
1279
  }
1246
1280
  }
1247
1281
  }
package/io-package.json CHANGED
@@ -1,8 +1,34 @@
1
1
  {
2
2
  "common": {
3
3
  "name": "senec",
4
- "version": "1.6.3",
4
+ "version": "1.6.5",
5
5
  "news": {
6
+ "1.6.5": {
7
+ "en": "Added AllTime Statistics (trigger initial calculations in adapter settings), https is now default for new instances.",
8
+ "de": "AllTime Statistiken hinzugefügt (initiale Berechnung bitte in den Adapter-Einstellungen aktivieren), https ist jetzt Standard für neue Instanzen.",
9
+ "ru": "Добавлена статистика AllTime (trigger начальные расчеты в настройках адаптера), https теперь по умолчанию для новых инстанций.",
10
+ "pt": "Adicionado AllTime Statistics ( cálculos iniciais de gatilho em configurações de adaptador), https agora é padrão para novas instâncias.",
11
+ "nl": "Toegevoegd AllTime Statistieken (trigger initialen berekeningen in adapter settings\" \"htps is nu default voor nieuwe instanties.",
12
+ "fr": "Ajout de AllTime Statistics (trigger initial calculations in adapt settings), https est maintenant par défaut pour de nouveaux cas.",
13
+ "it": "Aggiunta AllTime Statistics (configurazioni iniziali del trigger nelle impostazioni dell'adattatore), https è ora predefinito per nuove istanze.",
14
+ "es": "Añadido AllTime Statistics (trigger inicial calculations in adapter settings), https ahora es predeterminado para nuevas instancias.",
15
+ "pl": "Added AllTime Statistics (trigger początkowych obliczeń w ustawieniach adapterów), https jest obecnie domyślny do nowych instancji.",
16
+ "uk": "Додано AllTime Статистика (тригер початкових обчислень в налаштуваннях адаптера), HTTPS тепер за замовчуванням для нових екземплярів.",
17
+ "zh-cn": "加上《所有制度统计》(在适应环境中的过份初步计算),目前,新情况目前有缺陷。."
18
+ },
19
+ "1.6.4": {
20
+ "en": "Numbers are numbers again",
21
+ "de": "Zahlen sind wieder Zahlen",
22
+ "ru": "Номера снова",
23
+ "pt": "Os números são números novamente",
24
+ "nl": "Nummers zijn weer getallen",
25
+ "fr": "Les nombres sont de nouveau des nombres",
26
+ "it": "I numeri sono di nuovo numeri",
27
+ "es": "Números son números de nuevo",
28
+ "pl": "Liczby są ponownie numerami",
29
+ "uk": "Кількість чисел знову",
30
+ "zh-cn": "数字再次编号"
31
+ },
6
32
  "1.6.3": {
7
33
  "en": "Bugfixes for API reading.",
8
34
  "de": "Bugfixes im API Handling.",
@@ -192,7 +218,7 @@
192
218
  "pollingTimeout": 5000,
193
219
  "retries": 10,
194
220
  "retrymultiplier": 2,
195
- "useHttps": false,
221
+ "useHttps": true,
196
222
  "disclaimer": false,
197
223
  "highPrio_BMS": "",
198
224
  "highPrio_BMS_active": false,
@@ -223,7 +249,8 @@
223
249
  "highPrio_TEMPMEASURE": "",
224
250
  "highPrio_TEMPMEASURE_active": false,
225
251
  "api_use": false,
226
- "api_interval": 5
252
+ "api_interval": 5,
253
+ "api_alltimeRebuild": false
227
254
  },
228
255
  "nativeEncrypted": {
229
256
  "api_mail": "",
package/main.js CHANGED
@@ -292,12 +292,12 @@ class Senec extends utils.Adapter {
292
292
  apiKnownSystems.push(systemId);
293
293
  for (const[key2, value2] of Object.entries(value)) {
294
294
  if (typeof value2 === "object")
295
- this.doState(pfx + systemId + "." + key2, JSON.stringify(value2), "", "", false);
295
+ await this.doState(pfx + systemId + "." + key2, JSON.stringify(value2), "", "", false);
296
296
  else
297
- this.doState(pfx + systemId + "." + key2, value2, "", "", false);
297
+ await this.doState(pfx + systemId + "." + key2, value2, "", "", false);
298
298
  }
299
299
  }
300
- this.doState(pfx + 'IDs', JSON.stringify(apiKnownSystems), "Anlagen IDs", "", false);
300
+ await this.doState(pfx + 'IDs', JSON.stringify(apiKnownSystems), "Anlagen IDs", "", false);
301
301
  } catch (error) {
302
302
  throw new Error("Error reading Systems Information from Senec AppAPI. (" + error + ").");
303
303
  }
@@ -409,7 +409,7 @@ class Senec extends utils.Adapter {
409
409
  var body = "";
410
410
  try {
411
411
  for (let i = 0; i < apiKnownSystems.length; i++) {
412
- const baseUrl = apiSystemsUrl + "/" + apiKnownSystems[i]
412
+ const baseUrl = apiSystemsUrl + "/" + apiKnownSystems[i];
413
413
  var url = "";
414
414
  const tzObj = await this.getStateAsync("_api.Anlagen." + apiKnownSystems[i] + ".zeitzone");
415
415
  const tz = tzObj ? encodeURIComponent(tzObj.val) : encodeURIComponent("Europe/Berlin");
@@ -420,17 +420,14 @@ class Senec extends utils.Adapter {
420
420
  await this.decodeDashboard(apiKnownSystems[i], JSON.parse(body));
421
421
 
422
422
  for (let[key, value] of dates.entries()) {
423
- // statistik today
423
+ // statistik for period
424
424
  url = baseUrl + "/statistik?periode=" + api_trans[key].api + "&datum=" + value + "&locale=de_DE&timezone=" + tz;
425
425
  body = await this.doGet(url, "", this, this.config.pollingTimeout, false);
426
426
  await this.decodeStatistik(apiKnownSystems[i], JSON.parse(body), api_trans[key].dp);
427
427
  }
428
428
 
429
- // // wallboxes - only if wallbox exists? - Without: error 500
430
- //var url = apiSystemsUrl + "/" + apiKnownSystems[i] + "/wallboxes/1";
431
- //body = await this.doGet(url, "", this, this.config.pollingTimeout, false);
432
- //this.log.info("Abilities: " + body);
433
- //await this.decodeWallbox(apiKnownSystems[i], JSON.parse(body));
429
+ if (this.config.api_alltimeRebuild) await this.rebuildAllTimeHistory(apiKnownSystems[i]);
430
+
434
431
  }
435
432
  retry = 0;
436
433
  if (unloaded) return;
@@ -447,16 +444,19 @@ class Senec extends utils.Adapter {
447
444
  }
448
445
  }
449
446
 
447
+ /**
448
+ * Decodes Dashboard information from SENEC App API
449
+ */
450
450
  async decodeDashboard(system, obj) {
451
451
  const pfx = "_api.Anlagen." + system + ".Dashboard.";
452
452
  for (const[key, value] of Object.entries(obj)) {
453
453
  if (key == "zeitstempel" || key == "electricVehicleConnected") {
454
- this.doState(pfx + key, value, "", "", false);
454
+ await this.doState(pfx + key, value, "", "", false);
455
455
  } else {
456
456
  for (const[key2, value2] of Object.entries(value)) {
457
- this.doState(pfx + key + "." + key2, (value2.wert).toFixed(2), "", value2.einheit, false);
457
+ await this.doState(pfx + key + "." + key2, Number((value2.wert).toFixed(2)), "", value2.einheit, false);
458
458
  if (kiloList.includes(value2.einheit)) {
459
- this.doState(pfx + key + "." + key2 + " (k" + value2.einheit + ")", (value2.wert / 1000).toFixed(2), "", "k" + value2.einheit, false);
459
+ await this.doState(pfx + key + "." + key2 + " (k" + value2.einheit + ")", Number((value2.wert / 1000).toFixed(2)), "", "k" + value2.einheit, false);
460
460
  }
461
461
  }
462
462
  }
@@ -464,21 +464,105 @@ class Senec extends utils.Adapter {
464
464
 
465
465
  }
466
466
 
467
+ /**
468
+ * Decodes Statistik information from SENEC App API
469
+ */
467
470
  async decodeStatistik(system, obj, period) {
468
471
  const pfx = "_api.Anlagen." + system + ".Statistik." + period + ".";
469
472
  for (const[key, value] of Object.entries(obj.aggregation)) {
470
473
  // only reading 'aggregation' - no interest in fine granular information
471
474
  if (key == "startzeitpunkt") {
472
- this.doState(pfx + key, value, "", "", false);
475
+ await this.doState(pfx + key, value, "", "", false);
473
476
  } 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
+ if (!this.config.api_alltimeRebuild) { // don't update DPs if we are AllTime-Rebuild-Process
478
+ await this.doState(pfx + key, Number((value.wert).toFixed(2)), "", value.einheit, false);
479
+ if (kiloList.includes(value.einheit)) {
480
+ await this.doState(pfx + key + " (k"+ value.einheit + ")", Number((value.wert / 1000).toFixed(2)), "", "k" + value.einheit, false);
481
+ }
482
+ }
483
+ if (period == api_trans["THIS_YEAR"].dp) await this.insertAllTimeHistory(system, key, new Date(obj.aggregation.startzeitpunkt).getFullYear(), Number((value.wert).toFixed(0)), value.einheit);
484
+ }
485
+ }
486
+ const autarky = Number((((obj.aggregation.stromerzeugung.wert - obj.aggregation.netzeinspeisung.wert - obj.aggregation.speicherbeladung.wert + obj.aggregation.speicherentnahme.wert) / obj.aggregation.stromverbrauch.wert) * 100).toFixed(2));
487
+ await this.doState(pfx + "Autarkie", autarky, "", "%", false);
488
+ await this.updateAllTimeHistory(system);
489
+ }
490
+
491
+ /**
492
+ * inserts a value for a given key and year into AllTimeValueStore
493
+ */
494
+ async insertAllTimeHistory(system, key, year, value, einheit) {
495
+ const pfx = "_api.Anlagen." + system + ".Statistik.AllTime.";
496
+ const valueStore = pfx + "valueStore";
497
+ const statsObj = await this.getStateAsync(valueStore);
498
+ const stats = statsObj ? JSON.parse(statsObj.val) : {};
499
+ if (!stats[key]) stats[key] = {};
500
+ if (!stats[key][year]) stats[key][year] = {};
501
+ stats[key][year] = value;
502
+ stats[key]["einheit"] = einheit;
503
+ await this.doState(valueStore, JSON.stringify(stats), "", "", false);
504
+ }
505
+
506
+ /**
507
+ * Updated AllTimeHistory based on what we have in our AllTimeValueStore
508
+ */
509
+ async updateAllTimeHistory(system) {
510
+ const pfx = "_api.Anlagen." + system + ".Statistik.AllTime.";
511
+ const valueStore = pfx + "valueStore";
512
+ const statsObj = await this.getStateAsync(valueStore);
513
+ const stats = statsObj ? JSON.parse(statsObj.val) : {};
514
+ const sums = {};
515
+ for (const[key, value] of Object.entries(stats)) {
516
+ var einheit = "";
517
+ var sum = 0.0;
518
+ for (const[key2, value2] of Object.entries(value)) {
519
+ if (key2 == "einheit") {
520
+ einheit = value2;
521
+ } else {
522
+ sum += value2;
477
523
  }
478
524
  }
525
+ sums[key] = sum;
526
+ if (kiloList.includes(einheit)) {
527
+ await this.doState(pfx + key, Number((sum / 1000).toFixed(0)), "", "k" + einheit, false);
528
+ } else {
529
+ await this.doState(pfx + key, Number(sum.toFixed(0)), "", einheit, false);
530
+ }
531
+ }
532
+ const autarky = Number((((sums.stromerzeugung - sums.netzeinspeisung - sums.speicherbeladung + sums.speicherentnahme) / sums.stromverbrauch) * 100).toFixed(0));
533
+ await this.doState(pfx + "Autarkie", autarky, "", "%", false);
534
+ }
535
+
536
+ /**
537
+ * Rebuilds AllTimeHistory from SENEC App API
538
+ */
539
+ async rebuildAllTimeHistory(system) {
540
+ if (!this.config.api_use || !apiConnected) {
541
+ this.log.info('Usage of SENEC App API not configured or not connected.');
542
+ return;
479
543
  }
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);
544
+
545
+ this.log.info("Rebuilding AllTime History ...");
546
+ var year = new Date(new Date().getFullYear() - 1, 1, 1).toISOString().split('T')[0]; // starting last year, because we already got current year covered
547
+ var body = "";
548
+ try {
549
+ while (new Date(year).getFullYear() > 2008) { // senec was founded in 2009 by Mathias Hammer as Deutsche Energieversorgung GmbH (DEV) - so no way we have older data :)
550
+ this.log.info("Rebuilding AllTime History - Year: " + new Date(year).getFullYear());
551
+ const baseUrl = apiSystemsUrl + "/" + system
552
+ var url = "";
553
+ const tzObj = await this.getStateAsync("_api.Anlagen." + system + ".zeitzone");
554
+ const tz = tzObj ? encodeURIComponent(tzObj.val) : encodeURIComponent("Europe/Berlin");
555
+ url = baseUrl + "/statistik?periode=JAHR&datum=" + year + "&locale=de_DE&timezone=" + tz;
556
+ body = await this.doGet(url, "", this, this.config.pollingTimeout, false);
557
+ await this.decodeStatistik(system, JSON.parse(body), api_trans["THIS_YEAR"].dp);
558
+ year = new Date(new Date(year).getFullYear() - 1, 1, 1).toISOString().split('T')[0];
559
+ if (unloaded) return;
560
+ }
561
+ } catch (error) {
562
+ this.log.info("Rebuild ended.");
563
+ }
564
+ this.log.info("Restarting ...");
565
+ this.extendForeignObject(`system.adapter.${this.namespace}`, {native: {api_alltimeRebuild: false}});
482
566
  }
483
567
 
484
568
  /**
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "iobroker.senec",
3
- "version": "1.6.3",
3
+ "version": "1.6.5",
4
4
  "description": "Senec Home",
5
5
  "author": {
6
6
  "name": "NoBl",
@@ -35,21 +35,21 @@
35
35
  "@iobroker/adapter-dev": "^1.2.0",
36
36
  "@iobroker/testing": "^4.1.0",
37
37
  "@tsconfig/node14": "^14.1.0",
38
- "@types/chai": "^4.3.5",
39
- "@types/chai-as-promised": "^7.1.5",
38
+ "@types/chai": "^4.3.6",
39
+ "@types/chai-as-promised": "^7.1.6",
40
40
  "@types/mocha": "^10.0.1",
41
- "@types/node": "^20.5.7",
41
+ "@types/node": "^20.5.9",
42
42
  "@types/proxyquire": "^1.3.28",
43
43
  "@types/sinon": "^10.0.16",
44
44
  "@types/sinon-chai": "^3.2.9",
45
45
  "chai": "^4.3.8",
46
46
  "chai-as-promised": "^7.1.1",
47
- "eslint": "^8.47.0",
47
+ "eslint": "^8.48.0",
48
48
  "mocha": "^10.2.0",
49
49
  "proxyquire": "^2.1.3",
50
50
  "sinon": "^15.2.0",
51
51
  "sinon-chai": "^3.7.0",
52
- "typescript": "~5.1.6"
52
+ "typescript": "~5.2.2"
53
53
  },
54
54
  "main": "main.js",
55
55
  "files": [