iobroker.poolcontrol 0.3.0 → 0.3.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/README.md CHANGED
@@ -121,6 +121,14 @@ Funktionen können sich ändern – bitte regelmäßig den Changelog beachten.
121
121
  ## Changelog
122
122
  ### **WORK IN PROGRESS**
123
123
 
124
+ ### 0.3.1 (2025-10-18)
125
+ - FrostHelper stabilisiert:
126
+ - Feste Hysterese von +2 °C (bisher +1 °C)
127
+ - Ganzzahl-Rundung eingeführt zur Vermeidung von Schaltflattern um 3 °C
128
+ - Keine Änderungen an States oder Konfiguration erforderlich
129
+
130
+ ---
131
+
124
132
  ### 0.3.0 (12.10.2025)
125
133
  **Neu:** Intelligentes Pumpen-Monitoring & Lernsystem
126
134
 
@@ -64,6 +64,10 @@
64
64
  "lg": 3,
65
65
  "xl": 3,
66
66
  "newLine": true
67
+ },
68
+ "divider_hardware1": {
69
+ "type": "divider",
70
+ "newLine": true
67
71
  }
68
72
  }
69
73
  },
package/io-package.json CHANGED
@@ -1,8 +1,21 @@
1
1
  {
2
2
  "common": {
3
3
  "name": "poolcontrol",
4
- "version": "0.3.0",
4
+ "version": "0.3.1",
5
5
  "news": {
6
+ "0.3.1": {
7
+ "en": "Frost protection logic stabilized: fixed hysteresis of +2 °C and rounded temperature values to avoid pump switching fluctuations around 3 °C.",
8
+ "de": "Frostschutz-Logik stabilisiert: feste Hysterese von +2 °C und gerundete Temperaturwerte zur Vermeidung von Pumpenschaltflattern um 3 °C.",
9
+ "ru": "Логика защиты от замерзания стабилизирована: фиксированная гистерезис +2 °C и округленные значения температуры для предотвращения колебаний включения насоса около 3 °C.",
10
+ "pt": "Lógica de proteção contra congelamento estabilizada: histerese fixa de +2 °C e valores de temperatura arredondados para evitar flutuações de comutação da bomba em torno de 3 °C.",
11
+ "nl": "Vorstbeschermingslogica gestabiliseerd: vaste hysterese van +2 °C en afgeronde temperatuurwaarden om pompfluctuaties rond 3 °C te voorkomen.",
12
+ "fr": "Logique de protection antigel stabilisée : hystérésis fixe de +2 °C et valeurs de température arrondies pour éviter les fluctuations de commutation de la pompe autour de 3 °C.",
13
+ "it": "Logica di protezione antigelo stabilizzata: isteresi fissa di +2 °C e valori di temperatura arrotondati per evitare fluttuazioni di commutazione della pompa intorno a 3 °C.",
14
+ "es": "Lógica de protección contra heladas estabilizada: histéresis fija de +2 °C y valores de temperatura redondeados para evitar fluctuaciones de conmutación de la bomba alrededor de 3 °C.",
15
+ "pl": "Ustabilizowano logikę ochrony przed zamarzaniem: stała histereza +2 °C i zaokrąglone wartości temperatury, aby uniknąć wahań przełączania pompy w okolicach 3 °C.",
16
+ "uk": "Стабілізовано логіку захисту від замерзання: фіксована гістерезис +2 °C і округлені значення температури, щоб уникнути коливань увімкнення насоса біля 3 °C.",
17
+ "zh-cn": "防冻逻辑稳定:固定 +2 °C 滞后并四舍五入温度值,以避免泵在 3 °C 附近频繁切换。"
18
+ },
6
19
  "0.3.0": {
7
20
  "en": "Added real pump flow calculation, live monitoring, and self-learning normal range system for pump analysis.",
8
21
  "de": "Reelle Durchflussberechnung, Liveüberwachung und selbstlernendes Normalbereich-System zur Pumpenanalyse hinzugefügt.",
@@ -14,7 +27,7 @@
14
27
  "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.",
15
28
  "pl": "Dodano obliczanie rzeczywistego przepływu pompy, monitorowanie na żywo i samouczący się system normalnego zakresu do analizy pompy.",
16
29
  "zh-cn": "添加了实际泵流量计算、实时监控以及用于泵分析的自学习正常范围系统。"
17
- },
30
+ },
18
31
  "0.2.2": {
19
32
  "en": "Added automatic backwash reminder with speech and log notifications.",
20
33
  "de": "Automatische Rückspülerinnerung mit Sprach- und Log-Benachrichtigung hinzugefügt.",
@@ -68,10 +68,15 @@ const frostHelper = {
68
68
  const pumpActive = (await this.adapter.getStateAsync('pump.pump_switch'))?.val;
69
69
  let shouldRun = pumpActive;
70
70
 
71
- // Logik: einschalten bei <= frostTemp, ausschalten bei >= frostTemp+1
72
- if (outside <= frostTemp) {
71
+ // FIX: Stabilere Logik mit fester Hysterese von +2 °C und Ganzzahl-Rundung
72
+ const outsideRounded = Math.round(outside);
73
+ const frostTempRounded = Math.round(frostTemp);
74
+
75
+ // Einschalten bei <= frostTempRounded
76
+ // Ausschalten erst bei >= frostTempRounded + 2 (2 K Hysterese)
77
+ if (outsideRounded <= frostTempRounded) {
73
78
  shouldRun = true;
74
- } else if (outside >= frostTemp + 1) {
79
+ } else if (outsideRounded >= frostTempRounded + 2) {
75
80
  shouldRun = false;
76
81
  }
77
82
 
@@ -20,11 +20,12 @@
20
20
  *
21
21
  * Alle Zielstates sind persistent (siehe pumpStates2.js).
22
22
  * ----------------------------------------------------------
23
- * Version: 1.0.0
23
+ * Version: 1.0.3
24
24
  */
25
25
 
26
26
  const pumpHelper2 = {
27
27
  adapter: null,
28
+ lastKnownFlow: 0, // merkt sich den letzten gültigen Durchflusswert
28
29
 
29
30
  /**
30
31
  * Initialisiert den PumpHelper2
@@ -61,11 +62,20 @@ const pumpHelper2 = {
61
62
  await this._updateLiveValues();
62
63
  }
63
64
 
64
- // Pumpenstatus-Änderung → ggf. letzten Durchflusswert speichern
65
+ // Pumpenstatus-Änderung → letzten Durchflusswert sichern
65
66
  if (id.endsWith('pump.pump_switch')) {
66
67
  const pumpOn = state.val === true;
67
68
  if (!pumpOn) {
68
- await this._storeLastFlowValue();
69
+ // FIX: Verwende den zuletzt gemerkten Wert, statt live zu lesen (verhindert 0-Durchfluss)
70
+ const flowBeforeStop = this.lastKnownFlow;
71
+ if (flowBeforeStop > 0) {
72
+ await this._setIfChanged('pump.live.last_flow_lh', flowBeforeStop);
73
+ this.adapter.log.debug(
74
+ `[pumpHelper2] FIX: Letzter Durchflusswert gesichert: ${flowBeforeStop} l/h`,
75
+ );
76
+ } else {
77
+ this.adapter.log.debug('[pumpHelper2] Kein gespeicherter Durchflusswert vorhanden.');
78
+ }
69
79
  }
70
80
  }
71
81
  },
@@ -94,6 +104,11 @@ const pumpHelper2 = {
94
104
  // Reeller Durchfluss
95
105
  const flowCurrentLh = Math.round(nominalFlow * (currentPower / maxPower) * 10) / 10; // 1 Nachkommastelle
96
106
 
107
+ // Letzten gültigen Wert merken
108
+ if (flowCurrentLh > 0) {
109
+ this.lastKnownFlow = flowCurrentLh;
110
+ }
111
+
97
112
  // In States schreiben
98
113
  await this._setIfChanged('pump.live.current_power_w', currentPower);
99
114
  await this._setIfChanged('pump.live.flow_current_lh', flowCurrentLh);
@@ -107,19 +122,6 @@ const pumpHelper2 = {
107
122
  }
108
123
  },
109
124
 
110
- /**
111
- * Speichert den letzten bekannten Durchflusswert beim Pumpen-Stopp.
112
- */
113
- async _storeLastFlowValue() {
114
- try {
115
- const currentFlow = await this._getNumber('pump.live.flow_current_lh');
116
- await this._setIfChanged('pump.live.last_flow_lh', currentFlow);
117
- this.adapter.log.debug(`[pumpHelper2] Letzter Durchflusswert gespeichert: ${currentFlow} l/h`);
118
- } catch (err) {
119
- this.adapter.log.warn(`[pumpHelper2] Fehler bei _storeLastFlowValue: ${err.message}`);
120
- }
121
- },
122
-
123
125
  /**
124
126
  * Liest einen numerischen Statewert (oder 0 bei Fehler).
125
127
  *
@@ -53,7 +53,7 @@ const pumpHelper3 = {
53
53
  * @param {ioBroker.State} state - neuer Statewert
54
54
  */
55
55
  async handleStateChange(id, state) {
56
- if (!state || state.ack === false) {
56
+ if (!state) {
57
57
  return;
58
58
  }
59
59
 
@@ -21,9 +21,24 @@ const runtimeHelper = {
21
21
  resetTimer: null,
22
22
  liveTimer: null, // Timer für Live-Updates
23
23
 
24
- init(adapter) {
24
+ /**
25
+ * Initialisiert den Runtime-Helper.
26
+ * Führt eine kurze Startverzögerung ein, um sicherzustellen,
27
+ * dass persistente States nach einer Überinstallation korrekt geladen werden.
28
+ *
29
+ * @param {ioBroker.Adapter} adapter - Aktive ioBroker-Adapterinstanz.
30
+ * @returns {Promise<void>}
31
+ */
32
+ async init(adapter) {
25
33
  this.adapter = adapter;
26
34
 
35
+ // ------------------------------------------------------
36
+ // NEU: Kurze Startverzögerung, damit ioBroker persistente States
37
+ // vollständig aus der Datenbank laden kann (Überinstallationsschutz)
38
+ // ------------------------------------------------------
39
+ this.adapter.log.debug('[runtimeHelper] Warte 3 Sekunden, um persistente States zu laden ...');
40
+ await new Promise(resolve => setTimeout(resolve, 3000));
41
+
27
42
  // Pumpenschalter überwachen
28
43
  this.adapter.subscribeStates('pump.pump_switch');
29
44
 
@@ -52,6 +67,13 @@ const runtimeHelper = {
52
67
  const seasonRaw = (await this.adapter.getStateAsync('runtime.season_total'))?.val;
53
68
  const countRaw = (await this.adapter.getStateAsync('runtime.start_count_today'))?.val;
54
69
 
70
+ // FIX: Falls States leer oder neu angelegt sind, Warnhinweis ausgeben und Werte nicht überschreiben
71
+ if (!totalRaw && !seasonRaw) {
72
+ this.adapter.log.info(
73
+ '[runtimeHelper] Keine gespeicherten Laufzeiten gefunden – möglicherweise neue oder überinstallierte Instanz. Laufzeiten starten bei 0.',
74
+ );
75
+ }
76
+
55
77
  // >>> NEU: Formatierten Text (z. B. "3h 12m 5s") in Sekunden umwandeln
56
78
  this.runtimeTotal = this._parseFormattedTimeToSeconds(totalRaw);
57
79
  this.runtimeToday = this._parseFormattedTimeToSeconds(todayRaw);
@@ -134,6 +156,16 @@ const runtimeHelper = {
134
156
  await this.adapter.setStateAsync('runtime.season_total', { val: formattedSeason, ack: true });
135
157
  await this.adapter.setStateAsync('runtime.start_count_today', { val: this.startCountToday, ack: true });
136
158
 
159
+ // Poolparameter laden (vor Durchflussprüfung!)
160
+ const poolSize = (await this.adapter.getStateAsync('general.pool_size'))?.val || 0;
161
+ const minCirc = (await this.adapter.getStateAsync('general.min_circulation_per_day'))?.val || 1;
162
+
163
+ // daily_required immer direkt setzen – auch ohne Durchfluss
164
+ const dailyRequired = Math.round(poolSize * minCirc);
165
+ if (dailyRequired > 0) {
166
+ await this.adapter.setStateAsync('circulation.daily_required', { val: dailyRequired, ack: true });
167
+ }
168
+
137
169
  // Umwälzmenge berechnen
138
170
  // Reeller Durchflusswert aus pump.live.flow_current_lh
139
171
  const liveFlowLh = (await this.adapter.getStateAsync('pump.live.flow_current_lh'))?.val || 0;
@@ -143,19 +175,26 @@ const runtimeHelper = {
143
175
  return;
144
176
  }
145
177
 
146
- // Poolparameter laden
147
- const poolSize = (await this.adapter.getStateAsync('general.pool_size'))?.val || 0;
148
- const minCirc = (await this.adapter.getStateAsync('general.min_circulation_per_day'))?.val || 1;
149
-
150
178
  // Berechnung der realen Tagesumwälzung (Liter)
151
179
  const dailyTotal = Math.round((effectiveToday / 3600) * liveFlowLh);
152
- const dailyRequired = Math.round(poolSize * minCirc);
153
180
  const dailyRemaining = Math.max(dailyRequired - dailyTotal, 0);
154
181
 
155
- // Werte schreiben
156
- await this.adapter.setStateAsync('circulation.daily_total', { val: dailyTotal, ack: true });
157
- await this.adapter.setStateAsync('circulation.daily_required', { val: dailyRequired, ack: true });
158
- await this.adapter.setStateAsync('circulation.daily_remaining', { val: dailyRemaining, ack: true });
182
+ // Bestehende Werte für Total/Remaining laden
183
+ const oldTotal = (await this.adapter.getStateAsync('circulation.daily_total'))?.val || 0;
184
+ const oldRemaining = (await this.adapter.getStateAsync('circulation.daily_remaining'))?.val || 0;
185
+
186
+ // Nur schreiben, wenn tatsächlich sinnvolle Livewerte vorliegen
187
+ if (liveFlowLh > 0 && dailyTotal > 0) {
188
+ await this.adapter.setStateAsync('circulation.daily_total', { val: dailyTotal, ack: true });
189
+ await this.adapter.setStateAsync('circulation.daily_remaining', { val: dailyRemaining, ack: true });
190
+ this.adapter.log.debug(
191
+ `[runtimeHelper] Circulation-Werte aktualisiert (Total=${dailyTotal}, Required=${dailyRequired}, Remaining=${dailyRemaining})`,
192
+ );
193
+ } else {
194
+ this.adapter.log.debug(
195
+ `[runtimeHelper] Keine gültigen Live-Daten – bestehende Werte bleiben erhalten (Total=${oldTotal}, Required=${dailyRequired}, Remaining=${oldRemaining})`,
196
+ );
197
+ }
159
198
  } catch (err) {
160
199
  this.adapter.log.warn(`[runtimeHelper] Fehler beim Update der States: ${err.message}`);
161
200
  }
@@ -204,12 +243,30 @@ const runtimeHelper = {
204
243
  nextMidnight.setHours(24, 0, 0, 0);
205
244
  const msUntilMidnight = nextMidnight - now;
206
245
 
207
- this.resetTimer = setTimeout(() => {
246
+ this.resetTimer = setTimeout(async () => {
208
247
  this.runtimeToday = 0;
209
248
  this.startCountToday = 0;
210
249
  this.lastOn = this.isRunning ? Date.now() : null;
250
+
251
+ // Laufzeiten zurücksetzen
211
252
  this._updateStates();
253
+
254
+ // --- NEU: Circulation-Werte um Mitternacht zurücksetzen ---
255
+ await this.adapter.setStateAsync('circulation.daily_total', { val: 0, ack: true });
256
+
257
+ // daily_required neu berechnen (optional, falls sich Poolgröße geändert hat)
258
+ const poolSize = (await this.adapter.getStateAsync('general.pool_size'))?.val || 0;
259
+ const minCirc = (await this.adapter.getStateAsync('general.min_circulation_per_day'))?.val || 1;
260
+ const dailyRequired = Math.round(poolSize * minCirc);
261
+ await this.adapter.setStateAsync('circulation.daily_required', { val: dailyRequired, ack: true });
262
+
263
+ // 👉 daily_remaining neue berechnen auf Grundlage von daily_required
264
+ await this.adapter.setStateAsync('circulation.daily_remaining', { val: dailyRequired, ack: true });
265
+
266
+ // Nächsten Reset planen
212
267
  this._scheduleDailyReset();
268
+
269
+ this.adapter.log.debug('[runtimeHelper] Tagesreset (Runtime + Circulation) ausgeführt.');
213
270
  }, msUntilMidnight);
214
271
  },
215
272
 
@@ -347,6 +347,30 @@ async function createControlStates(adapter) {
347
347
  } catch (err) {
348
348
  adapter.log.error(`[controlStates] Fehler beim Erstellen der Control-States: ${err.message}`);
349
349
  }
350
+ // ------------------------------------------------------
351
+ // Control – Hardware
352
+ // ------------------------------------------------------
353
+ await adapter.setObjectNotExistsAsync('control.hardware', {
354
+ type: 'channel',
355
+ common: { name: 'Hardware-Steuerung (externe Boxen)' },
356
+ native: {},
357
+ });
358
+
359
+ // TempBox aktivieren/deaktivieren
360
+ await adapter.setObjectNotExistsAsync('control.hardware.use_tempbox', {
361
+ type: 'state',
362
+ common: {
363
+ name: 'Externe Temperaturbox verwenden',
364
+ desc: 'Wenn aktiviert, nutzt PoolControl die Werte der externen TempBox anstelle der manuellen Temperatursensoren.',
365
+ type: 'boolean',
366
+ role: 'switch',
367
+ def: false,
368
+ read: true,
369
+ write: true,
370
+ persist: true,
371
+ },
372
+ native: {},
373
+ });
350
374
  }
351
375
 
352
376
  module.exports = { createControlStates };
@@ -0,0 +1,184 @@
1
+ 'use strict';
2
+
3
+ /**
4
+ * hardwareStates.js – Bereich für externe Boxen (TempBox, TasterBox usw.)
5
+ * Überarbeitete Version: Checkboxen & settings_enabled entfernt
6
+ */
7
+
8
+ /**
9
+ * Erstellt die Objektstruktur für alle Hardware-Boxen (z. B. TempBox, TasterBox).
10
+ * Diese Version enthält keine Instanz-Checkboxen oder settings_enabled States mehr.
11
+ *
12
+ * @param {object} adapter - ioBroker Adapterinstanz
13
+ */
14
+ async function createHardwareStates(adapter) {
15
+ try {
16
+ // ------------------------------------------------------
17
+ // Oberordner: Hardware
18
+ // ------------------------------------------------------
19
+ await adapter.setObjectNotExistsAsync('hardware', {
20
+ type: 'channel',
21
+ common: { name: 'Hardware-Boxen (Sensoren & externe Geräte)' },
22
+ native: {},
23
+ });
24
+
25
+ // ======================================================
26
+ // TEMPERATUR-BOX (ESP32)
27
+ // ======================================================
28
+ await adapter.setObjectNotExistsAsync('hardware.tempbox', {
29
+ type: 'channel',
30
+ common: { name: 'Temperatur-Box (ESP32)' },
31
+ native: {},
32
+ });
33
+
34
+ // Statusmeldung: Erkennung/Verbindung
35
+ await adapter.setObjectNotExistsAsync('hardware.tempbox.status_detected', {
36
+ type: 'state',
37
+ common: {
38
+ name: 'Box-Status',
39
+ desc: 'Status der automatischen Erkennung der Temperatur-Box (z. B. verbunden/nicht gefunden)',
40
+ type: 'string',
41
+ role: 'text',
42
+ read: true,
43
+ write: false,
44
+ def: 'nicht gefunden',
45
+ },
46
+ native: {},
47
+ });
48
+
49
+ // LED-Status
50
+ await adapter.setObjectNotExistsAsync('hardware.tempbox.status_led', {
51
+ type: 'state',
52
+ common: {
53
+ name: 'LED-Status (TempBox)',
54
+ desc: 'Status der blauen LED auf der Temperatur-Box',
55
+ type: 'string',
56
+ role: 'text',
57
+ read: true,
58
+ write: false,
59
+ def: 'unbekannt',
60
+ },
61
+ native: {},
62
+ });
63
+
64
+ // Firmware-Version
65
+ await adapter.setObjectNotExistsAsync('hardware.tempbox.fw_version', {
66
+ type: 'state',
67
+ common: {
68
+ name: 'Box-Firmware-Version',
69
+ desc: 'Firmware-Version der Temperatur-Box (ESP32)',
70
+ type: 'string',
71
+ role: 'text',
72
+ read: true,
73
+ write: false,
74
+ def: '',
75
+ },
76
+ native: {},
77
+ });
78
+
79
+ // Box-ID
80
+ await adapter.setObjectNotExistsAsync('hardware.tempbox.box_id', {
81
+ type: 'state',
82
+ common: {
83
+ name: 'Box-ID (TempBox)',
84
+ desc: 'Eindeutige Kennung der Temperatur-Box (z. B. PC-TB-01)',
85
+ type: 'string',
86
+ role: 'text',
87
+ read: true,
88
+ write: false,
89
+ def: '',
90
+ },
91
+ native: {},
92
+ });
93
+
94
+ // Sensorsystem-Status
95
+ await adapter.setObjectNotExistsAsync('hardware.tempbox.status_sensors', {
96
+ type: 'state',
97
+ common: {
98
+ name: 'Sensorsystem-Status',
99
+ desc: 'Meldung zum Zustand der Temperatursensoren in der TempBox',
100
+ type: 'string',
101
+ role: 'text',
102
+ read: true,
103
+ write: false,
104
+ def: 'keine Daten',
105
+ },
106
+ native: {},
107
+ });
108
+
109
+ // ------------------------------------------------------
110
+ // TASTER-BOX (ESP32)
111
+ // ------------------------------------------------------
112
+ await adapter.setObjectNotExistsAsync('hardware.tasterbox', {
113
+ type: 'channel',
114
+ common: { name: 'Taster-Box (ESP32)' },
115
+ native: {},
116
+ });
117
+
118
+ // Statusmeldung
119
+ await adapter.setObjectNotExistsAsync('hardware.tasterbox.status_detected', {
120
+ type: 'state',
121
+ common: {
122
+ name: 'Box-Status',
123
+ desc: 'Status der automatischen Erkennung der Taster-Box (z. B. verbunden/nicht gefunden)',
124
+ type: 'string',
125
+ role: 'text',
126
+ read: true,
127
+ write: false,
128
+ def: 'nicht gefunden',
129
+ },
130
+ native: {},
131
+ });
132
+
133
+ // Firmware-Version
134
+ await adapter.setObjectNotExistsAsync('hardware.tasterbox.fw_version', {
135
+ type: 'state',
136
+ common: {
137
+ name: 'Box-Firmware-Version',
138
+ desc: 'Firmware-Version der Taster-Box (ESP32)',
139
+ type: 'string',
140
+ role: 'text',
141
+ read: true,
142
+ write: false,
143
+ def: '',
144
+ },
145
+ native: {},
146
+ });
147
+
148
+ // Box-ID
149
+ await adapter.setObjectNotExistsAsync('hardware.tasterbox.box_id', {
150
+ type: 'state',
151
+ common: {
152
+ name: 'Box-ID (TasterBox)',
153
+ desc: 'Eindeutige Kennung der Taster-Box (z. B. PC-TB-02)',
154
+ type: 'string',
155
+ role: 'text',
156
+ read: true,
157
+ write: false,
158
+ def: '',
159
+ },
160
+ native: {},
161
+ });
162
+
163
+ // Status der Taster
164
+ await adapter.setObjectNotExistsAsync('hardware.tasterbox.status_buttons', {
165
+ type: 'state',
166
+ common: {
167
+ name: 'Status der Taster',
168
+ desc: 'Textmeldung zum Zustand der Taster (z. B. gedrückt, losgelassen, keine Verbindung)',
169
+ type: 'string',
170
+ role: 'text',
171
+ read: true,
172
+ write: false,
173
+ def: 'unbekannt',
174
+ },
175
+ native: {},
176
+ });
177
+
178
+ adapter.log.debug('[hardwareStates] Hardware-State-Struktur erfolgreich angelegt (bereinigt ohne Checkboxen).');
179
+ } catch (err) {
180
+ adapter.log.error(`[hardwareStates] Fehler beim Anlegen der Hardware-States: ${err.message}`);
181
+ }
182
+ }
183
+
184
+ module.exports = { createHardwareStates };
@@ -12,7 +12,7 @@
12
12
  * Wird durch pumpHelper3.js verwaltet.
13
13
  * Alle Werte sind persistent (persist: true).
14
14
  * ----------------------------------------------------------
15
- * Version: 1.0.0
15
+ * Version: 1.0.1
16
16
  */
17
17
 
18
18
  /**
@@ -49,7 +49,10 @@ async function createPumpStates3(adapter) {
49
49
  },
50
50
  native: {},
51
51
  });
52
- await adapter.setStateAsync('pump.learning.learned_avg_power_w', { val: 0, ack: true });
52
+ const existingPower = await adapter.getStateAsync('pump.learning.learned_avg_power_w');
53
+ if (existingPower === null || existingPower.val === null || existingPower.val === undefined) {
54
+ await adapter.setStateAsync('pump.learning.learned_avg_power_w', { val: 0, ack: true });
55
+ }
53
56
 
54
57
  // ------------------------------------------------------
55
58
  // Durchschnittlicher Durchfluss (L/h)
@@ -68,7 +71,10 @@ async function createPumpStates3(adapter) {
68
71
  },
69
72
  native: {},
70
73
  });
71
- await adapter.setStateAsync('pump.learning.learned_avg_flow_lh', { val: 0, ack: true });
74
+ const existingFlow = await adapter.getStateAsync('pump.learning.learned_avg_flow_lh');
75
+ if (existingFlow === null || existingFlow.val === null || existingFlow.val === undefined) {
76
+ await adapter.setStateAsync('pump.learning.learned_avg_flow_lh', { val: 0, ack: true });
77
+ }
72
78
 
73
79
  // ------------------------------------------------------
74
80
  // Normalbereich Leistung – Untere / Obere Grenze
@@ -87,7 +93,10 @@ async function createPumpStates3(adapter) {
87
93
  },
88
94
  native: {},
89
95
  });
90
- await adapter.setStateAsync('pump.learning.normal_range_power_low', { val: 0, ack: true });
96
+ const existingPowerLow = await adapter.getStateAsync('pump.learning.normal_range_power_low');
97
+ if (existingPowerLow === null || existingPowerLow.val === null || existingPowerLow.val === undefined) {
98
+ await adapter.setStateAsync('pump.learning.normal_range_power_low', { val: 0, ack: true });
99
+ }
91
100
 
92
101
  await adapter.setObjectNotExistsAsync('pump.learning.normal_range_power_high', {
93
102
  type: 'state',
@@ -103,7 +112,10 @@ async function createPumpStates3(adapter) {
103
112
  },
104
113
  native: {},
105
114
  });
106
- await adapter.setStateAsync('pump.learning.normal_range_power_high', { val: 0, ack: true });
115
+ const existingPowerHigh = await adapter.getStateAsync('pump.learning.normal_range_power_high');
116
+ if (existingPowerHigh === null || existingPowerHigh.val === null || existingPowerHigh.val === undefined) {
117
+ await adapter.setStateAsync('pump.learning.normal_range_power_high', { val: 0, ack: true });
118
+ }
107
119
 
108
120
  // ------------------------------------------------------
109
121
  // Normalbereich Durchfluss – Untere / Obere Grenze
@@ -122,7 +134,10 @@ async function createPumpStates3(adapter) {
122
134
  },
123
135
  native: {},
124
136
  });
125
- await adapter.setStateAsync('pump.learning.normal_range_flow_low', { val: 0, ack: true });
137
+ const existingFlowLow = await adapter.getStateAsync('pump.learning.normal_range_flow_low');
138
+ if (existingFlowLow === null || existingFlowLow.val === null || existingFlowLow.val === undefined) {
139
+ await adapter.setStateAsync('pump.learning.normal_range_flow_low', { val: 0, ack: true });
140
+ }
126
141
 
127
142
  await adapter.setObjectNotExistsAsync('pump.learning.normal_range_flow_high', {
128
143
  type: 'state',
@@ -138,7 +153,10 @@ async function createPumpStates3(adapter) {
138
153
  },
139
154
  native: {},
140
155
  });
141
- await adapter.setStateAsync('pump.learning.normal_range_flow_high', { val: 0, ack: true });
156
+ const existingFlowHigh = await adapter.getStateAsync('pump.learning.normal_range_flow_high');
157
+ if (existingFlowHigh === null || existingFlowHigh.val === null || existingFlowHigh.val === undefined) {
158
+ await adapter.setStateAsync('pump.learning.normal_range_flow_high', { val: 0, ack: true });
159
+ }
142
160
 
143
161
  // ------------------------------------------------------
144
162
  // Abweichungen (Prozent)
@@ -157,7 +175,10 @@ async function createPumpStates3(adapter) {
157
175
  },
158
176
  native: {},
159
177
  });
160
- await adapter.setStateAsync('pump.learning.deviation_power_percent', { val: 0, ack: true });
178
+ const existingDevPower = await adapter.getStateAsync('pump.learning.deviation_power_percent');
179
+ if (existingDevPower === null || existingDevPower.val === null || existingDevPower.val === undefined) {
180
+ await adapter.setStateAsync('pump.learning.deviation_power_percent', { val: 0, ack: true });
181
+ }
161
182
 
162
183
  await adapter.setObjectNotExistsAsync('pump.learning.deviation_flow_percent', {
163
184
  type: 'state',
@@ -173,7 +194,10 @@ async function createPumpStates3(adapter) {
173
194
  },
174
195
  native: {},
175
196
  });
176
- await adapter.setStateAsync('pump.learning.deviation_flow_percent', { val: 0, ack: true });
197
+ const existingDevFlow = await adapter.getStateAsync('pump.learning.deviation_flow_percent');
198
+ if (existingDevFlow === null || existingDevFlow.val === null || existingDevFlow.val === undefined) {
199
+ await adapter.setStateAsync('pump.learning.deviation_flow_percent', { val: 0, ack: true });
200
+ }
177
201
 
178
202
  // ------------------------------------------------------
179
203
  // Textbewertung (Status)
@@ -191,7 +215,10 @@ async function createPumpStates3(adapter) {
191
215
  },
192
216
  native: {},
193
217
  });
194
- await adapter.setStateAsync('pump.learning.status_text', { val: '', ack: true });
218
+ const existingStatus = await adapter.getStateAsync('pump.learning.status_text');
219
+ if (existingStatus === null || existingStatus.val === null || existingStatus.val === undefined) {
220
+ await adapter.setStateAsync('pump.learning.status_text', { val: '', ack: true });
221
+ }
195
222
 
196
223
  // ------------------------------------------------------
197
224
  // Anzahl Lernzyklen
@@ -209,7 +236,10 @@ async function createPumpStates3(adapter) {
209
236
  },
210
237
  native: {},
211
238
  });
212
- await adapter.setStateAsync('pump.learning.learning_cycles_total', { val: 0, ack: true });
239
+ const existingCycles = await adapter.getStateAsync('pump.learning.learning_cycles_total');
240
+ if (existingCycles === null || existingCycles.val === null || existingCycles.val === undefined) {
241
+ await adapter.setStateAsync('pump.learning.learning_cycles_total', { val: 0, ack: true });
242
+ }
213
243
 
214
244
  // ------------------------------------------------------
215
245
  // Log-Eintrag
package/main.js CHANGED
@@ -34,6 +34,7 @@ const { createConsumptionStates } = require('./lib/stateDefinitions/consumptionS
34
34
  const { createStatusStates } = require('./lib/stateDefinitions/statusStates');
35
35
  const { createControlStates } = require('./lib/stateDefinitions/controlStates');
36
36
  const { createDebugLogStates } = require('./lib/stateDefinitions/debugLogStates');
37
+ const { createHardwareStates } = require('./lib/stateDefinitions/hardwareStates');
37
38
 
38
39
  class Poolcontrol extends utils.Adapter {
39
40
  constructor(options) {
@@ -87,9 +88,12 @@ class Poolcontrol extends utils.Adapter {
87
88
  // --- Control States ---
88
89
  await createControlStates(this);
89
90
 
90
- // --- DebugLog Staets ---
91
+ // --- DebugLog States ---
91
92
  await createDebugLogStates(this);
92
93
 
94
+ // --- Hardware States ---
95
+ await createHardwareStates(this);
96
+
93
97
  // --- Migration Helper zuletzt starten ---
94
98
  await migrationHelper.init(this);
95
99
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "iobroker.poolcontrol",
3
- "version": "0.3.0",
3
+ "version": "0.3.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",