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 +8 -0
- package/admin/jsonConfig.json +4 -0
- package/io-package.json +15 -2
- package/lib/helpers/frostHelper.js +8 -3
- package/lib/helpers/pumpHelper2.js +18 -16
- package/lib/helpers/pumpHelper3.js +1 -1
- package/lib/helpers/runtimeHelper.js +68 -11
- package/lib/stateDefinitions/controlStates.js +24 -0
- package/lib/stateDefinitions/hardwareStates.js +184 -0
- package/lib/stateDefinitions/pumpStates3.js +41 -11
- package/main.js +5 -1
- package/package.json +1 -1
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
|
|
package/admin/jsonConfig.json
CHANGED
package/io-package.json
CHANGED
|
@@ -1,8 +1,21 @@
|
|
|
1
1
|
{
|
|
2
2
|
"common": {
|
|
3
3
|
"name": "poolcontrol",
|
|
4
|
-
"version": "0.3.
|
|
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
|
-
//
|
|
72
|
-
|
|
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 (
|
|
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.
|
|
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 →
|
|
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
|
-
|
|
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
|
*
|
|
@@ -21,9 +21,24 @@ const runtimeHelper = {
|
|
|
21
21
|
resetTimer: null,
|
|
22
22
|
liveTimer: null, // Timer für Live-Updates
|
|
23
23
|
|
|
24
|
-
|
|
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
|
|
156
|
-
await this.adapter.
|
|
157
|
-
await this.adapter.
|
|
158
|
-
|
|
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.
|
|
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.
|
|
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.
|
|
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.
|
|
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.
|
|
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.
|
|
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.
|
|
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.
|
|
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.
|
|
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.
|
|
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.
|
|
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
|
|
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.
|
|
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",
|