iobroker.pv-notifications 1.0.93 → 1.0.99

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
@@ -339,6 +339,16 @@ sadam6752@gmail.com
339
339
  ### **WORK IN PROGRESS**
340
340
  -->
341
341
 
342
+ ### 1.0.99 (2026-03-01)
343
+
344
+ * (FIX) Weekly stats auto-saved every Sunday at 23:55 (before sourceanalytix reset at 00:00)
345
+ * (FIX) Monthly stats auto-saved on last day of month at 23:55 (before sourceanalytix reset at 00:00)
346
+ * (ADD) User can freely configure send time for weekly/monthly stats via statsWeekDay, statsWeekTime, monthlyStatsDay, monthlyStatsTime
347
+
348
+ ### 1.0.98 (2026-03-01)
349
+
350
+ * (FIX) Weekly/monthly stats now read directly from external states using getForeignStateAsync (not from internal states)
351
+
342
352
  ### 1.0.93 (2026-02-27)
343
353
 
344
354
  * (FIX) size attributes (xs, xl) added for number fields in jsonConfig.json (E5507)
package/io-package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "common": {
3
3
  "name": "pv-notifications",
4
- "version": "1.0.93",
4
+ "version": "1.0.99",
5
5
  "titleLang": {
6
6
  "en": "PV Notifications",
7
7
  "de": "PV-Benachrichtigungen",
package/main.js CHANGED
@@ -414,6 +414,38 @@ class PvNotifications extends utils.Adapter {
414
414
  this.stats.lastStatsReset = today;
415
415
  await this.saveStatistics();
416
416
  }
417
+
418
+ // Load saved last week data from states
419
+ const lastWeekProduction = await this.getStateAsync('statistics.lastWeekProduction');
420
+ const lastWeekConsumption = await this.getStateAsync('statistics.lastWeekConsumption');
421
+ const lastWeekFeedIn = await this.getStateAsync('statistics.lastWeekFeedIn');
422
+ const lastWeekGridPower = await this.getStateAsync('statistics.lastWeekGridPower');
423
+ const lastWeekFullCycles = await this.getStateAsync('statistics.lastWeekFullCycles');
424
+ const lastWeekEmptyCycles = await this.getStateAsync('statistics.lastWeekEmptyCycles');
425
+
426
+ this.stats.lastWeekProduction = lastWeekProduction && lastWeekProduction.val !== null ? lastWeekProduction.val : 0;
427
+ this.stats.lastWeekConsumption = lastWeekConsumption && lastWeekConsumption.val !== null ? lastWeekConsumption.val : 0;
428
+ this.stats.lastWeekFeedIn = lastWeekFeedIn && lastWeekFeedIn.val !== null ? lastWeekFeedIn.val : 0;
429
+ this.stats.lastWeekGridPower = lastWeekGridPower && lastWeekGridPower.val !== null ? lastWeekGridPower.val : 0;
430
+ this.stats.lastWeekFullCycles = lastWeekFullCycles && lastWeekFullCycles.val !== null ? lastWeekFullCycles.val : 0;
431
+ this.stats.lastWeekEmptyCycles = lastWeekEmptyCycles && lastWeekEmptyCycles.val !== null ? lastWeekEmptyCycles.val : 0;
432
+
433
+ // Load saved last month data from states
434
+ const lastMonthProduction = await this.getStateAsync('statistics.lastMonthProduction');
435
+ const lastMonthConsumption = await this.getStateAsync('statistics.lastMonthConsumption');
436
+ const lastMonthFeedIn = await this.getStateAsync('statistics.lastMonthFeedIn');
437
+ const lastMonthGridPower = await this.getStateAsync('statistics.lastMonthGridPower');
438
+ const lastMonthFullCycles = await this.getStateAsync('statistics.lastMonthFullCycles');
439
+ const lastMonthEmptyCycles = await this.getStateAsync('statistics.lastMonthEmptyCycles');
440
+
441
+ this.stats.lastMonthProduction = lastMonthProduction && lastMonthProduction.val !== null ? lastMonthProduction.val : 0;
442
+ this.stats.lastMonthConsumption = lastMonthConsumption && lastMonthConsumption.val !== null ? lastMonthConsumption.val : 0;
443
+ this.stats.lastMonthFeedIn = lastMonthFeedIn && lastMonthFeedIn.val !== null ? lastMonthFeedIn.val : 0;
444
+ this.stats.lastMonthGridPower = lastMonthGridPower && lastMonthGridPower.val !== null ? lastMonthGridPower.val : 0;
445
+ this.stats.lastMonthFullCycles = lastMonthFullCycles && lastMonthFullCycles.val !== null ? lastMonthFullCycles.val : 0;
446
+ this.stats.lastMonthEmptyCycles = lastMonthEmptyCycles && lastMonthEmptyCycles.val !== null ? lastMonthEmptyCycles.val : 0;
447
+
448
+ this.log.info('Statistics loaded from states');
417
449
  } catch (e) {
418
450
  this.log.error(`Error loading statistics: ${e.message}`);
419
451
  }
@@ -528,24 +560,29 @@ class PvNotifications extends utils.Adapter {
528
560
  return;
529
561
  }
530
562
 
531
- // Update other data points (Production, Consumption, etc.)
532
- // if (state.ack) { // Only process status updates
533
- if (id === this.config.totalProduction) {
534
- await this.setStateAsync('statistics.currentTotalProduction', state.val, true);
535
- }
536
- if (id === this.config.feedIn) {
537
- await this.setStateAsync('statistics.currentFeedIn', state.val, true);
538
- }
539
- if (id === this.config.consumption) {
540
- await this.setStateAsync('statistics.currentConsumption', state.val, true);
541
- }
542
- if (id === this.config.gridPower) {
543
- await this.setStateAsync('statistics.currentGridPower', state.val, true);
544
- }
545
- if (id === this.config.powerProduction) {
546
- await this.setStateAsync('statistics.currentPower', state.val, true);
563
+ // Update other data points (Production, Consumption, etc.) - only ack states
564
+ if (state.ack) {
565
+ if (id === this.config.totalProduction) {
566
+ await this.setStateAsync('statistics.currentTotalProduction', state.val, true);
567
+ this.log.debug(`Updated currentTotalProduction: ${state.val}`);
568
+ }
569
+ if (id === this.config.feedIn) {
570
+ await this.setStateAsync('statistics.currentFeedIn', state.val, true);
571
+ this.log.debug(`Updated currentFeedIn: ${state.val}`);
572
+ }
573
+ if (id === this.config.consumption) {
574
+ await this.setStateAsync('statistics.currentConsumption', state.val, true);
575
+ this.log.debug(`Updated currentConsumption: ${state.val}`);
576
+ }
577
+ if (id === this.config.gridPower) {
578
+ await this.setStateAsync('statistics.currentGridPower', state.val, true);
579
+ this.log.debug(`Updated currentGridPower: ${state.val}`);
580
+ }
581
+ if (id === this.config.powerProduction) {
582
+ await this.setStateAsync('statistics.currentPower', state.val, true);
583
+ this.log.debug(`Updated currentPower: ${state.val}`);
584
+ }
547
585
  }
548
- // }
549
586
  }
550
587
  }
