iobroker.poolcontrol 0.2.2 → 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 +26 -0
- package/admin/jsonConfig.json +4 -0
- package/io-package.json +27 -28
- package/lib/helpers/frostHelper.js +8 -3
- package/lib/helpers/migrationHelper.js +117 -0
- package/lib/helpers/pumpHelper2.js +162 -0
- package/lib/helpers/pumpHelper3.js +259 -0
- package/lib/helpers/runtimeHelper.js +75 -8
- package/lib/helpers/statusHelper.js +35 -10
- package/lib/stateDefinitions/controlStates.js +82 -18
- package/lib/stateDefinitions/hardwareStates.js +184 -0
- package/lib/stateDefinitions/pumpStates.js +33 -7
- package/lib/stateDefinitions/pumpStates2.js +125 -0
- package/lib/stateDefinitions/pumpStates3.js +252 -0
- package/lib/stateDefinitions/solarStates.js +57 -26
- package/lib/stateDefinitions/speechStates.js +34 -8
- package/lib/stateDefinitions/statusStates.js +8 -2
- package/main.js +34 -1
- package/package.json +1 -1
|
@@ -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,18 +156,45 @@ 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
|
|
|
137
|
-
//
|
|
138
|
-
const pumpLph = (await this.adapter.getStateAsync('pump.pump_power_lph'))?.val || 0;
|
|
159
|
+
// Poolparameter laden (vor Durchflussprüfung!)
|
|
139
160
|
const poolSize = (await this.adapter.getStateAsync('general.pool_size'))?.val || 0;
|
|
140
161
|
const minCirc = (await this.adapter.getStateAsync('general.min_circulation_per_day'))?.val || 1;
|
|
141
162
|
|
|
142
|
-
|
|
163
|
+
// daily_required immer direkt setzen – auch ohne Durchfluss
|
|
143
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
|
+
|
|
169
|
+
// Umwälzmenge berechnen
|
|
170
|
+
// Reeller Durchflusswert aus pump.live.flow_current_lh
|
|
171
|
+
const liveFlowLh = (await this.adapter.getStateAsync('pump.live.flow_current_lh'))?.val || 0;
|
|
172
|
+
|
|
173
|
+
if (liveFlowLh <= 0) {
|
|
174
|
+
this.adapter.log.debug('[runtimeHelper] Kein Live-Durchflusswert vorhanden, Berechnung übersprungen');
|
|
175
|
+
return;
|
|
176
|
+
}
|
|
177
|
+
|
|
178
|
+
// Berechnung der realen Tagesumwälzung (Liter)
|
|
179
|
+
const dailyTotal = Math.round((effectiveToday / 3600) * liveFlowLh);
|
|
144
180
|
const dailyRemaining = Math.max(dailyRequired - dailyTotal, 0);
|
|
145
181
|
|
|
146
|
-
|
|
147
|
-
await this.adapter.
|
|
148
|
-
await this.adapter.
|
|
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
|
+
}
|
|
149
198
|
} catch (err) {
|
|
150
199
|
this.adapter.log.warn(`[runtimeHelper] Fehler beim Update der States: ${err.message}`);
|
|
151
200
|
}
|
|
@@ -194,12 +243,30 @@ const runtimeHelper = {
|
|
|
194
243
|
nextMidnight.setHours(24, 0, 0, 0);
|
|
195
244
|
const msUntilMidnight = nextMidnight - now;
|
|
196
245
|
|
|
197
|
-
this.resetTimer = setTimeout(() => {
|
|
246
|
+
this.resetTimer = setTimeout(async () => {
|
|
198
247
|
this.runtimeToday = 0;
|
|
199
248
|
this.startCountToday = 0;
|
|
200
249
|
this.lastOn = this.isRunning ? Date.now() : null;
|
|
250
|
+
|
|
251
|
+
// Laufzeiten zurücksetzen
|
|
201
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
|
|
202
267
|
this._scheduleDailyReset();
|
|
268
|
+
|
|
269
|
+
this.adapter.log.debug('[runtimeHelper] Tagesreset (Runtime + Circulation) ausgeführt.');
|
|
203
270
|
}, msUntilMidnight);
|
|
204
271
|
},
|
|
205
272
|
|
|
@@ -12,6 +12,7 @@ const statusHelper = {
|
|
|
12
12
|
adapter: null,
|
|
13
13
|
midnightTimer: null,
|
|
14
14
|
pumpOn: null, // interner Merker für Pumpenstatus
|
|
15
|
+
_lastSummaryUpdate: 0, // FIX: Zeitstempel für Throttle-Schutz
|
|
15
16
|
|
|
16
17
|
init(adapter) {
|
|
17
18
|
this.adapter = adapter;
|
|
@@ -95,8 +96,23 @@ const statusHelper = {
|
|
|
95
96
|
await this.updateSummary();
|
|
96
97
|
},
|
|
97
98
|
|
|
99
|
+
// FIX: Hilfsfunktion zur sicheren Formatierung
|
|
100
|
+
safeValue(v, digits = 1) {
|
|
101
|
+
if (v == null || isNaN(v)) {
|
|
102
|
+
return '–';
|
|
103
|
+
}
|
|
104
|
+
return Number(v).toFixed(digits);
|
|
105
|
+
},
|
|
106
|
+
|
|
98
107
|
async updateSummary() {
|
|
99
108
|
try {
|
|
109
|
+
// FIX: Throttle - Mehrfachupdates innerhalb 1 Sekunde vermeiden
|
|
110
|
+
if (Date.now() - this._lastSummaryUpdate < 1000) {
|
|
111
|
+
this.adapter.log.debug('[statusHelper] UpdateSummary übersprungen (Throttle)');
|
|
112
|
+
return;
|
|
113
|
+
}
|
|
114
|
+
this._lastSummaryUpdate = Date.now();
|
|
115
|
+
|
|
100
116
|
// Werte laden
|
|
101
117
|
const pumpStatus = (await this.adapter.getStateAsync('pump.status'))?.val || 'unbekannt';
|
|
102
118
|
const pumpMode = (await this.adapter.getStateAsync('pump.mode'))?.val || 'unknown';
|
|
@@ -112,32 +128,41 @@ const statusHelper = {
|
|
|
112
128
|
// Laufzeit formatieren
|
|
113
129
|
const h = Math.floor(runtimeToday / 3600);
|
|
114
130
|
const m = Math.floor((runtimeToday % 3600) / 60);
|
|
115
|
-
const runtimeFormatted = `${h}h ${m}m`;
|
|
131
|
+
const runtimeFormatted = isNaN(h) || isNaN(m) ? '0h 00m' : `${h}h ${m}m`; // FIX: Schutz gegen NaN
|
|
116
132
|
|
|
117
133
|
// Umwälzungsquote
|
|
118
134
|
let circulationPct = 0;
|
|
119
135
|
if (dailyRequired > 0) {
|
|
120
136
|
circulationPct = Math.round((dailyTotal / dailyRequired) * 100);
|
|
121
137
|
}
|
|
138
|
+
if (isNaN(circulationPct)) {
|
|
139
|
+
circulationPct = 0;
|
|
140
|
+
} // FIX: NaN-Absicherung
|
|
122
141
|
|
|
123
142
|
// Text bauen
|
|
124
143
|
let text = `Pumpe: ${pumpStatus}`;
|
|
125
144
|
if (pumpMode && pumpMode !== 'unknown') {
|
|
126
145
|
text += ` (Modus: ${pumpMode})`;
|
|
127
146
|
}
|
|
147
|
+
|
|
148
|
+
const safe = this.safeValue.bind(this); // FIX: Kurzreferenz
|
|
128
149
|
if (poolTemp != null) {
|
|
129
|
-
text += `. Pool: ${poolTemp
|
|
150
|
+
text += `. Pool: ${safe(poolTemp)} °C`;
|
|
130
151
|
}
|
|
131
152
|
if (collectorTemp != null) {
|
|
132
|
-
text += `, Kollektor: ${collectorTemp
|
|
153
|
+
text += `, Kollektor: ${safe(collectorTemp)} °C`;
|
|
133
154
|
}
|
|
134
155
|
if (outsideTemp != null) {
|
|
135
|
-
text += `, Außentemperatur: ${outsideTemp
|
|
156
|
+
text += `, Außentemperatur: ${safe(outsideTemp)} °C`;
|
|
136
157
|
}
|
|
137
158
|
text += `. Tageslaufzeit: ${runtimeFormatted} (${circulationPct}% der Soll-Umwälzung).`;
|
|
138
159
|
|
|
139
|
-
// In States schreiben
|
|
140
|
-
await this.adapter.
|
|
160
|
+
// In States schreiben (nur bei Änderung)
|
|
161
|
+
const current = (await this.adapter.getStateAsync('status.summary'))?.val;
|
|
162
|
+
if (current !== text) {
|
|
163
|
+
await this.adapter.setStateAsync('status.summary', { val: text, ack: true });
|
|
164
|
+
}
|
|
165
|
+
|
|
141
166
|
await this.adapter.setStateAsync('status.last_summary_update', {
|
|
142
167
|
val: new Date().toISOString(),
|
|
143
168
|
ack: true,
|
|
@@ -147,10 +172,10 @@ const statusHelper = {
|
|
|
147
172
|
const json = {
|
|
148
173
|
pump: pumpStatus,
|
|
149
174
|
mode: pumpMode,
|
|
150
|
-
pool: poolTemp,
|
|
151
|
-
collector: collectorTemp,
|
|
152
|
-
outside: outsideTemp,
|
|
153
|
-
runtime_today: runtimeToday,
|
|
175
|
+
pool: poolTemp ?? null,
|
|
176
|
+
collector: collectorTemp ?? null,
|
|
177
|
+
outside: outsideTemp ?? null,
|
|
178
|
+
runtime_today: runtimeToday ?? 0,
|
|
154
179
|
runtime_formatted: runtimeFormatted,
|
|
155
180
|
circulation_pct: circulationPct,
|
|
156
181
|
};
|
|
@@ -36,7 +36,7 @@ async function createControlStates(adapter) {
|
|
|
36
36
|
native: {},
|
|
37
37
|
});
|
|
38
38
|
|
|
39
|
-
// State: control.season.active
|
|
39
|
+
// State: control.season.active (mit Persist-Schutz)
|
|
40
40
|
await adapter.setObjectNotExistsAsync('control.season.active', {
|
|
41
41
|
type: 'state',
|
|
42
42
|
common: {
|
|
@@ -47,15 +47,21 @@ async function createControlStates(adapter) {
|
|
|
47
47
|
read: true,
|
|
48
48
|
write: true,
|
|
49
49
|
def: false,
|
|
50
|
+
persist: true, // dauerhaft speichern
|
|
50
51
|
},
|
|
51
52
|
native: {},
|
|
52
53
|
});
|
|
53
54
|
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
55
|
+
const existingSeasonActive = await adapter.getStateAsync('control.season.active');
|
|
56
|
+
if (
|
|
57
|
+
existingSeasonActive === null ||
|
|
58
|
+
existingSeasonActive.val === null ||
|
|
59
|
+
existingSeasonActive.val === undefined
|
|
60
|
+
) {
|
|
61
|
+
const cfgValue = !!adapter.config.season_active;
|
|
62
|
+
await adapter.setStateAsync('control.season.active', { val: cfgValue, ack: true });
|
|
63
|
+
adapter.log.debug(`[controlStates] State control.season.active initialisiert mit Wert: ${cfgValue}`);
|
|
64
|
+
}
|
|
59
65
|
|
|
60
66
|
// ---------------------------------------------------------------------
|
|
61
67
|
// Channel: control.pump
|
|
@@ -100,7 +106,7 @@ async function createControlStates(adapter) {
|
|
|
100
106
|
});
|
|
101
107
|
await adapter.setStateAsync('control.pump.backwash_active', { val: false, ack: true });
|
|
102
108
|
|
|
103
|
-
// Rückspülungsdauer
|
|
109
|
+
// Rückspülungsdauer (mit Persist-Schutz)
|
|
104
110
|
await adapter.setObjectNotExistsAsync('control.pump.backwash_duration', {
|
|
105
111
|
type: 'state',
|
|
106
112
|
common: {
|
|
@@ -113,12 +119,20 @@ async function createControlStates(adapter) {
|
|
|
113
119
|
def: 1,
|
|
114
120
|
min: 1,
|
|
115
121
|
max: 60,
|
|
122
|
+
persist: true, // dauerhaft speichern
|
|
116
123
|
},
|
|
117
124
|
native: {},
|
|
118
125
|
});
|
|
119
|
-
await adapter.
|
|
126
|
+
const existingBackwashDuration = await adapter.getStateAsync('control.pump.backwash_duration');
|
|
127
|
+
if (
|
|
128
|
+
existingBackwashDuration === null ||
|
|
129
|
+
existingBackwashDuration.val === null ||
|
|
130
|
+
existingBackwashDuration.val === undefined
|
|
131
|
+
) {
|
|
132
|
+
await adapter.setStateAsync('control.pump.backwash_duration', { val: 1, ack: true });
|
|
133
|
+
}
|
|
120
134
|
|
|
121
|
-
//
|
|
135
|
+
// Rückspülerinnerung aktiv (mit Persist-Schutz)
|
|
122
136
|
await adapter.setObjectNotExistsAsync('control.pump.backwash_reminder_active', {
|
|
123
137
|
type: 'state',
|
|
124
138
|
common: {
|
|
@@ -129,11 +143,16 @@ async function createControlStates(adapter) {
|
|
|
129
143
|
read: true,
|
|
130
144
|
write: true,
|
|
131
145
|
def: false,
|
|
146
|
+
persist: true, // dauerhaft speichern
|
|
132
147
|
},
|
|
133
148
|
native: {},
|
|
134
149
|
});
|
|
135
|
-
await adapter.
|
|
150
|
+
const existingBackwashRem = await adapter.getStateAsync('control.pump.backwash_reminder_active');
|
|
151
|
+
if (existingBackwashRem === null || existingBackwashRem.val === null || existingBackwashRem.val === undefined) {
|
|
152
|
+
await adapter.setStateAsync('control.pump.backwash_reminder_active', { val: false, ack: true });
|
|
153
|
+
}
|
|
136
154
|
|
|
155
|
+
// Rückspülintervall (mit Persist-Schutz)
|
|
137
156
|
await adapter.setObjectNotExistsAsync('control.pump.backwash_interval_days', {
|
|
138
157
|
type: 'state',
|
|
139
158
|
common: {
|
|
@@ -147,10 +166,18 @@ async function createControlStates(adapter) {
|
|
|
147
166
|
def: 7,
|
|
148
167
|
min: 1,
|
|
149
168
|
max: 60,
|
|
169
|
+
persist: true, // dauerhaft speichern
|
|
150
170
|
},
|
|
151
171
|
native: {},
|
|
152
172
|
});
|
|
153
|
-
await adapter.
|
|
173
|
+
const existingBackwashInterval = await adapter.getStateAsync('control.pump.backwash_interval_days');
|
|
174
|
+
if (
|
|
175
|
+
existingBackwashInterval === null ||
|
|
176
|
+
existingBackwashInterval.val === null ||
|
|
177
|
+
existingBackwashInterval.val === undefined
|
|
178
|
+
) {
|
|
179
|
+
await adapter.setStateAsync('control.pump.backwash_interval_days', { val: 7, ack: true });
|
|
180
|
+
}
|
|
154
181
|
|
|
155
182
|
await adapter.setObjectNotExistsAsync('control.pump.backwash_last_date', {
|
|
156
183
|
type: 'state',
|
|
@@ -181,7 +208,6 @@ async function createControlStates(adapter) {
|
|
|
181
208
|
});
|
|
182
209
|
await adapter.setStateAsync('control.pump.backwash_required', { val: false, ack: true });
|
|
183
210
|
|
|
184
|
-
// ---------------------------------------------------------------------
|
|
185
211
|
// Wartungsmodus aktiv
|
|
186
212
|
await adapter.setObjectNotExistsAsync('control.pump.maintenance_active', {
|
|
187
213
|
type: 'state',
|
|
@@ -198,7 +224,7 @@ async function createControlStates(adapter) {
|
|
|
198
224
|
});
|
|
199
225
|
await adapter.setStateAsync('control.pump.maintenance_active', { val: false, ack: true });
|
|
200
226
|
|
|
201
|
-
// Benachrichtigungen aktivieren
|
|
227
|
+
// Benachrichtigungen aktivieren (mit Persist-Schutz)
|
|
202
228
|
await adapter.setObjectNotExistsAsync('control.pump.notifications_enabled', {
|
|
203
229
|
type: 'state',
|
|
204
230
|
common: {
|
|
@@ -209,10 +235,14 @@ async function createControlStates(adapter) {
|
|
|
209
235
|
read: true,
|
|
210
236
|
write: true,
|
|
211
237
|
def: true,
|
|
238
|
+
persist: true, // dauerhaft speichern
|
|
212
239
|
},
|
|
213
240
|
native: {},
|
|
214
241
|
});
|
|
215
|
-
await adapter.
|
|
242
|
+
const existingNoti = await adapter.getStateAsync('control.pump.notifications_enabled');
|
|
243
|
+
if (existingNoti === null || existingNoti.val === null || existingNoti.val === undefined) {
|
|
244
|
+
await adapter.setStateAsync('control.pump.notifications_enabled', { val: true, ack: true });
|
|
245
|
+
}
|
|
216
246
|
|
|
217
247
|
// ---------------------------------------------------------------------
|
|
218
248
|
// Channel: control.energy
|
|
@@ -252,7 +282,7 @@ async function createControlStates(adapter) {
|
|
|
252
282
|
native: {},
|
|
253
283
|
});
|
|
254
284
|
|
|
255
|
-
//
|
|
285
|
+
// Modus der Umwälzungsprüfung
|
|
256
286
|
await adapter.setObjectNotExistsAsync('control.circulation.mode', {
|
|
257
287
|
type: 'state',
|
|
258
288
|
common: {
|
|
@@ -263,6 +293,7 @@ async function createControlStates(adapter) {
|
|
|
263
293
|
read: true,
|
|
264
294
|
write: true,
|
|
265
295
|
def: 'notify',
|
|
296
|
+
persist: true,
|
|
266
297
|
states: {
|
|
267
298
|
auto: 'Automatik',
|
|
268
299
|
manual: 'Manuell',
|
|
@@ -272,9 +303,14 @@ async function createControlStates(adapter) {
|
|
|
272
303
|
},
|
|
273
304
|
native: {},
|
|
274
305
|
});
|
|
306
|
+
try {
|
|
307
|
+
await adapter.extendObjectAsync('control.circulation.mode', { common: { persist: true } });
|
|
308
|
+
} catch (err) {
|
|
309
|
+
adapter.log.warn(`[controlStates] persist-Flag für control.circulation.mode nicht gesetzt: ${err.message}`);
|
|
310
|
+
}
|
|
275
311
|
await adapter.setStateAsync('control.circulation.mode', { val: 'notify', ack: true });
|
|
276
312
|
|
|
277
|
-
//
|
|
313
|
+
// Prüfzeitpunkt (mit Persist-Schutz)
|
|
278
314
|
await adapter.setObjectNotExistsAsync('control.circulation.check_time', {
|
|
279
315
|
type: 'state',
|
|
280
316
|
common: {
|
|
@@ -285,12 +321,16 @@ async function createControlStates(adapter) {
|
|
|
285
321
|
read: true,
|
|
286
322
|
write: true,
|
|
287
323
|
def: '18:00',
|
|
324
|
+
persist: true, // dauerhaft speichern
|
|
288
325
|
},
|
|
289
326
|
native: {},
|
|
290
327
|
});
|
|
291
|
-
await adapter.
|
|
328
|
+
const existingCheckTime = await adapter.getStateAsync('control.circulation.check_time');
|
|
329
|
+
if (existingCheckTime === null || existingCheckTime.val === null || existingCheckTime.val === undefined) {
|
|
330
|
+
await adapter.setStateAsync('control.circulation.check_time', { val: '18:00', ack: true });
|
|
331
|
+
}
|
|
292
332
|
|
|
293
|
-
//
|
|
333
|
+
// letzter Bericht
|
|
294
334
|
await adapter.setObjectNotExistsAsync('control.circulation.last_report', {
|
|
295
335
|
type: 'state',
|
|
296
336
|
common: {
|
|
@@ -307,6 +347,30 @@ async function createControlStates(adapter) {
|
|
|
307
347
|
} catch (err) {
|
|
308
348
|
adapter.log.error(`[controlStates] Fehler beim Erstellen der Control-States: ${err.message}`);
|
|
309
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
|
+
});
|
|
310
374
|
}
|
|
311
375
|
|
|
312
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 };
|