iobroker.poolcontrol 1.3.27 → 1.3.29

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.
Files changed (40) hide show
  1. package/README.md +22 -18
  2. package/admin/i18n/de/translations.json +4 -0
  3. package/admin/i18n/en/translations.json +8 -2
  4. package/admin/i18n/es/translations.json +4 -0
  5. package/admin/i18n/fr/translations.json +4 -0
  6. package/admin/i18n/it/translations.json +4 -0
  7. package/admin/i18n/nl/translations.json +4 -0
  8. package/admin/i18n/pl/translations.json +4 -0
  9. package/admin/i18n/pt/translations.json +4 -0
  10. package/admin/i18n/ru/translations.json +4 -0
  11. package/admin/i18n/uk/translations.json +4 -0
  12. package/admin/i18n/zh-cn/translations.json +4 -0
  13. package/admin/jsonConfig.json +31 -1
  14. package/io-package.json +27 -27
  15. package/lib/helpers/aiHelper.js +5 -5
  16. package/lib/helpers/consumptionHelper.js +47 -5
  17. package/lib/helpers/heatHelper.js +12 -9
  18. package/lib/helpers/infoHelper.js +11 -11
  19. package/lib/helpers/poolInsightsHelper.js +620 -0
  20. package/lib/helpers/pumpHelper.js +15 -7
  21. package/lib/helpers/statisticsHelper.js +7 -3
  22. package/lib/helpers/statisticsHelperMonth.js +19 -15
  23. package/lib/helpers/statisticsHelperWeek.js +11 -5
  24. package/lib/i18n/de.json +31 -1
  25. package/lib/i18n/en.json +34 -4
  26. package/lib/i18n/es.json +31 -1
  27. package/lib/i18n/fr.json +31 -1
  28. package/lib/i18n/it.json +31 -1
  29. package/lib/i18n/nl.json +31 -1
  30. package/lib/i18n/pl.json +31 -1
  31. package/lib/i18n/pt.json +31 -1
  32. package/lib/i18n/ru.json +31 -1
  33. package/lib/i18n/uk.json +31 -1
  34. package/lib/i18n/zh-cn.json +31 -1
  35. package/lib/stateDefinitions/poolInsightsStates.js +289 -0
  36. package/lib/stateDefinitions/pumpStates.js +39 -0
  37. package/lib/stateDefinitions/solarStates.js +12 -3
  38. package/lib/stateDefinitions/statusStates.js +1 -1
  39. package/main.js +20 -0
  40. package/package.json +1 -1