551
588
 
@@ -1166,7 +1203,9 @@ ${statusText}`;
1166
1203
  const now = new Date();
1167
1204
  const hours = now.getHours();
1168
1205
  const minutes = now.getMinutes();
1169
- const day = now.getDay(); // 0=So, 1=Mo, ..., 6=Sa
1206
+ const jsDay = now.getDay(); // JavaScript: 0=So, 1=Mo, ..., 6=Sa
1207
+ // Umwandeln in ioBroker-Format: 0=Mo, 1=Di, ..., 6=So
1208
+ const day = jsDay === 0 ? 6 : jsDay - 1;
1170
1209
  const date = now.getDate();
1171
1210
 
1172
1211
  // Alle 5 Minuten: Statistik prüfen (um :00, :05, :10, ...)
@@ -1227,59 +1266,79 @@ ${statusText}`;
1227
1266
  }
1228
1267
 
1229
1268
  /**
1230
- * Reset weekly statistics
1269
+ * Reset weekly statistics - AUTOMATISCH am Sonntag um 23:55 (vor sourceanalytix Reset)
1231
1270
  */
1232
- resetWeeklyStats() {
1233
- const today = new Date().getDay();
1234
- if (today === this.config.statsWeekDay && today !== this.stats.lastWeekReset) {
1235
- this.log.info('Resetting weekly statistics');
1236
-
1237
- // Aktuelle Daten als "letzte Woche" speichern
1238
- this.stats.lastWeekProduction = this.getStateValue(this.config.weeklyProduction);
1239
- this.stats.lastWeekConsumption = this.getStateValue(this.config.weeklyConsumption);
1240
- this.stats.lastWeekFeedIn = this.getStateValue(this.config.weeklyFeedIn);
1241
- this.stats.lastWeekGridPower = this.getStateValue(this.config.weeklyGridPower);
1271
+ async resetWeeklyStats() {
1272
+ const now = new Date();
1273
+ const jsDay = now.getDay(); // JavaScript: 0=So, 1=Mo, ..., 6=Sa
1274
+ const hours = now.getHours();
1275
+ const minutes = now.getMinutes();
1276
+
1277
+ // Automatische Speicherung: Jeden Sonntag um 23:55 (vor Mitternacht)
1278
+ // JavaScript: 0 = Sonntag → genau das wollen wir!
1279
+ if (jsDay === 0 && hours === 23 && minutes === 55) {
1280
+ this.log.info('Auto-saving weekly statistics (Sunday 23:55, before sourceanalytix reset)');
1281
+
1282
+ // Aktuelle Daten aus externen States lesen (direkter Zugriff)
1283
+ const totalProd = await this.getForeignStateAsync(this.config.totalProduction);
1284
+ const consumption = await this.getForeignStateAsync(this.config.consumption);
1285
+ const feedIn = await this.getForeignStateAsync(this.config.feedIn);
1286
+ const gridPower = await this.getForeignStateAsync(this.config.gridPower);
1287
+
1288
+ this.stats.lastWeekProduction = totalProd && totalProd.val !== null ? totalProd.val : 0;
1289
+ this.stats.lastWeekConsumption = consumption && consumption.val !== null ? consumption.val : 0;
1290
+ this.stats.lastWeekFeedIn = feedIn && feedIn.val !== null ? feedIn.val : 0;
1291
+ this.stats.lastWeekGridPower = gridPower && gridPower.val !== null ? gridPower.val : 0;
1242
1292
  this.stats.lastWeekFullCycles = this.stats.weekFullCycles;
1243
1293
  this.stats.lastWeekEmptyCycles = this.stats.weekEmptyCycles;
1244
1294
 
1245
1295
  // Wöchentliche Statistik zurücksetzen
1246
1296
  this.stats.weekFullCycles = 0;
1247
1297
  this.stats.weekEmptyCycles = 0;
1248
- this.stats.lastWeekReset = today;
1249
1298
 
1250
1299
  this.saveStatistics();
1251
- this.sendTelegram(this.buildWeeklyStatsMessage());
1300
+ this.log.info(`Weekly stats saved: Production=${this.stats.lastWeekProduction} kWh, FeedIn=${this.stats.lastWeekFeedIn} kWh`);
1301
+ // KEIN Senden hier - Senden erfolgt nur in startScheduledTasks() zur konfigurierten Zeit
1252
1302
  }
1253
1303
  }
1254
1304
 
1255
1305
  /**
1256
- * Monatsstatistik zurücksetzen
1306
+ * Monatsstatistik zurücksetzen - AUTOMATISCH am letzten Tag des Monats um 23:55 (vor sourceanalytix Reset)
1257
1307
  */
1258
- resetMonthlyStats() {
1308
+ async resetMonthlyStats() {
1259
1309
  if (!this.config.monthlyStatsEnabled) {
1260
1310
  return;
1261
1311
  }
1262
1312
 
1263
- const today = new Date().getDate();
1264
1313
  const now = new Date();
1314
+ const today = now.getDate();
1265
1315
  const hours = now.getHours();
1266
- const [statHours] = this.config.monthlyStatsTime.split(':').map(Number);
1267
- // const [statMinutes] = ... // ESLint: unused
1268
-
1269
- // Daten am konfigurierten Tag nach der Sendezeit speichern
1270
- if (today === this.config.monthlyStatsDay && this.stats.lastMonthReset !== today && hours >= statHours) {
1271
- this.log.info('Resetting monthly statistics');
1272
-
1273
- // Aktuelle Daten als "letzter Monat" speichern
1274
- this.stats.lastMonthProduction = this.getStateValue(this.config.monthlyProduction);
1275
- this.stats.lastMonthConsumption = this.getStateValue(this.config.monthlyConsumption);
1276
- this.stats.lastMonthFeedIn = this.getStateValue(this.config.monthlyFeedIn);
1277
- this.stats.lastMonthGridPower = this.getStateValue(this.config.monthlyGridPower);
1316
+ const minutes = now.getMinutes();
1317
+
1318
+ // Letzten Tag des aktuellen Monats berechnen
1319
+ // new Date(Jahr, Monat+1, 0) gibt den letzten Tag des aktuellen Monats zurück
1320
+ const lastDayOfMonth = new Date(now.getFullYear(), now.getMonth() + 1, 0).getDate();
1321
+
1322
+ // Automatische Speicherung: Letzter Tag des Monats um 23:55 (vor Mitternacht)
1323
+ if (today === lastDayOfMonth && hours === 23 && minutes === 55) {
1324
+ this.log.info(`Auto-saving monthly statistics (last day of month ${today}. ${now.getMonth()+1}.${now.getFullYear()} 23:55, before sourceanalytix reset)`);
1325
+
1326
+ // Aktuelle Daten aus externen States lesen (direkter Zugriff)
1327
+ const totalProd = await this.getForeignStateAsync(this.config.monthlyProduction);
1328
+ const consumption = await this.getForeignStateAsync(this.config.monthlyConsumption);
1329
+ const feedIn = await this.getForeignStateAsync(this.config.monthlyFeedIn);
1330
+ const gridPower = await this.getForeignStateAsync(this.config.monthlyGridPower);
1331
+
1332
+ this.stats.lastMonthProduction = totalProd && totalProd.val !== null ? totalProd.val : 0;
1333
+ this.stats.lastMonthConsumption = consumption && consumption.val !== null ? consumption.val : 0;
1334
+ this.stats.lastMonthFeedIn = feedIn && feedIn.val !== null ? feedIn.val : 0;
1335
+ this.stats.lastMonthGridPower = gridPower && gridPower.val !== null ? gridPower.val : 0;
1278
1336
  this.stats.lastMonthFullCycles = this.stats.fullCycles;
1279
1337
  this.stats.lastMonthEmptyCycles = this.stats.emptyCycles;
1280
1338
 
1281
1339
  this.stats.lastMonthReset = today;
1282
1340
  this.saveStatistics();
1341
+ this.log.info(`Monthly stats saved: Production=${this.stats.lastMonthProduction} kWh, FeedIn=${this.stats.lastMonthFeedIn} kWh`);
1283
1342
  }
1284
1343
  }
1285
1344
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "iobroker.pv-notifications",
3
- "version": "1.0.93",
3
+ "version": "1.0.99",
4
4
  "description": "PV-Anlage Batterie-Status Telegram Benachrichtigungen",
5
5
  "author": {
6
6
  "name": "Alex1808 via LLM: Qwen",