iobroker.poolcontrol 0.5.0 → 0.5.1

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/io-package.json CHANGED
@@ -1,8 +1,21 @@
1
1
  {
2
2
  "common": {
3
3
  "name": "poolcontrol",
4
- "version": "0.5.0",
4
+ "version": "0.5.1",
5
5
  "news": {
6
+ "0.5.1": {
7
+ "en": "Extended week and month statistics with persistent data, unified JSON format, and installation protection.",
8
+ "de": "Erweiterte Wochen- und Monatsstatistik mit persistenten Daten, einheitlichem JSON-Format und Überinstallationsschutz.",
9
+ "ru": "Расширенная недельная и месячная статистика с постоянными данными, унифицированным форматом JSON и защитой при переустановке.",
10
+ "pt": "Estatísticas semanais e mensais expandidas com dados persistentes, formato JSON unificado e proteção de reinstalação.",
11
+ "nl": "Uitgebreide week- en maandstatistieken met persistente gegevens, uniforme JSON-indeling en installatiebescherming.",
12
+ "fr": "Statistiques hebdomadaires et mensuelles étendues avec données persistantes, format JSON unifié et protection à la réinstallation.",
13
+ "it": "Statistiche settimanali e mensili estese con dati persistenti, formato JSON unificato e protezione dall'installazione.",
14
+ "es": "Estadísticas semanales y mensuales ampliadas con datos persistentes, formato JSON unificado y protección de reinstalación.",
15
+ "pl": "Rozszerzone statystyki tygodniowe i miesięczne z trwałymi danymi, ujednoliconym formatem JSON i ochroną przed ponowną instalacją.",
16
+ "uk": "Розширена тижнева та місячна статистика з постійними даними, уніфікованим форматом JSON і захистом під час перевстановлення.",
17
+ "zh-cn": "扩展的周和月统计,具有持久数据、统一的 JSON 格式和重新安装保护。"
18
+ },
6
19
  "0.5.0": {
7
20
  "en": "Added weekly and monthly temperature statistics under analytics.statistics.temperature.week and analytics.statistics.temperature.month with automatic summaries, independent helpers and persistent data points.",
8
21
  "de": "Wöchentliche und monatliche Temperaturstatistiken unter analytics.statistics.temperature.week und analytics.statistics.temperature.month hinzugefügt – mit automatischen Zusammenfassungen, unabhängigen Helpern und persistenten Datenpunkten.",
@@ -52,18 +65,6 @@
52
65
  "es": "Se añadió el cálculo del caudal real de la bomba, la monitorización en vivo y un sistema de rango normal autoaprendente para el análisis de la bomba.",
53
66
  "pl": "Dodano obliczanie rzeczywistego przepływu pompy, monitorowanie na żywo i samouczący się system normalnego zakresu do analizy pompy.",
54
67
  "zh-cn": "添加了实际泵流量计算、实时监控以及用于泵分析的自学习正常范围系统。"
55
- },
56
- "0.2.2": {
57
- "en": "Added automatic backwash reminder with speech and log notifications.",
58
- "de": "Automatische Rückspülerinnerung mit Sprach- und Log-Benachrichtigung hinzugefügt.",
59
- "ru": "Добавлено автоматическое напоминание о обратной промывке с голосовыми и журнальными уведомлениями.",
60
- "pt": "Adicionado lembrete automático de retrolavagem com notificações de voz e registro.",
61
- "nl": "Automatische terugspoelherinnering toegevoegd met spraak- en logmeldingen.",
62
- "fr": "Ajout d’un rappel automatique de contre-lavage avec notifications vocales et journaux.",
63
- "it": "Aggiunto promemoria automatico di controlavaggio con notifiche vocali e log.",
64
- "es": "Recordatorio automático de contralavado añadido con notificaciones de voz y registro.",
65
- "pl": "Dodano automatyczne przypomnienie o płukaniu wstecznym z powiadomieniami głosowymi i dziennikiem.",
66
- "zh-cn": "添加了带语音和日志通知的自动反冲洗提醒。"
67
68
  }
68
69
  },
69
70
  "titleLang": {
@@ -33,6 +33,14 @@ const statisticsHelper = {
33
33
  this.adapter = adapter;
34
34
  adapter.log.debug('statisticsHelper: Initialisierung gestartet.');
35
35
 
36
+ // --- Überinstallationsschutz ---
37
+ try {
38
+ // Prüft, ob alle States vorhanden sind, und legt fehlende still neu an
39
+ await this._verifyStructure();
40
+ } catch {
41
+ // keine Log-Ausgabe – stiller Schutz
42
+ }
43
+
36
44
  try {
37
45
  await this._createTemperatureStatistics();
38
46
  await this._subscribeActiveSensors();
@@ -227,12 +235,16 @@ const statisticsHelper = {
227
235
  await adapter.setStateAsync(`${basePath}.data_points_count`, { val: newCount, ack: true });
228
236
  await adapter.setStateAsync(`${basePath}.last_update`, { val: now, ack: true });
229
237
 
230
- // Summary aktualisieren
231
- // NEU: Zusammenfassung mit gerundeten Werten (1 Nachkommastelle)
238
+ // Summary aktualisieren – erweitert um Datum, Zeitpunkte, Messanzahl, Name
232
239
  const summary = {
240
+ name: 'Tagesstatistik',
241
+ date: new Date().toISOString().slice(0, 10),
233
242
  temp_min: newMin,
243
+ temp_min_time: (await adapter.getStateAsync(`${basePath}.temp_min_time`))?.val || '',
234
244
  temp_max: newMax,
245
+ temp_max_time: (await adapter.getStateAsync(`${basePath}.temp_max_time`))?.val || '',
235
246
  temp_avg: newAvg,
247
+ data_points_count: newCount,
236
248
  updated: now,
237
249
  };
238
250
  await adapter.setStateAsync(`${basePath}.summary_json`, { val: JSON.stringify(summary), ack: true });
@@ -246,7 +258,7 @@ const statisticsHelper = {
246
258
  },
247
259
 
248
260
  /**
249
- * Gesamt-HTML/JSON-Ausgabe aktualisieren (Endgültige Version)
261
+ * Gesamt-HTML/JSON-Ausgabe aktualisieren (erweiterte Version)
250
262
  * Liest die fertigen summary_json-Werte aller aktiven Sensoren aus
251
263
  * und erstellt daraus eine zusammengefasste HTML- und JSON-Ausgabe.
252
264
  */
@@ -257,10 +269,6 @@ const statisticsHelper = {
257
269
  try {
258
270
  // Alle Sensoren durchlaufen
259
271
  for (const sensor of this.sensors) {
260
- // const isActive = (await adapter.getStateAsync(`temperature.${sensor.id}.active`))?.val === true;
261
- // if (!isActive) continue;
262
-
263
- // Fertige Einzel-Summary des Sensors abrufen
264
272
  const summaryState = await adapter.getStateAsync(
265
273
  `analytics.statistics.temperature.today.${sensor.id}.summary_json`,
266
274
  );
@@ -280,7 +288,12 @@ const statisticsHelper = {
280
288
  const min = parsed.temp_min;
281
289
  const max = parsed.temp_max;
282
290
  const avg = parsed.temp_avg;
291
+ const date = parsed.date || '';
292
+ const minTime = parsed.temp_min_time || '';
293
+ const maxTime = parsed.temp_max_time || '';
294
+ const count = parsed.data_points_count || 0;
283
295
 
296
+ // Falls gar keine Werte vorhanden, überspringen
284
297
  if (min == null && max == null && avg == null) {
285
298
  continue;
286
299
  }
@@ -290,11 +303,16 @@ const statisticsHelper = {
290
303
  const rMax = typeof max === 'number' ? Math.round(max * 10) / 10 : max;
291
304
  const rAvg = typeof avg === 'number' ? Math.round(avg * 10) / 10 : avg;
292
305
 
306
+ // Erweiterte Datensammlung
293
307
  allData.push({
294
308
  name: sensor.name,
309
+ date,
295
310
  min: rMin,
311
+ min_time: minTime,
296
312
  max: rMax,
313
+ max_time: maxTime,
297
314
  avg: rAvg,
315
+ data_points_count: count,
298
316
  });
299
317
  }
300
318
 
@@ -311,14 +329,24 @@ const statisticsHelper = {
311
329
  return;
312
330
  }
313
331
 
314
- // JSON-Ausgabe erstellen
315
- const jsonOutput = JSON.stringify(allData);
332
+ // JSON-Ausgabe erstellen (komplett erweitert)
333
+ const jsonOutput = JSON.stringify(allData, null, 2);
316
334
 
317
- // HTML-Ausgabe erstellen
335
+ // HTML-Ausgabe erstellen (mit Datum & Anzahl Messwerte)
318
336
  let html = '<table style="width:100%;border-collapse:collapse;">';
319
- html += '<tr><th style="text-align:left;">Sensor</th><th>Min</th><th>Max</th><th>Ø</th></tr>';
337
+ html +=
338
+ '<tr><th style="text-align:left;">Sensor</th><th>Datum</th><th>Min</th><th>Zeit</th><th>Max</th><th>Zeit</th><th>Ø</th><th>Anz.</th></tr>';
320
339
  for (const entry of allData) {
321
- html += `<tr><td>${entry.name}</td><td>${entry.min ?? '-'}</td><td>${entry.max ?? '-'}</td><td>${entry.avg ?? '-'}</td></tr>`;
340
+ html += `<tr>
341
+ <td>${entry.name}</td>
342
+ <td>${entry.date || '-'}</td>
343
+ <td>${entry.min ?? '-'}</td>
344
+ <td>${entry.min_time || '-'}</td>
345
+ <td>${entry.max ?? '-'}</td>
346
+ <td>${entry.max_time || '-'}</td>
347
+ <td>${entry.avg ?? '-'}</td>
348
+ <td>${entry.data_points_count ?? '-'}</td>
349
+ </tr>`;
322
350
  }
323
351
  html += '</table>';
324
352
 
@@ -331,10 +359,8 @@ const statisticsHelper = {
331
359
  val: html,
332
360
  ack: true,
333
361
  });
334
-
335
- adapter.log.debug('statisticsHelper: Gesamtzusammenfassung (Summary All) erfolgreich aktualisiert.');
336
- } catch (err) {
337
- adapter.log.warn(`statisticsHelper: Fehler bei Gesamtzusammenfassung: ${err.message}`);
362
+ } catch {
363
+ // bewusst kein Log hier – stiller Schutz
338
364
  }
339
365
  },
340
366
 
@@ -438,6 +464,18 @@ const statisticsHelper = {
438
464
  adapter.log.debug('statisticsHelper: Tagesstatistik zurückgesetzt.');
439
465
  },
440
466
 
467
+ /**
468
+ * Stiller Überinstallationsschutz:
469
+ * Prüft und legt fehlende States erneut an, ohne bestehende Werte zu überschreiben.
470
+ */
471
+ async _verifyStructure() {
472
+ try {
473
+ await this._createTemperatureStatistics();
474
+ } catch {
475
+ // bewusst keine Logs – stiller Selbstschutz
476
+ }
477
+ },
478
+
441
479
  cleanup() {
442
480
  if (this.midnightTimer) {
443
481
  clearTimeout(this.midnightTimer);
@@ -31,6 +31,13 @@ const statisticsHelperMonth = {
31
31
  this.adapter = adapter;
32
32
  adapter.log.debug('statisticsHelperMonth: Initialisierung gestartet.');
33
33
 
34
+ // --- Überinstallationsschutz ---
35
+ try {
36
+ await this._verifyStructure();
37
+ } catch {
38
+ // keine Log-Ausgabe – stiller Schutz
39
+ }
40
+
34
41
  try {
35
42
  await this._createTemperatureStatistics();
36
43
  await this._subscribeActiveSensors();
@@ -224,9 +231,15 @@ const statisticsHelperMonth = {
224
231
  await adapter.setStateAsync(`${basePath}.last_update`, { val: now, ack: true });
225
232
 
226
233
  const summary = {
234
+ name: 'Monatsstatistik',
235
+ month_label: this._getCurrentMonthLabel(),
236
+ date: new Date().toISOString().slice(0, 10),
227
237
  temp_min: newMin,
238
+ temp_min_time: (await adapter.getStateAsync(`${basePath}.temp_min_time`))?.val || '',
228
239
  temp_max: newMax,
240
+ temp_max_time: (await adapter.getStateAsync(`${basePath}.temp_max_time`))?.val || '',
229
241
  temp_avg: newAvg,
242
+ data_points_count: newCount,
230
243
  updated: now,
231
244
  };
232
245
  await adapter.setStateAsync(`${basePath}.summary_json`, { val: JSON.stringify(summary), ack: true });
@@ -269,8 +282,22 @@ const statisticsHelperMonth = {
269
282
  const rMin = typeof min === 'number' ? Math.round(min * 10) / 10 : min;
270
283
  const rMax = typeof max === 'number' ? Math.round(max * 10) / 10 : max;
271
284
  const rAvg = typeof avg === 'number' ? Math.round(avg * 10) / 10 : avg;
272
-
273
- allData.push({ name: sensor.name, min: rMin, max: rMax, avg: rAvg });
285
+ const date = parsed.date || '';
286
+ const minTime = parsed.temp_min_time || '';
287
+ const maxTime = parsed.temp_max_time || '';
288
+ const count = parsed.data_points_count || 0;
289
+
290
+ allData.push({
291
+ month_label: this._getCurrentMonthLabel(),
292
+ name: sensor.name,
293
+ date,
294
+ min: rMin,
295
+ min_time: minTime,
296
+ max: rMax,
297
+ max_time: maxTime,
298
+ avg: rAvg,
299
+ data_points_count: count,
300
+ });
274
301
  }
275
302
 
276
303
  if (allData.length === 0) {
@@ -407,6 +434,28 @@ const statisticsHelperMonth = {
407
434
  adapter.log.debug('statisticsHelperMonth: Monatsstatistik zurückgesetzt.');
408
435
  },
409
436
 
437
+ /**
438
+ * Stiller Überinstallationsschutz:
439
+ * Prüft und legt fehlende States erneut an, ohne bestehende Werte zu überschreiben.
440
+ */
441
+ async _verifyStructure() {
442
+ try {
443
+ await this._createTemperatureStatistics();
444
+ } catch {
445
+ // bewusst keine Logs – stiller Selbstschutz
446
+ }
447
+ },
448
+
449
+ /**
450
+ * Gibt den aktuellen Monat als lesbares Label zurück, z. B. "Oktober 2025".
451
+ */
452
+ _getCurrentMonthLabel() {
453
+ const d = new Date();
454
+ const month = d.toLocaleString('de-DE', { month: 'long' });
455
+ const year = d.getFullYear();
456
+ return `${month.charAt(0).toUpperCase() + month.slice(1)} ${year}`;
457
+ },
458
+
410
459
  cleanup() {
411
460
  if (this.monthResetTimer) {
412
461
  clearTimeout(this.monthResetTimer);
@@ -31,6 +31,14 @@ const statisticsHelperWeek = {
31
31
  this.adapter = adapter;
32
32
  adapter.log.debug('statisticsHelperWeek: Initialisierung gestartet.');
33
33
 
34
+ // --- Überinstallationsschutz ---
35
+ try {
36
+ // Prüft, ob alle States vorhanden sind, und legt fehlende still neu an
37
+ await this._verifyStructure();
38
+ } catch {
39
+ // keine Log-Ausgabe – stiller Schutz
40
+ }
41
+
34
42
  try {
35
43
  await this._createTemperatureStatistics();
36
44
  await this._subscribeActiveSensors();
@@ -225,10 +233,17 @@ const statisticsHelperWeek = {
225
233
  await adapter.setStateAsync(`${basePath}.data_points_count`, { val: newCount, ack: true });
226
234
  await adapter.setStateAsync(`${basePath}.last_update`, { val: now, ack: true });
227
235
 
236
+ // Summary aktualisieren – erweitert um Datum, Zeitpunkte, Messanzahl, Name
228
237
  const summary = {
238
+ name: 'Wochenstatistik',
239
+ week_range: this._getCurrentWeekRange(),
240
+ date: new Date().toISOString().slice(0, 10),
229
241
  temp_min: newMin,
242
+ temp_min_time: (await adapter.getStateAsync(`${basePath}.temp_min_time`))?.val || '',
230
243
  temp_max: newMax,
244
+ temp_max_time: (await adapter.getStateAsync(`${basePath}.temp_max_time`))?.val || '',
231
245
  temp_avg: newAvg,
246
+ data_points_count: newCount,
232
247
  updated: now,
233
248
  };
234
249
  await adapter.setStateAsync(`${basePath}.summary_json`, { val: JSON.stringify(summary), ack: true });
@@ -241,7 +256,7 @@ const statisticsHelperWeek = {
241
256
  },
242
257
 
243
258
  /**
244
- * Gesamtzusammenfassung aller Sensoren aktualisieren
259
+ * Gesamt-HTML/JSON-Ausgabe aktualisieren (erweiterte Version – Woche)
245
260
  */
246
261
  async _updateOverallSummary() {
247
262
  const adapter = this.adapter;
@@ -263,7 +278,14 @@ const statisticsHelperWeek = {
263
278
  continue;
264
279
  }
265
280
 
266
- const { temp_min: min, temp_max: max, temp_avg: avg } = parsed;
281
+ const min = parsed.temp_min;
282
+ const max = parsed.temp_max;
283
+ const avg = parsed.temp_avg;
284
+ const date = parsed.date || '';
285
+ const minTime = parsed.temp_min_time || '';
286
+ const maxTime = parsed.temp_max_time || '';
287
+ const count = parsed.data_points_count || 0;
288
+
267
289
  if (min == null && max == null && avg == null) {
268
290
  continue;
269
291
  }
@@ -272,7 +294,17 @@ const statisticsHelperWeek = {
272
294
  const rMax = typeof max === 'number' ? Math.round(max * 10) / 10 : max;
273
295
  const rAvg = typeof avg === 'number' ? Math.round(avg * 10) / 10 : avg;
274
296
 
275
- allData.push({ name: sensor.name, min: rMin, max: rMax, avg: rAvg });
297
+ allData.push({
298
+ week_range: this._getCurrentWeekRange(),
299
+ name: sensor.name,
300
+ date,
301
+ min: rMin,
302
+ min_time: minTime,
303
+ max: rMax,
304
+ max_time: maxTime,
305
+ avg: rAvg,
306
+ data_points_count: count,
307
+ });
276
308
  }
277
309
 
278
310
  if (allData.length === 0) {
@@ -287,12 +319,22 @@ const statisticsHelperWeek = {
287
319
  return;
288
320
  }
289
321
 
290
- const jsonOutput = JSON.stringify(allData);
322
+ const jsonOutput = JSON.stringify(allData, null, 2);
291
323
 
292
324
  let html = '<table style="width:100%;border-collapse:collapse;">';
293
- html += '<tr><th style="text-align:left;">Sensor</th><th>Min</th><th>Max</th><th>Ø</th></tr>';
325
+ html +=
326
+ '<tr><th style="text-align:left;">Sensor</th><th>Datum</th><th>Min</th><th>Zeit</th><th>Max</th><th>Zeit</th><th>Ø</th><th>Anz.</th></tr>';
294
327
  for (const entry of allData) {
295
- html += `<tr><td>${entry.name}</td><td>${entry.min ?? '-'}</td><td>${entry.max ?? '-'}</td><td>${entry.avg ?? '-'}</td></tr>`;
328
+ html += `<tr>
329
+ <td>${entry.name}</td>
330
+ <td>${entry.date || '-'}</td>
331
+ <td>${entry.min ?? '-'}</td>
332
+ <td>${entry.min_time || '-'}</td>
333
+ <td>${entry.max ?? '-'}</td>
334
+ <td>${entry.max_time || '-'}</td>
335
+ <td>${entry.avg ?? '-'}</td>
336
+ <td>${entry.data_points_count ?? '-'}</td>
337
+ </tr>`;
296
338
  }
297
339
  html += '</table>';
298
340
 
@@ -304,10 +346,8 @@ const statisticsHelperWeek = {
304
346
  val: html,
305
347
  ack: true,
306
348
  });
307
-
308
- adapter.log.debug('statisticsHelperWeek: Gesamtzusammenfassung erfolgreich aktualisiert.');
309
- } catch (err) {
310
- adapter.log.warn(`statisticsHelperWeek: Fehler bei Gesamtzusammenfassung: ${err.message}`);
349
+ } catch {
350
+ // bewusst kein Log hier – stiller Schutz
311
351
  }
312
352
  },
313
353
 
@@ -412,6 +452,45 @@ const statisticsHelperWeek = {
412
452
  adapter.log.debug('statisticsHelperWeek: Wochenstatistik zurückgesetzt.');
413
453
  },
414
454
 
455
+ /**
456
+ * Stiller Überinstallationsschutz:
457
+ * Prüft und legt fehlende States erneut an, ohne bestehende Werte zu überschreiben.
458
+ */
459
+ async _verifyStructure() {
460
+ try {
461
+ await this._createTemperatureStatistics();
462
+ } catch {
463
+ // bewusst keine Logs – stiller Selbstschutz
464
+ }
465
+ },
466
+
467
+ /**
468
+ * Berechnet den Zeitraum (Start/Ende) der aktuellen Woche.
469
+ * Rückgabe: z. B. "27.10.2025 – 02.11.2025"
470
+ */
471
+ _getCurrentWeekRange() {
472
+ const today = new Date();
473
+
474
+ // Aktuellen Sonntag (nächster Wochenreset) finden
475
+ const nextSunday = new Date(today);
476
+ const day = today.getDay(); // Sonntag=0, Montag=1 …
477
+ const daysUntilSunday = (7 - day) % 7;
478
+ nextSunday.setDate(today.getDate() + daysUntilSunday);
479
+
480
+ // Wochenstart ist der Montag vor diesem Sonntag
481
+ const monday = new Date(nextSunday);
482
+ monday.setDate(nextSunday.getDate() - 6);
483
+
484
+ const fmt = d =>
485
+ d.toLocaleDateString('de-DE', {
486
+ day: '2-digit',
487
+ month: '2-digit',
488
+ year: 'numeric',
489
+ });
490
+
491
+ return `${fmt(monday)} – ${fmt(nextSunday)}`;
492
+ },
493
+
415
494
  cleanup() {
416
495
  if (this.weekResetTimer) {
417
496
  clearTimeout(this.weekResetTimer);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "iobroker.poolcontrol",
3
- "version": "0.5.0",
3
+ "version": "0.5.1",
4
4
  "description": "Steuerung & Automatisierung für den Pool (Pumpe, Heizung, Ventile, Sensoren).",
5
5
  "author": "DasBo1975 <dasbo1975@outlook.de>",
6
6
  "homepage": "https://github.com/DasBo1975/ioBroker.poolcontrol",