@@ -0,0 +1,289 @@
1
+ 'use strict';
2
+
3
+ /**
4
+ * poolInsightsStates.js
5
+ * ---------------------
6
+ * Creates states for analytics.insights.pool.
7
+ *
8
+ * Scope:
9
+ * - Rule-based overall pool insights
10
+ * - Read-only analysis output
11
+ * - Optional handoff to speech.queue handled by the helper
12
+ * - No automatic control, no dosing, no pump or actuator switching
13
+ */
14
+
15
+ async function createChannel(adapter, id, name) {
16
+ await adapter.setObjectNotExistsAsync(id, {
17
+ type: 'channel',
18
+ common: { name },
19
+ native: {},
20
+ });
21
+ }
22
+
23
+ async function createState(adapter, id, common) {
24
+ await adapter.setObjectNotExistsAsync(id, {
25
+ type: 'state',
26
+ common,
27
+ native: {},
28
+ });
29
+
30
+ if (!Object.prototype.hasOwnProperty.call(common, 'def')) {
31
+ return;
32
+ }
33
+
34
+ const existing = await adapter.getStateAsync(id);
35
+ if (!existing || existing.val === null || existing.val === undefined) {
36
+ await adapter.setStateAsync(id, { val: common.def, ack: true });
37
+ }
38
+ }
39
+
40
+ /**
41
+ * @param {import('iobroker').Adapter} adapter - ioBroker adapter instance
42
+ * @returns {Promise<void>}
43
+ */
44
+ async function createPoolInsightsStates(adapter) {
45
+ adapter.log.debug('[poolInsightsStates] Initialization started');
46
+
47
+ await createChannel(adapter, 'analytics', {
48
+ en: 'Analytics & insights (statistics, history, reports)',
49
+ de: 'Analysen & Statistiken (Verlauf, Berichte)',
50
+ });
51
+
52
+ await createChannel(adapter, 'analytics.insights', {
53
+ en: 'Insights & analysis',
54
+ de: 'Erkenntnisse & Analysen',
55
+ });
56
+
57
+ await createChannel(adapter, 'analytics.insights.pool', {
58
+ en: 'Pool insights (overall analysis)',
59
+ de: 'Pool Insights (Gesamtanalyse)',
60
+ });
61
+
62
+ await createChannel(adapter, 'analytics.insights.pool.debug', {
63
+ en: 'Debug',
64
+ de: 'Debug',
65
+ });
66
+
67
+ const states = [
68
+ {
69
+ id: 'analytics.insights.pool.enabled',
70
+ common: {
71
+ name: {
72
+ en: 'Enable pool insights',
73
+ de: 'Pool Insights aktivieren',
74
+ },
75
+ desc: {
76
+ en: 'Enables the rule-based overall pool analysis. No automatic control is performed.',
77
+ de: 'Aktiviert die regelbasierte Pool-Gesamtanalyse. Es erfolgt keine automatische Steuerung.',
78
+ },
79
+ type: 'boolean',
80
+ role: 'switch',
81
+ read: true,
82
+ write: true,
83
+ def: false,
84
+ persist: true,
85
+ },
86
+ },
87
+ {
88
+ id: 'analytics.insights.pool.manual_trigger',
89
+ common: {
90
+ name: {
91
+ en: 'Run pool insights now',
92
+ de: 'Pool Insights jetzt ausführen',
93
+ },
94
+ desc: {
95
+ en: 'Starts a manual pool insights analysis and is reset afterwards.',
96
+ de: 'Startet eine manuelle Pool-Insights-Analyse und wird danach zurückgesetzt.',
97
+ },
98
+ type: 'boolean',
99
+ role: 'button',
100
+ read: true,
101
+ write: true,
102
+ def: false,
103
+ },
104
+ },
105
+ {
106
+ id: 'analytics.insights.pool.send_to_speech_queue',
107
+ common: {
108
+ name: {
109
+ en: 'Send summary to speech queue',
110
+ de: 'Zusammenfassung an Speech Queue senden',
111
+ },
112
+ desc: {
113
+ en: 'Allows pool insights to optionally write the summary to speech.queue.',
114
+ de: 'Erlaubt Pool Insights optional, die Zusammenfassung an speech.queue zu schreiben.',
115
+ },
116
+ type: 'boolean',
117
+ role: 'switch',
118
+ read: true,
119
+ write: true,
120
+ def: false,
121
+ persist: true,
122
+ },
123
+ },
124
+ {
125
+ id: 'analytics.insights.pool.status',
126
+ common: {
127
+ name: {
128
+ en: 'Status',
129
+ de: 'Status',
130
+ },
131
+ type: 'string',
132
+ role: 'text',
133
+ read: true,
134
+ write: false,
135
+ def: 'disabled',
136
+ states: {
137
+ disabled: 'disabled',
138
+ idle: 'idle',
139
+ scheduled: 'scheduled',
140
+ running: 'running',
141
+ completed: 'completed',
142
+ error: 'error',
143
+ },
144
+ },
145
+ },
146
+ {
147
+ id: 'analytics.insights.pool.level',
148
+ common: {
149
+ name: {
150
+ en: 'Level',
151
+ de: 'Stufe',
152
+ },
153
+ type: 'string',
154
+ role: 'text',
155
+ read: true,
156
+ write: false,
157
+ def: 'none',
158
+ states: {
159
+ none: 'none',
160
+ ok: 'ok',
161
+ info: 'info',
162
+ warning: 'warning',
163
+ },
164
+ },
165
+ },
166
+ {
167
+ id: 'analytics.insights.pool.summary_text',
168
+ common: {
169
+ name: {
170
+ en: 'Summary text',
171
+ de: 'Zusammenfassungstext',
172
+ },
173
+ type: 'string',
174
+ role: 'text',
175
+ read: true,
176
+ write: false,
177
+ def: '',
178
+ },
179
+ },
180
+ {
181
+ id: 'analytics.insights.pool.summary_html',
182
+ common: {
183
+ name: {
184
+ en: 'Summary HTML',
185
+ de: 'Zusammenfassung HTML',
186
+ },
187
+ type: 'string',
188
+ role: 'html',
189
+ read: true,
190
+ write: false,
191
+ def: '',
192
+ },
193
+ },
194
+ {
195
+ id: 'analytics.insights.pool.summary_json',
196
+ common: {
197
+ name: {
198
+ en: 'Summary JSON',
199
+ de: 'Zusammenfassung JSON',
200
+ },
201
+ type: 'string',
202
+ role: 'json',
203
+ read: true,
204
+ write: false,
205
+ def: '{}',
206
+ },
207
+ },
208
+ {
209
+ id: 'analytics.insights.pool.observations_json',
210
+ common: {
211
+ name: {
212
+ en: 'Observations JSON',
213
+ de: 'Beobachtungen JSON',
214
+ },
215
+ type: 'string',
216
+ role: 'json',
217
+ read: true,
218
+ write: false,
219
+ def: '[]',
220
+ },
221
+ },
222
+ {
223
+ id: 'analytics.insights.pool.recommendations_json',
224
+ common: {
225
+ name: {
226
+ en: 'Recommendations JSON',
227
+ de: 'Empfehlungen JSON',
228
+ },
229
+ type: 'string',
230
+ role: 'json',
231
+ read: true,
232
+ write: false,
233
+ def: '[]',
234
+ },
235
+ },
236
+ {
237
+ id: 'analytics.insights.pool.last_update',
238
+ common: {
239
+ name: {
240
+ en: 'Last update',
241
+ de: 'Letzte Aktualisierung',
242
+ },
243
+ type: 'string',
244
+ role: 'date',
245
+ read: true,
246
+ write: false,
247
+ def: '',
248
+ },
249
+ },
250
+ {
251
+ id: 'analytics.insights.pool.last_speech_at',
252
+ common: {
253
+ name: {
254
+ en: 'Last speech output',
255
+ de: 'Letzte Sprachausgabe',
256
+ },
257
+ type: 'string',
258
+ role: 'date',
259
+ read: true,
260
+ write: false,
261
+ def: '',
262
+ },
263
+ },
264
+ {
265
+ id: 'analytics.insights.pool.debug.last_reason',
266
+ common: {
267
+ name: {
268
+ en: 'Last analysis reason',
269
+ de: 'Letzter Analysegrund',
270
+ },
271
+ type: 'string',
272
+ role: 'text',
273
+ read: true,
274
+ write: false,
275
+ def: '',
276
+ },
277
+ },
278
+ ];
279
+
280
+ for (const state of states) {
281
+ await createState(adapter, state.id, state.common);
282
+ }
283
+
284
+ adapter.log.debug('[poolInsightsStates] Initialization completed');
285
+ }
286
+
287
+ module.exports = {
288
+ createPoolInsightsStates,
289
+ };
@@ -233,6 +233,45 @@ async function createPumpStates(adapter) {
233
233
  });
