iobroker.poolcontrol 0.2.0 → 0.2.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.2.0",
4
+ "version": "0.2.1",
5
5
  "news": {
6
+ "0.2.1": {
7
+ "en": "Fixed the issue with invisible states for speech control and added proper internal variable handling.",
8
+ "de": "Behebung des Problems mit unsichtbaren States für die Sprachsteuerung und hinzugefügte korrekte Handhabung interner Variablen.",
9
+ "ru": "Исправлена проблема с невидимыми состояниями для управления речью и добавлена правильная обработка внутренних переменных.",
10
+ "pt": "Corrigido o problema com estados invisíveis para controle de fala e adicionada a manipulação adequada de variáveis internas.",
11
+ "nl": "Het probleem met onzichtbare toestanden voor spraakbesturing is opgelost en de juiste verwerking van interne variabelen toegevoegd.",
12
+ "fr": "Correction du problème des états invisibles pour le contrôle vocal et ajout d'un traitement correct des variables internes.",
13
+ "it": "Risolto il problema con gli stati invisibili per il controllo vocale e aggiunto un corretto trattamento delle variabili interne.",
14
+ "es": "Se solucionó el problema con los estados invisibles para el control de voz y se añadió el manejo adecuado de las variables internas.",
15
+ "pl": "Naprawiono problem z niewidzialnymi stanami dla kontroli głosowej i dodano prawidłowe przetwarzanie zmiennych wewnętrznych.",
16
+ "uk": "Виправлено проблему з невидимими станами для керування голосом та додано правильну обробку внутрішніх змінних.",
17
+ "zh-cn": "修复了语音控制的隐形状态问题,并添加了正确的内部变量处理。"
18
+ },
6
19
  "0.2.0": {
7
20
  "en": "New diagnostic area 'SystemCheck' for internal debug logs and future analysis tools.",
8
21
  "de": "Neuer Diagnosebereich 'SystemCheck' für interne Debug-Logs und künftige Analysefunktionen.",
@@ -115,6 +128,11 @@
115
128
  "extIcon": "https://raw.githubusercontent.com/DasBo1975/ioBroker.poolcontrol/main/admin/poolcontrol.png",
116
129
  "readme": "https://github.com/DasBo1975/ioBroker.poolcontrol/blob/main/README.md",
117
130
  "loglevel": "info",
131
+ "ignoreLogs": [
132
+ "State .*speech\\.solar_active.* has no existing object",
133
+ "State .*speech\\.frost_active.* has no existing object",
134
+ "State .*speech\\.time_active.* has no existing object"
135
+ ],
118
136
  "tier": 3,
119
137
  "mode": "daemon",
120
138
  "type": "climate-control",
@@ -29,13 +29,13 @@ const consumptionHelper = {
29
29
  this.price = parseFloat(String(adapter.config.energy_price_eur_kwh).replace(',', '.')) || 0;
30
30
  this.lastKnownPrice = this.price;
31
31
 
32
- this.adapter.log.info(`[consumptionHelper] Strompreis: ${this.price} €/kWh`);
32
+ this.adapter.log.debug(`[consumptionHelper] Strompreis: ${this.price} €/kWh`);
33
33
 
34
34
  if (this.energyId) {
35
35
  adapter.subscribeForeignStates(this.energyId);
36
- adapter.log.info(`[consumptionHelper] Überwache externen kWh-Zähler: ${this.energyId}`);
36
+ adapter.log.debug(`[consumptionHelper] Überwache externen kWh-Zähler: ${this.energyId}`);
37
37
  } else {
38
- adapter.log.info('[consumptionHelper] Kein externer kWh-Zähler konfiguriert → Verbrauchslogik inaktiv.');
38
+ adapter.log.debug('[consumptionHelper] Kein externer kWh-Zähler konfiguriert → Verbrauchslogik inaktiv.');
39
39
  }
40
40
 
41
41
  this._scheduleDailyReset();
@@ -51,7 +51,7 @@ const consumptionHelper = {
51
51
  this.baseTotalKwh = totalKwh;
52
52
  this.baseTotalEur = totalEur;
53
53
 
54
- this.adapter.log.info(
54
+ this.adapter.log.debug(
55
55
  `[consumptionHelper] Kosten-Basis geladen → ${this.baseTotalEur.toFixed(
56
56
  2,
57
57
  )} € bei ${this.baseTotalKwh.toFixed(3)} kWh`,
@@ -169,7 +169,7 @@ const consumptionHelper = {
169
169
  this.baselines.month = totalNow - (month || 0);
170
170
  this.baselines.year = totalNow - (year || 0);
171
171
 
172
- this.adapter.log.info(`[consumptionHelper] Baselines geladen: ${JSON.stringify(this.baselines)}`);
172
+ this.adapter.log.debug(`[consumptionHelper] Baselines geladen: ${JSON.stringify(this.baselines)}`);
173
173
  },
174
174
 
175
175
  // NEU: Baselines aus States rekonstruieren (beim Adapterstart)
@@ -186,7 +186,7 @@ const consumptionHelper = {
186
186
  this.baselines.month = totalNow - month;
187
187
  this.baselines.year = totalNow - year;
188
188
 
189
- this.adapter.log.info(`[consumptionHelper] Bestehende Verbrauchswerte wiederhergestellt.`);
189
+ this.adapter.log.debug(`[consumptionHelper] Bestehende Verbrauchswerte wiederhergestellt.`);
190
190
  } catch (err) {
191
191
  this.adapter.log.warn(
192
192
  `[consumptionHelper] Fehler beim Wiederherstellen der Verbrauchsstände: ${err.message}`,
@@ -2,14 +2,17 @@
2
2
 
3
3
  /**
4
4
  * controlHelper
5
- * - Überwacht States im Bereich "control"
6
- * - Steuert manuelle Aktionen: Rückspülung, Wartungsmodus
7
- * - Synchronisiert mit Pumpenmodus und Sprach-/Benachrichtigungssystem
8
- * - Erweiterung: automatische Sprach- und Benachrichtigungs-Ausgabe bei Start/Ende
5
+ * - Steuert Wartungsmodus, Rückspülung, Energie-Reset, Saison
6
+ * - Führt tägliche Umwälzprüfung (z. B. 18:00 Uhr) durch
7
+ * - Automatisches Nachpumpen, wenn Tagesziel nicht erreicht
8
+ * - Sendet Statusmeldungen über speech.queue
9
+ * - Nutzt Vorrangsteuerung über pump.mode = "controlHelper"
9
10
  */
10
11
 
11
12
  let adapter;
12
13
  let backwashTimer = null;
14
+ let dailyTimer = null;
15
+ let previousPumpMode = null;
13
16
 
14
17
  /**
15
18
  * Initialisiert den Control-Helper.
@@ -24,15 +27,154 @@ function init(a) {
24
27
  adapter.subscribeStates('control.season.active');
25
28
  adapter.subscribeStates('control.pump.backwash_start');
26
29
  adapter.subscribeStates('control.pump.maintenance_active');
27
- adapter.subscribeStates('control.energy.reset'); // NEU: Energie-Reset-Button
30
+ adapter.subscribeStates('control.energy.reset');
31
+ adapter.subscribeStates('control.circulation.check_time');
32
+
33
+ // Täglichen Check planen
34
+ _scheduleDailyCheck().catch(err =>
35
+ adapter.log.error(`[controlHelper] Fehler bei _scheduleDailyCheck(): ${err.message}`),
36
+ );
28
37
 
29
38
  adapter.log.debug('[controlHelper] Überwachung der Control-States aktiviert');
30
39
  }
31
40
 
41
+ /**
42
+ * Plant den täglichen Umwälzungscheck neu.
43
+ */
44
+ async function _scheduleDailyCheck() {
45
+ try {
46
+ if (dailyTimer) {
47
+ clearTimeout(dailyTimer);
48
+ }
49
+
50
+ const timeStr = (await adapter.getStateAsync('control.circulation.check_time'))?.val || '18:00';
51
+ const [hours, minutes] = timeStr.split(':').map(x => parseInt(x, 10));
52
+
53
+ const now = new Date();
54
+ const next = new Date();
55
+ next.setHours(hours, minutes, 0, 0);
56
+ if (next <= now) {
57
+ next.setDate(next.getDate() + 1);
58
+ }
59
+
60
+ const diffMs = next - now;
61
+ adapter.log.debug(`[controlHelper] Nächster Tages-Umwälzungscheck geplant für ${next.toLocaleTimeString()}`);
62
+
63
+ dailyTimer = setTimeout(async () => {
64
+ await _runDailyCirculationCheck();
65
+ await _scheduleDailyCheck();
66
+ }, diffMs);
67
+ } catch (err) {
68
+ adapter.log.error(`[controlHelper] Fehler bei _scheduleDailyCheck(): ${err.message}`);
69
+ }
70
+ }
71
+
72
+ /**
73
+ * Führt den täglichen Umwälzungsbericht und ggf. Nachpumpen aus.
74
+ */
75
+ async function _runDailyCirculationCheck() {
76
+ try {
77
+ adapter.log.debug('[controlHelper] Starte Tages-Umwälzungscheck ...');
78
+
79
+ const seasonActive = (await adapter.getStateAsync('status.season_active'))?.val;
80
+ const mode = (await adapter.getStateAsync('control.circulation.mode'))?.val || 'off';
81
+ const dailyTotal = Math.round((await adapter.getStateAsync('circulation.daily_total'))?.val || 0);
82
+ const dailyRequired = Math.round((await adapter.getStateAsync('circulation.daily_required'))?.val || 0);
83
+ const collector = Number((await adapter.getStateAsync('temperature.collector.current'))?.val || 0);
84
+ const pool = Number((await adapter.getStateAsync('temperature.surface.current'))?.val || 0);
85
+
86
+ if (!seasonActive) {
87
+ adapter.log.debug('[controlHelper] Saison inaktiv – Tagesprüfung übersprungen.');
88
+ return;
89
+ }
90
+
91
+ if (!dailyRequired || dailyRequired <= 0) {
92
+ await _sendSpeech('Keine Zielumwälzmenge festgelegt – Tagesbericht übersprungen.');
93
+ return;
94
+ }
95
+
96
+ const percent = Math.min(100, Math.round((dailyTotal / dailyRequired) * 100));
97
+ const missing = Math.max(0, Math.round(dailyRequired - dailyTotal));
98
+ let message = '';
99
+
100
+ switch (mode) {
101
+ case 'notify':
102
+ message = `Heutige Umwälzung: ${dailyTotal} l (${percent} %). Es fehlen noch ${missing} l. Bitte ggf. manuell nachpumpen.`;
103
+ break;
104
+
105
+ case 'manual':
106
+ message = `Heutige Umwälzung: ${dailyTotal} l (${percent} %). Es fehlen noch ${missing} l. Bitte Pumpe manuell einschalten.`;
107
+ break;
108
+
109
+ case 'auto':
110
+ if (percent >= 100) {
111
+ message = `Tagesumwälzung abgeschlossen: ${dailyTotal} l (${percent} %). Kein Nachpumpen erforderlich.`;
112
+ } else if (collector > pool) {
113
+ message = `Heutige Umwälzung: ${dailyTotal} l (${percent} %). Es fehlen ${missing} l. Nachpumpen startet automatisch (Kollektor wärmer).`;
114
+ await _startAutoPumping(missing);
115
+ return;
116
+ } else {
117
+ message = `Heutige Umwälzung: ${dailyTotal} l (${percent} %). Kein automatisches Nachpumpen, Kollektor kälter als Pool.`;
118
+ }
119
+ break;
120
+
121
+ default:
122
+ adapter.log.debug(`[controlHelper] Modus '${mode}' → keine Aktion.`);
123
+ return;
124
+ }
125
+
126
+ await _sendSpeech(message);
127
+ await adapter.setStateAsync('control.circulation.last_report', { val: new Date().toISOString(), ack: true });
128
+ } catch (err) {
129
+ adapter.log.error(`[controlHelper] Fehler beim Tagescheck: ${err.message}`);
130
+ }
131
+ }
132
+
133
+ /**
134
+ * Startet automatisches Nachpumpen.
135
+ *
136
+ * @param {number} missingLiter - Fehlende Umwälzmenge in Litern
137
+ */
138
+ async function _startAutoPumping(missingLiter) {
139
+ try {
140
+ const notify = (await adapter.getStateAsync('control.pump.notifications_enabled'))?.val;
141
+
142
+ previousPumpMode = (await adapter.getStateAsync('pump.mode'))?.val || 'auto';
143
+ await adapter.setStateAsync('pump.mode', { val: 'controlHelper', ack: true });
144
+ await adapter.setStateAsync('pump.reason', { val: 'nachpumpen', ack: true });
145
+ await adapter.setStateAsync('pump.pump_switch', { val: true, ack: false });
146
+
147
+ adapter.log.info(`[controlHelper] Automatisches Nachpumpen gestartet (${missingLiter} l fehlen).`);
148
+ if (notify) {
149
+ await _sendSpeech(`Automatisches Nachpumpen gestartet. Es fehlen ${missingLiter} Liter.`);
150
+ }
151
+
152
+ const interval = setInterval(async () => {
153
+ const total = Math.round((await adapter.getStateAsync('circulation.daily_total'))?.val || 0);
154
+ const required = Math.round((await adapter.getStateAsync('circulation.daily_required'))?.val || 0);
155
+
156
+ if (total >= required) {
157
+ clearInterval(interval);
158
+ await adapter.setStateAsync('pump.pump_switch', { val: false, ack: false });
159
+ await adapter.setStateAsync('pump.mode', { val: previousPumpMode, ack: true });
160
+ await adapter.setStateAsync('pump.reason', { val: '', ack: true });
161
+ previousPumpMode = null;
162
+
163
+ adapter.log.info('[controlHelper] Nachpumpen abgeschlossen – Tagesziel erreicht.');
164
+ if (notify) {
165
+ await _sendSpeech('Nachpumpen abgeschlossen. Tagesziel erreicht.');
166
+ }
167
+ }
168
+ }, 60 * 1000);
169
+ } catch (err) {
170
+ adapter.log.error(`[controlHelper] Fehler beim automatischen Nachpumpen: ${err.message}`);
171
+ }
172
+ }
173
+
32
174
  /**
33
175
  * Reagiert auf Änderungen der States im Bereich control.*
34
176
  *
35
- * @param {string} id - ID des geänderten States
177
+ * @param {string} id - Objekt-ID des geänderten States
36
178
  * @param {ioBroker.State} state - Neuer State-Wert
37
179
  */
38
180
  async function handleStateChange(id, state) {
@@ -41,39 +183,41 @@ async function handleStateChange(id, state) {
41
183
  return;
42
184
  }
43
185
 
44
- // === SAISONSTATUS =====================================================
186
+ // === SAISONSTATUS ===
45
187
  if (id.endsWith('control.season.active')) {
46
188
  const newVal = !!state.val;
47
189
  adapter.log.info(`[controlHelper] Poolsaison wurde ${newVal ? 'aktiviert' : 'deaktiviert'}.`);
48
190
  await adapter.setStateAsync('status.season_active', { val: newVal, ack: true });
49
191
  }
50
192
 
51
- // === WARTUNGSMODUS ====================================================
193
+ // === WARTUNGSMODUS ===
52
194
  if (id.endsWith('control.pump.maintenance_active')) {
53
195
  const active = !!state.val;
54
196
  const notify = (await adapter.getStateAsync('control.pump.notifications_enabled'))?.val;
55
197
 
56
198
  if (active) {
57
- await adapter.setStateAsync('pump.mode', { val: 'override', ack: true });
199
+ previousPumpMode = (await adapter.getStateAsync('pump.mode'))?.val || 'auto';
200
+ await adapter.setStateAsync('pump.mode', { val: 'controlHelper', ack: true });
201
+ await adapter.setStateAsync('pump.reason', { val: 'wartung', ack: true });
58
202
  await adapter.setStateAsync('pump.pump_switch', { val: false, ack: false });
59
203
  adapter.log.info('[controlHelper] Wartungsmodus aktiviert. Automatik pausiert.');
60
204
 
61
205
  if (notify) {
62
- await sendNotification(
63
- 'Wartungsmodus aktiviert. Automatikfunktionen sind vorübergehend deaktiviert.',
64
- );
206
+ await _sendSpeech('Wartungsmodus aktiviert. Automatikfunktionen deaktiviert.');
65
207
  }
66
208
  } else {
67
- await adapter.setStateAsync('pump.mode', { val: 'auto', ack: true });
68
- adapter.log.info('[controlHelper] Wartungsmodus beendet. Automatik wieder aktiv.');
209
+ await adapter.setStateAsync('pump.mode', { val: previousPumpMode, ack: true });
210
+ await adapter.setStateAsync('pump.reason', { val: '', ack: true });
211
+ previousPumpMode = null;
69
212
 
213
+ adapter.log.info('[controlHelper] Wartungsmodus beendet. Automatik wieder aktiv.');
70
214
  if (notify) {
71
- await sendNotification('Wartungsmodus beendet. Automatikbetrieb ist wieder aktiv.');
215
+ await _sendSpeech('Wartungsmodus beendet. Automatikbetrieb wieder aktiv.');
72
216
  }
73
217
  }
74
218
  }
75
219
 
76
- // === RÜCKSPÜLUNG ======================================================
220
+ // === RÜCKSPÜLUNG ===
77
221
  if (id.endsWith('control.pump.backwash_start') && state.val === true) {
78
222
  const duration = (await adapter.getStateAsync('control.pump.backwash_duration'))?.val || 1;
79
223
  const notify = (await adapter.getStateAsync('control.pump.notifications_enabled'))?.val;
@@ -86,14 +230,16 @@ async function handleStateChange(id, state) {
86
230
  }
87
231
 
88
232
  await adapter.setStateAsync('control.pump.backwash_active', { val: true, ack: true });
89
- await adapter.setStateAsync('pump.mode', { val: 'override', ack: true });
233
+ await adapter.setStateAsync('control.pump.backwash_start', { val: false, ack: true });
234
+ await adapter.setStateAsync('pump.mode', { val: 'controlHelper', ack: true });
235
+ await adapter.setStateAsync('pump.reason', { val: 'rückspülen', ack: true });
90
236
  await adapter.setStateAsync('pump.pump_switch', { val: true, ack: false });
91
237
 
92
238
  const durationText = duration === 1 ? 'eine Minute' : `${duration} Minuten`;
93
- adapter.log.info(`[controlHelper] Rückspülung gestartet (Dauer: ${duration} Minuten).`);
239
+ adapter.log.info(`[controlHelper] Rückspülung gestartet (${duration} Minuten).`);
94
240
 
95
241
  if (notify) {
96
- await sendNotification(`Rückspülung gestartet. Dauer ${durationText}.`);
242
+ await _sendSpeech(`Rückspülung gestartet. Dauer ${durationText}.`);
97
243
  }
98
244
 
99
245
  if (backwashTimer) {
@@ -104,12 +250,12 @@ async function handleStateChange(id, state) {
104
250
  try {
105
251
  await adapter.setStateAsync('pump.pump_switch', { val: false, ack: false });
106
252
  await adapter.setStateAsync('pump.mode', { val: prevMode, ack: true });
253
+ await adapter.setStateAsync('pump.reason', { val: '', ack: true });
107
254
  await adapter.setStateAsync('control.pump.backwash_active', { val: false, ack: true });
108
255
 
109
256
  adapter.log.info('[controlHelper] Rückspülung beendet. Automatik wieder aktiv.');
110
-
111
257
  if (notify) {
112
- await sendNotification('Rückspülung abgeschlossen. Automatikmodus wieder aktiv.');
258
+ await _sendSpeech('Rückspülung abgeschlossen. Automatikmodus wieder aktiv.');
113
259
  }
114
260
  } catch (err) {
115
261
  adapter.log.warn(`[controlHelper] Fehler beim Beenden der Rückspülung: ${err.message}`);
@@ -119,35 +265,39 @@ async function handleStateChange(id, state) {
119
265
  );
120
266
  }
121
267
 
122
- // === ENERGIEZÄHLER RESET =============================================
268
+ // === ENERGIEZÄHLER RESET ===
123
269
  if (id.endsWith('control.energy.reset') && state.val === true) {
124
270
  const now = new Date();
125
271
  const timestamp = now.toLocaleString('de-DE');
126
272
  const notify = (await adapter.getStateAsync('control.pump.notifications_enabled'))?.val;
127
273
 
128
- adapter.log.info(`[controlHelper] Energiezähler-Reset ausgelöst (${timestamp})`);
274
+ adapter.log.info(`[controlHelper] Energiezähler wird vollständig zurückgesetzt (${timestamp}).`);
129
275
 
130
- try {
131
- // Den eigentlichen Reset im consumptionHelper ausführen
132
- const consumptionHelper = require('../helpers/consumptionHelper');
133
- if (consumptionHelper && typeof consumptionHelper.resetAll === 'function') {
134
- await consumptionHelper.resetAll(adapter);
135
- } else {
136
- adapter.log.warn('[controlHelper] consumptionHelper.resetAll() nicht verfügbar');
137
- }
276
+ const consStates = [
277
+ 'consumption.total_kwh',
278
+ 'consumption.day_kwh',
279
+ 'consumption.week_kwh',
280
+ 'consumption.month_kwh',
281
+ 'consumption.year_kwh',
282
+ 'consumption.last_total_kwh',
283
+ 'consumption.offset_kwh',
284
+ 'costs.total_eur',
285
+ 'costs.day_eur',
286
+ 'costs.week_eur',
287
+ 'costs.month_eur',
288
+ 'costs.year_eur',
289
+ ];
138
290
 
139
- // Button wieder zurücksetzen
140
- await adapter.setStateAsync('control.energy.reset', { val: false, ack: true });
291
+ for (const sid of consStates) {
292
+ await adapter.setStateAsync(sid, { val: 0, ack: true });
293
+ }
141
294
 
142
- // Log & Sprach-/Benachrichtigungsausgabe
143
- const msg = `Energiezähler wurde am ${timestamp} vollständig zurückgesetzt.`;
144
- adapter.log.info(`[controlHelper] ${msg}`);
295
+ await adapter.setStateAsync('control.energy.reset', { val: false, ack: true });
145
296
 
146
- if (notify) {
147
- await sendNotification(msg);
148
- }
149
- } catch (err) {
150
- adapter.log.error(`[controlHelper] Fehler beim Energiezähler-Reset: ${err.message}`);
297
+ const msg = `Energiezähler und Kosten wurden am ${timestamp} vollständig zurückgesetzt.`;
298
+ adapter.log.info(`[controlHelper] ${msg}`);
299
+ if (notify) {
300
+ await _sendSpeech(msg);
151
301
  }
152
302
  }
153
303
  } catch (err) {
@@ -156,32 +306,35 @@ async function handleStateChange(id, state) {
156
306
  }
157
307
 
158
308
  /**
159
- * Leitet Benachrichtigungen an speechHelper weiter
160
- * (setzt nur speech.last_text, Versand erfolgt dort)
309
+ * Sendet Text an speech.queue
161
310
  *
162
- * @param {string} text - Nachrichtentext, der gesendet werden soll
311
+ * @param {string} text - Nachricht, die an speech.queue gesendet werden soll
163
312
  */
164
- async function sendNotification(text) {
313
+ async function _sendSpeech(text) {
165
314
  if (!text) {
166
315
  return;
167
316
  }
168
-
169
317
  try {
170
- await adapter.setStateAsync('speech.last_text', { val: text, ack: false });
171
- adapter.log.debug(`[controlHelper] Benachrichtigung an speechHelper weitergeleitet: ${text}`);
318
+ await adapter.setStateAsync('speech.queue', { val: text, ack: false });
319
+ adapter.log.debug(`[controlHelper] Nachricht an speech.queue: ${text}`);
172
320
  } catch (err) {
173
- adapter.log.warn(`[controlHelper] Fehler beim Weiterleiten der Benachrichtigung: ${err.message}`);
321
+ adapter.log.warn(`[controlHelper] Fehler beim Senden an speech.queue: ${err.message}`);
174
322
  }
175
323
  }
176
324
 
177
325
  /**
178
- * Stoppt Timer und räumt Ressourcen auf.
326
+ * Aufräumen
179
327
  */
180
328
  function cleanup() {
181
329
  if (backwashTimer) {
182
330
  clearTimeout(backwashTimer);
183
331
  backwashTimer = null;
184
332
  }
333
+ if (dailyTimer) {
334
+ clearTimeout(dailyTimer);
335
+ dailyTimer = null;
336
+ }
337
+ previousPumpMode = null;
185
338
  }
186
339
 
187
340
  module.exports = { init, handleStateChange, cleanup };
@@ -42,10 +42,10 @@ const debugLogHelper = {
42
42
  if (target !== 'none') {
43
43
  this._subscribeTarget(target);
44
44
  } else {
45
- adapter.log.info('[debugLogHelper] Kein Bereich ausgewählt – Logger inaktiv.');
45
+ adapter.log.debug('[debugLogHelper] Kein Bereich ausgewählt – Logger inaktiv.');
46
46
  }
47
47
 
48
- adapter.log.info('[debugLogHelper] Initialisierung abgeschlossen');
48
+ adapter.log.debug('[debugLogHelper] Initialisierung abgeschlossen');
49
49
  },
50
50
 
51
51
  /**
@@ -104,18 +104,18 @@ const debugLogHelper = {
104
104
  }
105
105
  if (this.subscribedTarget && this.subscribedTarget !== 'none') {
106
106
  this.adapter.unsubscribeStates(`${this.subscribedTarget}.*`);
107
- this.adapter.log.info(`[debugLogHelper] Überwachung für Bereich "${this.subscribedTarget}" beendet.`);
107
+ this.adapter.log.debug(`[debugLogHelper] Überwachung für Bereich "${this.subscribedTarget}" beendet.`);
108
108
  }
109
109
 
110
110
  this.subscribedTarget = newTarget;
111
111
 
112
112
  if (newTarget === 'none') {
113
- this.adapter.log.info('[debugLogHelper] Kein Bereich aktiv.');
113
+ this.adapter.log.debug('[debugLogHelper] Kein Bereich aktiv.');
114
114
  return;
115
115
  }
116
116
 
117
117
  this._subscribeTarget(newTarget);
118
- this.adapter.log.info(`[debugLogHelper] Überwachung für Bereich "${newTarget}" gestartet.`);
118
+ this.adapter.log.debug(`[debugLogHelper] Überwachung für Bereich "${newTarget}" gestartet.`);
119
119
  await this._appendLog(
120
120
  `\n=== Debug-Log gestartet: Bereich "${newTarget}" @ ${new Date().toLocaleString()} ===\n`,
121
121
  );
@@ -18,7 +18,7 @@ const frostHelper = {
18
18
  // Minütlicher Check
19
19
  this._scheduleCheck();
20
20
 
21
- this.adapter.log.info('[frostHelper] initialisiert (Prüfung alle 60s)');
21
+ this.adapter.log.debug('[frostHelper] initialisiert (Prüfung alle 60s)');
22
22
  },
23
23
 
24
24
  _scheduleCheck() {
@@ -32,6 +32,13 @@ const frostHelper = {
32
32
 
33
33
  async _checkFrost() {
34
34
  try {
35
+ // --- NEU: Vorrangprüfung durch ControlHelper ---
36
+ const pumpStatus = (await this.adapter.getStateAsync('pump.status'))?.val || '';
37
+ if (pumpStatus.includes('controlHelper')) {
38
+ this.adapter.log.debug('[frostHelper] Vorrang durch ControlHelper aktiv – Frostschutz pausiert.');
39
+ return;
40
+ }
41
+
35
42
  // Nur aktiv im AUTO-Modus
36
43
  const mode = (await this.adapter.getStateAsync('pump.mode'))?.val;
37
44
  if (mode !== 'auto') {
@@ -68,13 +75,22 @@ const frostHelper = {
68
75
  shouldRun = false;
69
76
  }
70
77
 
78
+ // --- NEU: Sprachsignal für Frostschutz setzen ---
79
+ const oldVal = (await this.adapter.getStateAsync('speech.frost_active'))?.val;
80
+ if (oldVal !== shouldRun) {
81
+ await this.adapter.setStateChangedAsync('speech.frost_active', {
82
+ val: shouldRun,
83
+ ack: true,
84
+ });
85
+ }
86
+
71
87
  // Schalten nur, wenn sich etwas ändert
72
88
  if (shouldRun !== pumpActive) {
73
89
  await this.adapter.setStateAsync('pump.pump_switch', {
74
90
  val: shouldRun,
75
91
  ack: false,
76
92
  });
77
- this.adapter.log.info(
93
+ this.adapter.log.debug(
78
94
  `[frostHelper] Frostschutz → Pumpe ${shouldRun ? 'EIN' : 'AUS'} (Außen=${outside}°C, Grenze=${frostTemp}°C)`,
79
95
  );
80
96
  }
@@ -38,21 +38,21 @@ const pumpHelper = {
38
38
  .then(s => {
39
39
  const val = this._parseNumber(s?.val);
40
40
  this.adapter.setStateAsync('pump.current_power', { val, ack: true });
41
- this.adapter.log.info(
41
+ this.adapter.log.debug(
42
42
  `[pumpHelper] Überwache Leistung von ${this.currentPowerId} (Startwert: ${val})`,
43
43
  );
44
44
  })
45
45
  .catch(() => {
46
- this.adapter.log.info(`[pumpHelper] Überwache Leistung von ${this.currentPowerId}`);
46
+ this.adapter.log.debug(`[pumpHelper] Überwache Leistung von ${this.currentPowerId}`);
47
47
  });
48
48
  } else {
49
- this.adapter.log.info('[pumpHelper] Keine Objekt-ID für aktuelle Leistung konfiguriert');
49
+ this.adapter.log.debug('[pumpHelper] Keine Objekt-ID für aktuelle Leistung konfiguriert');
50
50
  }
51
51
 
52
52
  // Echte Steckdose beobachten (Status-Spiegelung)
53
53
  if (this.deviceId) {
54
54
  this.adapter.subscribeForeignStates(this.deviceId);
55
- this.adapter.log.info(`[pumpHelper] Überwache Steckdose: ${this.deviceId}`);
55
+ this.adapter.log.debug(`[pumpHelper] Überwache Steckdose: ${this.deviceId}`);
56
56
 
57
57
  // NEU: Initialwert übernehmen
58
58
  this.adapter
@@ -61,17 +61,17 @@ const pumpHelper = {
61
61
  if (s) {
62
62
  const val = !!s.val;
63
63
  this.adapter.setStateAsync('pump.pump_switch', { val, ack: true });
64
- this.adapter.log.info(`[pumpHelper] Initialer Pumpenstatus von Steckdose übernommen: ${val}`);
64
+ this.adapter.log.debug(`[pumpHelper] Initialer Pumpenstatus von Steckdose übernommen: ${val}`);
65
65
  }
66
66
  })
67
67
  .catch(err =>
68
68
  this.adapter.log.warn(`[pumpHelper] Konnte initialen Pumpenstatus nicht laden: ${err.message}`),
69
69
  );
70
70
  } else {
71
- this.adapter.log.info('[pumpHelper] Keine Objekt-ID für Pumpen-Steckdose konfiguriert');
71
+ this.adapter.log.debug('[pumpHelper] Keine Objekt-ID für Pumpen-Steckdose konfiguriert');
72
72
  }
73
73
 
74
- this.adapter.log.info('[pumpHelper] initialisiert');
74
+ this.adapter.log.debug('[pumpHelper] initialisiert');
75
75
  // Initialer Status
76
76
  this._updateStatus().catch(err =>
77
77
  this.adapter.log.warn(`[pumpHelper] Initiales Status-Update fehlgeschlagen: ${err.message}`),
@@ -171,12 +171,45 @@ const pumpHelper = {
171
171
  case 'auto':
172
172
  status = 'EIN (automatik)';
173
173
  break;
174
+ case 'controlHelper':
175
+ try {
176
+ const reason = (await this.adapter.getStateAsync('pump.reason'))?.val || '';
177
+ status = reason ? `EIN (${reason})` : 'EIN (Systemsteuerung)';
178
+ } catch {
179
+ status = 'EIN (Systemsteuerung)';
180
+ }
181
+ break;
182
+ case 'speechTextHelper':
183
+ try {
184
+ const reason = (await this.adapter.getStateAsync('pump.reason'))?.val || '';
185
+ status = reason ? `EIN (${reason})` : 'EIN (Sprachsteuerung)';
186
+ } catch {
187
+ status = 'EIN (Sprachsteuerung)';
188
+ }
189
+ break;
174
190
  default:
175
191
  status = 'EIN';
176
192
  break;
193
+ case 'frostHelper':
194
+ try {
195
+ const reason = (await this.adapter.getStateAsync('pump.reason'))?.val || '';
196
+ status = reason ? `EIN (${reason})` : 'EIN (Frostschutz)';
197
+ } catch {
198
+ status = 'EIN (Frostschutz)';
199
+ }
200
+ break;
201
+
202
+ case 'timeHelper':
203
+ try {
204
+ const reason = (await this.adapter.getStateAsync('pump.reason'))?.val || '';
205
+ status = reason ? `EIN (${reason})` : 'EIN (Zeitsteuerung)';
206
+ } catch {
207
+ status = 'EIN (Zeitsteuerung)';
208
+ }
209
+ break;
177
210
  }
178
211
  }
179
- await this.adapter.setStateAsync('pump.status', {
212
+ await this.adapter.setStateChangedAsync('pump.status', {
180
213
  val: status,
181
214
  ack: true,
182
215
  });