234
234
  }
235
235
 
236
+ // NEU: Sicherheits-Timeout für Leistungsprüfung nach Pumpenstart
237
+ await adapter.setObjectNotExistsAsync('pump.startup_power_check_timeout_sec', {
238
+ type: 'state',
239
+ common: {
240
+ name: {
241
+ en: 'Startup power check timeout',
242
+ de: 'Timeout Leistungsprüfung nach Pumpenstart',
243
+ },
244
+ desc: {
245
+ en: 'Time in seconds PoolControl waits after a pump start for valid power consumption. Higher values delay error detection and should only be used if the measurement hardware reports power values with a delay.',
246
+ de: 'Zeit in Sekunden, die PoolControl nach einem Pumpenstart auf eine gültige Leistungsaufnahme wartet. Höhere Werte verzögern die Fehlererkennung und sollten nur verwendet werden, wenn die eingesetzte Messhardware Leistungswerte verzögert liefert.',
247
+ },
248
+ type: 'number',
249
+ role: 'value',
250
+ unit: 's',
251
+ read: true,
252
+ write: true,
253
+ min: 5,
254
+ max: 10,
255
+ step: 1,
256
+ def: 5,
257
+ persist: true,
258
+ },
259
+ native: {},
260
+ });
261
+
262
+ // NEU: nur initial setzen, vorhandene/persistente Werte nicht überschreiben
263
+ const existingStartupPowerTimeout = await adapter.getStateAsync('pump.startup_power_check_timeout_sec');
264
+ if (
265
+ existingStartupPowerTimeout === null ||
266
+ existingStartupPowerTimeout.val === null ||
267
+ existingStartupPowerTimeout.val === undefined
268
+ ) {
269
+ await adapter.setStateAsync('pump.startup_power_check_timeout_sec', {
270
+ val: 5,
271
+ ack: true,
272
+ });
273
+ }
274
+
236
275
  // Pumpenstatus (Text)
237
276
  await adapter.setObjectNotExistsAsync('pump.status', {
238
277
  type: 'state',
@@ -245,7 +245,10 @@ async function createSolarStates(adapter) {
245
245
  const existingWarnActive = await adapter.getStateAsync('solar.warn_active');
246
246
  if (existingWarnActive === null || existingWarnActive.val === null || existingWarnActive.val === undefined) {
247
247
  // Nur bei erstmaligem Anlegen auf false setzen
248
- await adapter.setStateAsync('solar.warn_active', { val: false, ack: true });
248
+ await adapter.setStateAsync('solar.warn_active', {
249
+ val: adapter.config.solar_collector_warn_active ?? false,
250
+ ack: true,
251
+ });
249
252
  }
250
253
 
251
254
  // NEU: Warnschwelle (°C, mit Persist-Schutz)
@@ -271,7 +274,10 @@ async function createSolarStates(adapter) {
271
274
  });
272
275
  const existingWarnTemp = await adapter.getStateAsync('solar.warn_temp');
273
276
  if (existingWarnTemp === null || existingWarnTemp.val === null || existingWarnTemp.val === undefined) {
274
- await adapter.setStateAsync('solar.warn_temp', { val: 80, ack: true });
277
+ await adapter.setStateAsync('solar.warn_temp', {
278
+ val: adapter.config.solar_collector_warn_temp ?? 80,
279
+ ack: true,
280
+ });
275
281
  }
276
282
 
277
283
  // NEU: Sprachausgabe bei Warnung (mit Persist-Schutz)
@@ -296,7 +302,10 @@ async function createSolarStates(adapter) {
296
302
  });
297
303
  const existingWarnSpeech = await adapter.getStateAsync('solar.warn_speech');
298
304
  if (existingWarnSpeech === null || existingWarnSpeech.val === null || existingWarnSpeech.val === undefined) {
299
- await adapter.setStateAsync('solar.warn_speech', { val: true, ack: true });
305
+ await adapter.setStateAsync('solar.warn_speech', {
306
+ val: adapter.config.solar_collector_warn_speech ?? true,
307
+ ack: true,
308
+ });
300
309
  }
301
310
  }
302
311
 
@@ -268,7 +268,7 @@ async function createStatusStates(adapter) {
268
268
  // Prüfen, ob bereits ein persistierter Wert existiert
269
269
  const existingSeasonActive = await adapter.getStateAsync('status.season_active');
270
270
  if (existingSeasonActive === null || existingSeasonActive.val === null || existingSeasonActive.val === undefined) {
271
- await adapter.setStateAsync('status.season_active', { val: false, ack: true });
271
+ await adapter.setStateAsync('status.season_active', { val: adapter.config.season_active ?? false, ack: true });
272
272
  }
273
273
  }
274
274
 
package/main.js CHANGED
@@ -42,6 +42,7 @@ const heatHelper = require('./lib/helpers/heatHelper');
42
42
  const actuatorsHelper = require('./lib/helpers/actuatorsHelper'); // NEU
43
43
  const solarInsightsHelper = require('./lib/helpers/solarInsightsHelper');
44
44
  const solarLogbookHelper = require('./lib/helpers/solarLogbookHelper'); // NEU
45
+ const poolInsightsHelper = require('./lib/helpers/poolInsightsHelper');
45
46
  const { createTemperatureStates } = require('./lib/stateDefinitions/temperatureStates');
46
47
  const { createPumpStates } = require('./lib/stateDefinitions/pumpStates');
47
48
  const { createPumpStates2 } = require('./lib/stateDefinitions/pumpStates2');
@@ -71,6 +72,7 @@ const { createHeatStates } = require('./lib/stateDefinitions/heatStates');
71
72
  const { createActuatorsStates } = require('./lib/stateDefinitions/actuatorsStates');
72
73
  const { createSolarInsightsStates } = require('./lib/stateDefinitions/solarInsightsStates');
73
74
  const { createPhotovoltaicInsightsStates } = require('./lib/stateDefinitions/photovoltaicInsightsStates');
75
+ const { createPoolInsightsStates } = require('./lib/stateDefinitions/poolInsightsStates');
74
76
 
75
77
  class Poolcontrol extends utils.Adapter {
76
78
  constructor(options) {
@@ -148,6 +150,9 @@ class Poolcontrol extends utils.Adapter {
148
150
  // --- Photovoltaic Insights / Analyse ---
149
151
  await createPhotovoltaicInsightsStates(this);
150
152
 
153
+ // --- Pool Insights / Gesamtanalyse ---
154
+ await createPoolInsightsStates(this);
155
+
151
156
  // --- Sprachausgaben ---
152
157
  await createSpeechStates(this);
153
158
 
@@ -224,6 +229,7 @@ class Poolcontrol extends utils.Adapter {
224
229
  actuatorsHelper.init(this); // NEU
225
230
  solarInsightsHelper.init(this);
226
231
  solarLogbookHelper.init(this); // NEU
232
+ poolInsightsHelper.init(this);
227
233
  }
228
234
 
229
235
  onUnload(callback) {
@@ -240,6 +246,12 @@ class Poolcontrol extends utils.Adapter {
240
246
  if (statisticsHelper.cleanup) {
241
247
  statisticsHelper.cleanup();
242
248
  }
249
+ if (statisticsHelperWeek.cleanup) {
250
+ statisticsHelperWeek.cleanup();
251
+ }
252
+ if (statisticsHelperMonth.cleanup) {
253
+ statisticsHelperMonth.cleanup();
254
+ }
243
255
  if (pumpHelper.cleanup) {
244
256
  pumpHelper.cleanup();
245
257
  }
@@ -288,6 +300,9 @@ class Poolcontrol extends utils.Adapter {
288
300
  if (solarLogbookHelper.cleanup) {
289
301
  solarLogbookHelper.cleanup(); // NEU
290
302
  }
303
+ if (poolInsightsHelper.cleanup) {
304
+ poolInsightsHelper.cleanup();
305
+ }
291
306
  if (speechTextHelper.cleanup) {
292
307
  speechTextHelper.cleanup();
293
308
  }
@@ -486,6 +501,11 @@ class Poolcontrol extends utils.Adapter {
486
501
  } catch (e) {
487
502
  this.log.warn(`[solarInsightsHelper] Error in handleStateChange: ${e.message}`);
488
503
  }
504
+ try {
505
+ poolInsightsHelper.handleStateChange(id, state);
506
+ } catch (e) {
507
+ this.log.warn(`[poolInsightsHelper] Error in handleStateChange: ${e.message}`);
508
+ }
489
509
  if (id.includes('control.')) {
490
510
  controlHelper.handleStateChange(id, state);
491
511
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "iobroker.poolcontrol",
3
- "version": "1.3.27",
3
+ "version": "1.3.29",
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",