iobroker.poolcontrol 0.6.4 → 0.7.0

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
@@ -135,6 +135,16 @@ Neue Funktionen werden regelmäßig ergänzt – bitte den Changelog beachten.
135
135
  ## Changelog
136
136
  ### **WORK IN PROGRESS**
137
137
 
138
+
139
+ ## v0.7.0 (2025-11-29)
140
+ - Einführung eines neuen Drucksensor-Systems unter `pump.pressure.*`
141
+ - Unterstützung externer Drucksensor-ObjektID (bar-Wert aus ioBroker)
142
+ - Trenderkennung (steigend/fallend/stabil) und gleitender Druckdurchschnitt
143
+ - Selbstlernende Min-/Max-Druckwerte mit manuellem Reset-State
144
+ - Neuer Diagnose-Text (`status_text_diagnostic`) mit erweiterten Analyseinformationen
145
+ - Erweiterte Pumpenüberwachung ohne automatische Steuerlogik (rein informativ)
146
+
147
+
138
148
  ## v0.6.2 (2025-11-07)
139
149
  - Überarbeitung der Instanzübersicht mit neuen Header-Strukturen für klarere Bedienung
140
150
  - Neues Startseitenbild „Egon im Blaumann“ in der Admin-Oberfläche integriert
@@ -199,6 +199,48 @@
199
199
  "md": 12,
200
200
  "lg": 12,
201
201
  "xl": 12
202
+ },
203
+ "pressure_header": {
204
+ "type": "header",
205
+ "text": "Drucksensor (Filterdruck)",
206
+ "size": 4,
207
+ "newLine": true,
208
+ "xs": 12,
209
+ "sm": 12,
210
+ "md": 12,
211
+ "lg": 12,
212
+ "xl": 12
213
+ },
214
+ "use_pressure_sensor": {
215
+ "type": "checkbox",
216
+ "label": "Drucksensor verwenden",
217
+ "default": false,
218
+ "xs": 12,
219
+ "sm": 3,
220
+ "md": 3,
221
+ "lg": 3,
222
+ "xl": 3,
223
+ "newLine": true
224
+ },
225
+ "pressure_sensor_id": {
226
+ "type": "objectId",
227
+ "label": "Objekt-ID Drucksensor (bar)",
228
+ "default": "",
229
+ "xs": 12,
230
+ "sm": 3,
231
+ "md": 3,
232
+ "lg": 3,
233
+ "xl": 3
234
+ },
235
+ "pressure_hint": {
236
+ "type": "staticText",
237
+ "text": "Hinweis: Unterstützt sowohl externe Sensoren als auch die PoolControl-Druckbox.",
238
+ "newLine": true,
239
+ "xs": 12,
240
+ "sm": 12,
241
+ "md": 12,
242
+ "lg": 12,
243
+ "xl": 12
202
244
  },
203
245
  "pump_safety_header": {
204
246
  "type": "header",
package/io-package.json CHANGED
@@ -1,8 +1,34 @@
1
1
  {
2
2
  "common": {
3
3
  "name": "poolcontrol",
4
- "version": "0.6.4",
4
+ "version": "0.7.0",
5
5
  "news": {
6
+ "0.7.0": {
7
+ "en": "Added full pressure sensor integration (pump.pressure.*), including trend detection, self-learning min/max values, diagnostic output and configurable normal pressure range. No automatic control logic – purely informative.",
8
+ "de": "Vollständige Drucksensor-Integration (pump.pressure.*) hinzugefügt, inklusive Trenderkennung, selbstlernenden Min-/Max-Werten, Diagnoseausgabe und einstellbarem Normaldruckbereich. Keine automatische Logik – rein informativ.",
9
+ "ru": "Добавлена полная интеграция датчика давления (pump.pressure.*), включая определение тренда, самообучающиеся минимальные/максимальные значения, диагностический вывод и настраиваемый диапазон нормального давления. Без автоматической логики – только информация.",
10
+ "pt": "Adicionada integração completa do sensor de pressão (pump.pressure.*), incluindo detecção de tendência, valores mínimos/máximos autoaprendidos, saída de diagnóstico e faixa de pressão normal configurável. Sem lógica automática – apenas informativo.",
11
+ "nl": "Volledige druksensorintegratie (pump.pressure.*) toegevoegd, inclusief trenddetectie, zelflerende min/max-waarden, diagnostische uitvoer en instelbaar normaaldrukbereik. Geen automatische logica – puur informatief.",
12
+ "fr": "Intégration complète du capteur de pression (pump.pressure.*) ajoutée, comprenant la détection de tendance, des valeurs min/max auto-apprises, une sortie de diagnostic et une plage de pression normale configurable. Aucune logique automatique – uniquement informatif.",
13
+ "it": "Aggiunta integrazione completa del sensore di pressione (pump.pressure.*), con rilevamento delle tendenze, valori min/max autoapprendenti, output diagnostico e intervallo di pressione normale configurabile. Nessuna logica automatica – solo informativo.",
14
+ "es": "Se añadió la integración completa del sensor de presión (pump.pressure.*), incluyendo detección de tendencia, valores mínimos/máximos autoaprendidos, salida de diagnóstico y rango de presión normal configurable. Sin lógica automática – solo informativo.",
15
+ "pl": "Dodano pełną integrację czujnika ciśnienia (pump.pressure.*), w tym wykrywanie trendów, samo uczące się wartości min/max, dane diagnostyczne oraz konfigurowalny zakres ciśnienia normalnego. Bez automatycznej logiki – wyłącznie informacyjne.",
16
+ "uk": "Додано повну інтеграцію датчика тиску (pump.pressure.*), включаючи визначення трендів, самонавчальні мін/макс значення, діагностичний вихід та налаштовуваний діапазон нормального тиску. Без автоматичної логіки – лише інформаційно.",
17
+ "zh-cn": "新增完整压力传感器集成 (pump.pressure.*),包括趋势检测、自学习最小/最大值、诊断输出以及可配置的正常压力范围。无自动控制逻辑,仅提供信息。"
18
+ },
19
+ "0.6.5": {
20
+ "en": "Fix for weekly, monthly and yearly reset in consumptionHelper",
21
+ "de": "Fix für Wochen-, Monats- und Jahres-Reset im ConsumptionHelper",
22
+ "ru": "Исправление еженедельного, ежемесячного и ежегодного сброса в consumptionHelper",
23
+ "pt": "Correção para redefinição semanal, mensal e anual no consumptionHelper",
24
+ "nl": "Fix voor wekelijkse, maandelijkse en jaarlijkse reset in consumptionHelper",
25
+ "fr": "Correctif pour la réinitialisation hebdomadaire, mensuelle et annuelle dans consumptionHelper",
26
+ "it": "Correzione per il reset settimanale, mensile e annuale in consumptionHelper",
27
+ "es": "Corrección para el reinicio semanal, mensual y anual en consumptionHelper",
28
+ "pl": "Poprawka cotygodniowego, comiesięcznego i corocznego resetu w consumptionHelper",
29
+ "uk": "Виправлення щотижневого, щомісячного та щорічного скидання в consumptionHelper",
30
+ "zh-cn": "修复 consumptionHelper 中每周、每月和每年重置的问题"
31
+ },
6
32
  "0.6.4": {
7
33
  "en": "Fix for weekly, monthly and yearly reset in consumptionHelper",
8
34
  "de": "Fix für Wochen-, Monats- und Jahres-Reset im ConsumptionHelper",
@@ -65,32 +91,6 @@
65
91
  "pl": "Dodano pełną kontrolę fotowoltaiczną z automatycznym zarządzaniem pompą (tryb 'Automatik (PV)'). Adapter reaguje teraz na nadwyżkę PV w oparciu o konfigurowalne wartości zużycia i produkcji energii. Zawiera migrację dla istniejących instalacji. Poprawiono spójność systemu i wewnętrzne logowanie.",
66
92
  "uk": "Додано повний фотогальванічний контроль з автоматичним керуванням насосом (режим 'Automatik (PV)'). Адаптер тепер реагує на надлишок PV на основі налаштовуваних значень споживання та генерації. Включено міграцію для існуючих установок. Покращено системну узгодженість і внутрішнє журналювання.",
67
93
  "zh-cn": "新增完整的光伏控制与自动泵管理(模式“Automatik (PV)”)。适配器现在可根据可配置的家庭耗电与发电数值响应光伏盈余。包含对现有安装的迁移。改进了系统一致性和内部日志记录。"
68
- },
69
- "0.5.5": {
70
- "en": "Fixed remaining timer recursion issue in weekly and monthly statistics to fully prevent infinite reset loops. Cleaned up jsonConfig structure and improved timer handling.",
71
- "de": "Verbleibende Timer-Rekursion in Wochen- und Monatsstatistik behoben, um Endlosschleifen beim Reset endgültig zu verhindern. jsonConfig-Struktur bereinigt und Timer-Handling verbessert.",
72
- "ru": "Исправлена оставшаяся рекурсия таймера в еженедельной и ежемесячной статистике для полного предотвращения бесконечных циклов сброса. Упрощена структура jsonConfig и улучшено управление таймерами.",
73
- "pt": "Corrigida a recursão restante do temporizador nas estatísticas semanais e mensais para evitar completamente loops de redefinição infinitos. Estrutura jsonConfig limpa e manuseio de temporizador aprimorado.",
74
- "nl": "Overgebleven timerrecursie in week- en maandstatistieken opgelost om oneindige resetlussen volledig te voorkomen. jsonConfig-structuur opgeschoond en timerafhandeling verbeterd.",
75
- "fr": "Correction de la récursion restante du minuteur dans les statistiques hebdomadaires et mensuelles afin d'éviter définitivement les boucles de réinitialisation infinies. Structure jsonConfig nettoyée et gestion du minuteur améliorée.",
76
- "it": "Corretta la ricorsione residua del timer nelle statistiche settimanali e mensili per prevenire completamente i loop di reset infiniti. Pulita la struttura jsonConfig e migliorata la gestione dei timer.",
77
- "es": "Corregida la recursión restante del temporizador en las estadísticas semanales y mensuales para evitar completamente los bucles de reinicio infinitos. Estructura jsonConfig limpiada y manejo del temporizador mejorado.",
78
- "pl": "Naprawiono pozostałą rekursję timera w statystykach tygodniowych i miesięcznych, aby całkowicie zapobiec nieskończonym pętlom resetowania. Uporządkowano strukturę jsonConfig i ulepszono obsługę timera.",
79
- "uk": "Виправлено залишкову рекурсію таймера у тижневій та місячній статистиці, щоб повністю запобігти нескінченним циклам скидання. Очищено структуру jsonConfig і покращено обробку таймерів.",
80
- "zh-cn": "修复了每周和每月统计中的剩余计时器递归问题,以完全防止无限重置循环。清理了 jsonConfig 结构并改进了计时器处理。"
81
- },
82
- "0.5.4": {
83
- "en": "Fixed a rare infinite loop during weekly and monthly statistics reset that could cause Redis overload. Added timer protection and improved stability.",
84
- "de": "Selten auftretende Endlosschleife beim Wochen- und Monatsreset der Statistik behoben, die zu Redis-Überlastung führen konnte. Timer-Schutz und Stabilität verbessert.",
85
- "ru": "Исправлена редкая бесконечная петля при сбросе еженедельной и ежемесячной статистики, вызывавшая перегрузку Redis. Добавлена защита таймера и улучшена стабильность.",
86
- "pt": "Corrigido um loop infinito raro durante a redefinição das estatísticas semanais e mensais que podia causar sobrecarga do Redis. Adicionada proteção de temporizador e melhorada a estabilidade.",
87
- "nl": "Zeldzame oneindige lus opgelost tijdens de reset van wekelijkse en maandelijkse statistieken die Redis kon overbelasten. Timerbescherming en stabiliteit verbeterd.",
88
- "fr": "Correction d'une boucle infinie rare lors de la réinitialisation des statistiques hebdomadaires et mensuelles pouvant provoquer une surcharge de Redis. Protection du minuteur et stabilité améliorées.",
89
- "it": "Corretto un raro ciclo infinito durante il reset delle statistiche settimanali e mensili che poteva causare un sovraccarico di Redis. Migliorata la protezione del timer e la stabilità.",
90
- "es": "Se corrigió un bucle infinito poco frecuente durante el reinicio de las estadísticas semanales y mensuales que podía causar sobrecarga de Redis. Protección de temporizador y estabilidad mejoradas.",
91
- "pl": "Naprawiono rzadką nieskończoną pętlę podczas resetowania statystyk tygodniowych i miesięcznych, która mogła powodować przeciążenie Redis. Dodano ochronę timera i poprawiono stabilność.",
92
- "uk": "Виправлено рідкісну нескінченну петлю під час скидання тижневої та місячної статистики, що могла спричинити перевантаження Redis. Додано захист таймера та покращено стабільність.",
93
- "zh-cn": "修复了每周和每月统计重置期间可能导致 Redis 过载的罕见无限循环。改进了计时器保护和系统稳定性。"
94
94
  }
95
95
  },
96
96
  "titleLang": {
@@ -268,51 +268,71 @@ const consumptionHelper = {
268
268
  },
269
269
 
270
270
  // ---------------------------------------------------------
271
- // 🔵 WÖCHENTLICHER RESET (Montag 00:05 Uhr)
271
+ // 🔵 WÖCHENTLICHER RESET (täglicher Check um 00:05 Uhr, nur Montag)
272
272
  // ---------------------------------------------------------
273
273
  _scheduleWeeklyReset() {
274
274
  const now = new Date();
275
275
  const next = new Date(now);
276
+ next.setHours(0, 5, 0, 0); // täglicher Check 00:05
276
277
 
277
- // Montag = 1 (Sonntag = 0)
278
- const day = now.getDay();
279
- const daysUntilMonday = (1 - day + 7) % 7;
280
-
281
- next.setDate(now.getDate() + daysUntilMonday);
282
- next.setHours(0, 5, 0, 0);
278
+ // Wenn die Zeit heute schon vorbei ist → morgen 00:05
279
+ if (next <= now) {
280
+ next.setDate(next.getDate() + 1);
281
+ }
283
282
 
284
283
  const delay = next - now;
285
284
 
286
285
  setTimeout(async () => {
287
286
  try {
288
- this.adapter.log.info('[consumptionHelper] Wochen-Reset (Montag 00:05)');
289
- await this.adapter.setStateAsync('consumption.week_kwh', { val: 0, ack: true });
290
- await this.adapter.setStateAsync('costs.week_eur', { val: 0, ack: true });
291
- this.baselines.week = (await this.adapter.getStateAsync('consumption.total_kwh'))?.val || 0;
287
+ const nowCheck = new Date();
288
+
289
+ // Nur wenn heute Montag ist (0 = Sonntag, 1 = Montag)
290
+ if (nowCheck.getDay() === 1) {
291
+ this.adapter.log.info('[consumptionHelper] Wochen-Reset (Montag 00:05)');
292
+ await this.adapter.setStateAsync('consumption.week_kwh', { val: 0, ack: true });
293
+ await this.adapter.setStateAsync('costs.week_eur', { val: 0, ack: true });
294
+ this.baselines.week = (await this.adapter.getStateAsync('consumption.total_kwh'))?.val || 0;
295
+ }
292
296
  } catch (err) {
293
297
  this.adapter.log.warn(`[consumptionHelper] Fehler beim Wochenreset: ${err.message}`);
294
298
  }
295
- this._scheduleWeeklyReset(); // erneut planen
299
+
300
+ // Morgen wieder planen
301
+ this._scheduleWeeklyReset();
296
302
  }, delay);
297
303
  },
298
304
 
299
305
  // ---------------------------------------------------------
300
- // 🔵 MONATLICHER RESET (1. des Monats 00:05 Uhr)
306
+ // 🔵 MONATLICHER RESET (täglicher Check um 00:05 Uhr, nur am 1.)
301
307
  // ---------------------------------------------------------
302
308
  _scheduleMonthlyReset() {
303
309
  const now = new Date();
304
- const next = new Date(now.getFullYear(), now.getMonth() + 1, 1, 0, 5, 0, 0);
310
+ const next = new Date(now);
311
+ next.setHours(0, 5, 0, 0); // täglicher Check 00:05
312
+
313
+ // Wenn die Zeit heute schon vorbei ist → morgen 00:05
314
+ if (next <= now) {
315
+ next.setDate(next.getDate() + 1);
316
+ }
317
+
305
318
  const delay = next - now;
306
319
 
307
320
  setTimeout(async () => {
308
321
  try {
309
- this.adapter.log.info('[consumptionHelper] Monats-Reset (1. 00:05)');
310
- await this.adapter.setStateAsync('consumption.month_kwh', { val: 0, ack: true });
311
- await this.adapter.setStateAsync('costs.month_eur', { val: 0, ack: true });
312
- this.baselines.month = (await this.adapter.getStateAsync('consumption.total_kwh'))?.val || 0;
322
+ const nowCheck = new Date();
323
+
324
+ // Nur am 1. des Monats
325
+ if (nowCheck.getDate() === 1) {
326
+ this.adapter.log.info('[consumptionHelper] Monats-Reset (1. 00:05)');
327
+ await this.adapter.setStateAsync('consumption.month_kwh', { val: 0, ack: true });
328
+ await this.adapter.setStateAsync('costs.month_eur', { val: 0, ack: true });
329
+ this.baselines.month = (await this.adapter.getStateAsync('consumption.total_kwh'))?.val || 0;
330
+ }
313
331
  } catch (err) {
314
332
  this.adapter.log.warn(`[consumptionHelper] Fehler beim Monatsreset: ${err.message}`);
315
333
  }
334
+
335
+ // Morgen wieder planen
316
336
  this._scheduleMonthlyReset();
317
337
  }, delay);
318
338
  },
@@ -123,7 +123,7 @@ const frostHelper = {
123
123
  val: shouldRun,
124
124
  ack: false,
125
125
  });
126
- this.adapter.log.debug(
126
+ this.adapter.log.info(
127
127
  `[frostHelper] Frostschutz → Pumpe ${shouldRun ? 'EIN' : 'AUS'} (Außen=${outside}°C, Grenze=${frostTemp}°C)`,
128
128
  );
129
129
  }
@@ -0,0 +1,362 @@
1
+ 'use strict';
2
+
3
+ /**
4
+ * pumpHelper4.js
5
+ * ----------------------------------------------------------
6
+ * Drucksensor-Logik für Pumpenbereich
7
+ *
8
+ * Verarbeitet:
9
+ * - pump.pressure.current_bar
10
+ * - pump.pressure.previous_bar
11
+ * - pump.pressure.status_text
12
+ * - pump.pressure.last_update
13
+ *
14
+ * Liest:
15
+ * - Objekt-ID des Drucksensors aus jsonConfig
16
+ *
17
+ * Version: 1.0.0
18
+ */
19
+
20
+ const pumpHelper4 = {
21
+ adapter: null,
22
+ pressureObjectId: null,
23
+
24
+ /**
25
+ * Initialisiert den Helper
26
+ *
27
+ * @param {ioBroker.Adapter} adapter – aktive Adapterinstanz
28
+ */
29
+ async init(adapter) {
30
+ this.adapter = adapter;
31
+
32
+ this.pressureObjectId = adapter.config.pressure_sensor_oid || '';
33
+ adapter.log.info(`[pumpHelper4] Initialisierung gestartet (OID: ${this.pressureObjectId || 'nicht gesetzt'})`);
34
+
35
+ if (!this.pressureObjectId) {
36
+ adapter.log.info('[pumpHelper4] Kein Drucksensor in der Instanzkonfiguration angegeben.');
37
+ return;
38
+ }
39
+
40
+ // Drucksensor abonnieren
41
+ adapter.subscribeForeignStates(this.pressureObjectId);
42
+
43
+ adapter.log.info('[pumpHelper4] Erfolgreich initialisiert');
44
+
45
+ // Benutzerdefinierte Normaldruck-Werte einmalig laden
46
+ this.adapter.getStateAsync('pump.pressure.normal_min_bar').then(s => {
47
+ this.lastMin = Number(s?.val) || 0.2;
48
+ });
49
+ this.adapter.getStateAsync('pump.pressure.normal_max_bar').then(s => {
50
+ this.lastMax = Number(s?.val) || 0.8;
51
+ });
52
+ },
53
+
54
+ /**
55
+ * Verarbeitet alle relevanten State-Änderungen
56
+ *
57
+ * @param {string} id - Objekt-ID
58
+ * @param {ioBroker.State} state - Neuer Wert
59
+ */
60
+ async handleStateChange(id, state) {
61
+ if (!state) {
62
+ return;
63
+ }
64
+
65
+ // ------------------------------------------
66
+ // Manueller Reset des Lernsystems
67
+ // ------------------------------------------
68
+ if (id === 'poolcontrol.0.pump.pressure.learning.reset' && state.val === true) {
69
+ const current = await this._getNumber('pump.pressure.current_bar');
70
+
71
+ // Lernwerte zurücksetzen
72
+ await this.adapter.setStateAsync('pump.pressure.learning.learned_min_bar', { val: current, ack: true });
73
+ await this.adapter.setStateAsync('pump.pressure.learning.learned_max_bar', { val: current, ack: true });
74
+ await this.adapter.setStateAsync('pump.pressure.learning.avg_bar', { val: current, ack: true });
75
+ await this.adapter.setStateAsync('pump.pressure.learning.trend_rising', { val: 0, ack: true });
76
+ await this.adapter.setStateAsync('pump.pressure.learning.trend_falling', { val: 0, ack: true });
77
+ await this.adapter.setStateAsync('pump.pressure.learning.trend_stability', { val: 0, ack: true });
78
+
79
+ // Diagnose-Text
80
+ await this.adapter.setStateAsync('pump.pressure.status_text_diagnostic', {
81
+ val: 'Lernsystem zurückgesetzt',
82
+ ack: true,
83
+ });
84
+
85
+ // Reset-Button zurücksetzen
86
+ await this.adapter.setStateAsync('pump.pressure.learning.reset', {
87
+ val: false,
88
+ ack: true,
89
+ });
90
+
91
+ return; // WICHTIG: nichts weiter tun
92
+ }
93
+
94
+ // ------------------------------------------
95
+ // Live-Update der Min/Max-Werte
96
+ // ------------------------------------------
97
+ if (id === 'poolcontrol.0.pump.pressure.normal_min_bar') {
98
+ this.lastMin = Number(state.val) || 0.2;
99
+ this.adapter.log.debug(`[pumpHelper4] Neuer Minimaldruck übernommen: ${this.lastMin} bar`);
100
+ return;
101
+ }
102
+
103
+ if (id === 'poolcontrol.0.pump.pressure.normal_max_bar') {
104
+ this.lastMax = Number(state.val) || 0.8;
105
+ this.adapter.log.debug(`[pumpHelper4] Neuer Maximaldruck übernommen: ${this.lastMax} bar`);
106
+ return;
107
+ }
108
+
109
+ // Nicht der Drucksensor? Dann raus.
110
+ if (id !== this.pressureObjectId) {
111
+ return;
112
+ }
113
+
114
+ try {
115
+ const newBar = Number(state.val);
116
+
117
+ if (isNaN(newBar)) {
118
+ this.adapter.log.warn(`[pumpHelper4] Ungültiger Druckwert empfangen: ${state.val}`);
119
+ return;
120
+ }
121
+
122
+ // ------------------------------------------
123
+ // NEU: Pumpenschalter prüfen
124
+ // ------------------------------------------
125
+ const pumpSwitch = await this.adapter.getStateAsync('pump.pump_switch');
126
+ const pumpIsOn = !!pumpSwitch?.val;
127
+
128
+ // Zeitstempel immer setzen
129
+ const now = new Date().toISOString();
130
+
131
+ // Wenn Pumpe AUS → Status setzen und raus
132
+ if (!pumpIsOn) {
133
+ await this.adapter.setStateAsync('pump.pressure.status_text', {
134
+ val: 'Pumpe aus – kein Filterdruck',
135
+ ack: true,
136
+ });
137
+
138
+ // trotzdem current_bar und last_update aktualisieren
139
+ await this.adapter.setStateAsync('pump.pressure.current_bar', {
140
+ val: newBar,
141
+ ack: true,
142
+ });
143
+
144
+ await this.adapter.setStateAsync('pump.pressure.last_update', {
145
+ val: now,
146
+ ack: true,
147
+ });
148
+
149
+ return;
150
+ }
151
+
152
+ // Alten Wert lesen
153
+ const previous = await this._getNumber('pump.pressure.current_bar');
154
+
155
+ // previous_bar nur updaten, wenn alter Wert > 0
156
+ if (previous > 0) {
157
+ await this.adapter.setStateAsync('pump.pressure.previous_bar', {
158
+ val: previous,
159
+ ack: true,
160
+ });
161
+ }
162
+
163
+ // ------------------------------------------
164
+ // Trend-Erkennung
165
+ // ------------------------------------------
166
+ let rising = 0;
167
+ let falling = 0;
168
+ let stability = 0;
169
+
170
+ const diff = newBar - previous;
171
+
172
+ // Steigend
173
+ if (diff > 0.01) {
174
+ rising = Number(diff.toFixed(3));
175
+ }
176
+
177
+ // Fallend
178
+ if (diff < -0.01) {
179
+ falling = Number(Math.abs(diff).toFixed(3));
180
+ }
181
+
182
+ // Stabil
183
+ if (Math.abs(diff) <= 0.01) {
184
+ stability = 1;
185
+ }
186
+
187
+ // Werte setzen
188
+ await this.adapter.setStateAsync('pump.pressure.learning.trend_rising', {
189
+ val: rising,
190
+ ack: true,
191
+ });
192
+
193
+ await this.adapter.setStateAsync('pump.pressure.learning.trend_falling', {
194
+ val: falling,
195
+ ack: true,
196
+ });
197
+
198
+ await this.adapter.setStateAsync('pump.pressure.learning.trend_stability', {
199
+ val: stability,
200
+ ack: true,
201
+ });
202
+
203
+ // current_bar aktualisieren
204
+ await this.adapter.setStateAsync('pump.pressure.current_bar', {
205
+ val: newBar,
206
+ ack: true,
207
+ });
208
+
209
+ // ------------------------------------------
210
+ // Gleitender Druckdurchschnitt
211
+ // ------------------------------------------
212
+ const oldAvg = await this._getNumber('pump.pressure.learning.avg_bar');
213
+ const newAvg = oldAvg === 0 ? newBar : oldAvg * 0.9 + newBar * 0.1;
214
+
215
+ await this.adapter.setStateAsync('pump.pressure.learning.avg_bar', {
216
+ val: Number(newAvg.toFixed(3)),
217
+ ack: true,
218
+ });
219
+
220
+ // ------------------------------------------
221
+ // Learning MIN/MAX (autoadaptiv)
222
+ // ------------------------------------------
223
+ const learnedMin = await this._getNumber('pump.pressure.learning.learned_min_bar');
224
+ const learnedMax = await this._getNumber('pump.pressure.learning.learned_max_bar');
225
+
226
+ // Wenn noch keine Werte vorhanden
227
+ let newLearnedMin = learnedMin || newBar;
228
+ let newLearnedMax = learnedMax || newBar;
229
+
230
+ // Pumpe stabil → Min/Max leicht erweitern
231
+ if (newBar < newLearnedMin) {
232
+ newLearnedMin = newBar;
233
+ }
234
+
235
+ if (newBar > newLearnedMax) {
236
+ newLearnedMax = newBar;
237
+ }
238
+
239
+ await this.adapter.setStateAsync('pump.pressure.learning.learned_min_bar', {
240
+ val: Number(newLearnedMin.toFixed(3)),
241
+ ack: true,
242
+ });
243
+
244
+ await this.adapter.setStateAsync('pump.pressure.learning.learned_max_bar', {
245
+ val: Number(newLearnedMax.toFixed(3)),
246
+ ack: true,
247
+ });
248
+
249
+ // Zeitstempel schreiben
250
+ // const now = new Date().toISOString();
251
+ await this.adapter.setStateAsync('pump.pressure.last_update', {
252
+ val: now,
253
+ ack: true,
254
+ });
255
+
256
+ // Statustext setzen
257
+ const text = this._getStatusText(newBar);
258
+ await this.adapter.setStateAsync('pump.pressure.status_text', {
259
+ val: text,
260
+ ack: true,
261
+ });
262
+
263
+ // ------------------------------------------
264
+ // Diagnose-Text (Trend + Lernen)
265
+ // ------------------------------------------
266
+ let diagText = '';
267
+
268
+ if (!pumpIsOn) {
269
+ diagText = 'Pumpe aus – keine Diagnose möglich';
270
+ } else {
271
+ const trendParts = [];
272
+
273
+ if (rising > 0) {
274
+ trendParts.push(`steigend (+${rising} bar)`);
275
+ }
276
+ if (falling > 0) {
277
+ trendParts.push(`fallend (-${falling} bar)`);
278
+ }
279
+ if (stability === 1) {
280
+ trendParts.push('stabil');
281
+ }
282
+
283
+ const trendText = trendParts.length > 0 ? trendParts.join(', ') : 'kein Trend erkennbar';
284
+
285
+ const avg = await this._getNumber('pump.pressure.learning.avg_bar');
286
+ const learnedMin = await this._getNumber('pump.pressure.learning.learned_min_bar');
287
+ const learnedMax = await this._getNumber('pump.pressure.learning.learned_max_bar');
288
+
289
+ diagText =
290
+ `Trend: ${trendText} | ` +
291
+ `Durchschnitt: ${avg.toFixed(3)} bar | ` +
292
+ `Lernbereich: ${learnedMin.toFixed(3)}–${learnedMax.toFixed(3)} bar`;
293
+ }
294
+
295
+ // in State schreiben
296
+ await this.adapter.setStateAsync('pump.pressure.status_text_diagnostic', {
297
+ val: diagText,
298
+ ack: true,
299
+ });
300
+
301
+ this.adapter.log.debug(`[pumpHelper4] Druck aktualisiert: ${newBar} bar (vorher: ${previous} bar)`);
302
+ } catch (err) {
303
+ this.adapter.log.warn(`[pumpHelper4] Fehler bei handleStateChange: ${err.message}`);
304
+ }
305
+ },
306
+
307
+ /**
308
+ * Gibt einen einfachen Status basierend auf dem Druck zurück.
309
+ *
310
+ * Berücksichtigt Benutzerwerte:
311
+ * - normal_min_bar
312
+ * - normal_max_bar
313
+ *
314
+ * @param {number} bar - aktueller Druck in bar
315
+ * @returns {string} Statusmeldung
316
+ */
317
+ _getStatusText(bar) {
318
+ if (isNaN(bar)) {
319
+ return 'Ungültiger Druckwert';
320
+ }
321
+
322
+ const min = this.lastMin ?? 0.2;
323
+ const max = this.lastMax ?? 0.8;
324
+
325
+ // Zu niedrig?
326
+ if (bar < min) {
327
+ return `Druck zu niedrig (unter ${min} bar)`;
328
+ }
329
+
330
+ // Normalbereich?
331
+ if (bar >= min && bar <= max) {
332
+ return `Normaldruck (${min}–${max} bar)`;
333
+ }
334
+
335
+ // Zu hoch?
336
+ if (bar > max) {
337
+ return `Druck erhöht (über ${max} bar)`;
338
+ }
339
+
340
+ return 'Unbekannter Zustand';
341
+ },
342
+
343
+ /**
344
+ * Liest einen numerischen State
345
+ *
346
+ * @param {string} id - Die vollständige Objekt-ID des State, dessen numerischer Wert gelesen werden soll
347
+ */
348
+ async _getNumber(id) {
349
+ const state = await this.adapter.getStateAsync(id);
350
+ const val = Number(state?.val);
351
+ return isNaN(val) ? 0 : val;
352
+ },
353
+
354
+ /**
355
+ * Cleanup bei Adapter-Unload
356
+ */
357
+ cleanup() {
358
+ this.adapter?.log.debug('[pumpHelper4] Cleanup ausgeführt.');
359
+ },
360
+ };
361
+
362
+ module.exports = pumpHelper4;
@@ -0,0 +1,312 @@
1
+ 'use strict';
2
+
3
+ /**
4
+ * pumpStates4.js
5
+ * ----------------------------------------------------------
6
+ * Legt die States für den Drucksensor im Pumpenbereich an.
7
+ *
8
+ * Ordnerstruktur:
9
+ * pump.pressure.*
10
+ *
11
+ * Diese Datei ergänzt die bisherigen pumpStates.js,
12
+ * pumpStates2.js und pumpStates3.js um Drucksensor-Unterstützung.
13
+ *
14
+ * Alle States sind echte Variablen (persist = true nur falls nötig).
15
+ * ----------------------------------------------------------
16
+ * Version: 1.0.0
17
+ */
18
+
19
+ /**
20
+ * Erstellt alle States für den Drucksensor im Bereich pump.pressure.
21
+ *
22
+ * @param {import('iobroker').Adapter} adapter – Aktive ioBroker Adapterinstanz
23
+ */
24
+ async function createPumpStates4(adapter) {
25
+ adapter.log.debug('[pumpStates4] Initialisierung der Drucksensor-States gestartet.');
26
+
27
+ // ------------------------------------------------------
28
+ // Root-Kanal: pump.pressure
29
+ // ------------------------------------------------------
30
+ await adapter.setObjectNotExistsAsync('pump.pressure', {
31
+ type: 'channel',
32
+ common: {
33
+ name: 'Pumpen-Drucksensor (Filterdruck)',
34
+ },
35
+ native: {},
36
+ });
37
+
38
+ // ------------------------------------------------------
39
+ // Aktueller Druck (bar)
40
+ // ------------------------------------------------------
41
+ await adapter.setObjectNotExistsAsync('pump.pressure.current_bar', {
42
+ type: 'state',
43
+ common: {
44
+ name: 'Aktueller Druck (bar)',
45
+ desc: 'Gemessener aktueller Filterdruck in bar',
46
+ type: 'number',
47
+ role: 'value.pressure',
48
+ unit: 'bar',
49
+ read: true,
50
+ write: false,
51
+ persist: false, // Livewert → NICHT speichern
52
+ },
53
+ native: {},
54
+ });
55
+
56
+ // initial setzen (überinstallationssicher)
57
+ const existing = await adapter.getStateAsync('pump.pressure.current_bar');
58
+ if (existing === null || existing === undefined) {
59
+ await adapter.setStateAsync('pump.pressure.current_bar', { val: 0, ack: true });
60
+ }
61
+
62
+ // ------------------------------------------------------
63
+ // Vorheriger Druckwert (bar)
64
+ // ------------------------------------------------------
65
+ await adapter.setObjectNotExistsAsync('pump.pressure.previous_bar', {
66
+ type: 'state',
67
+ common: {
68
+ name: 'Vorheriger Druck (bar)',
69
+ desc: 'Letzter bekannter Filterdruck vor der aktuellen Messung',
70
+ type: 'number',
71
+ role: 'value.pressure',
72
+ unit: 'bar',
73
+ read: true,
74
+ write: false,
75
+ persist: false,
76
+ },
77
+ native: {},
78
+ });
79
+
80
+ // initial setzen (überinstallationssicher)
81
+ const prev = await adapter.getStateAsync('pump.pressure.previous_bar');
82
+ if (prev === null || prev === undefined) {
83
+ await adapter.setStateAsync('pump.pressure.previous_bar', { val: 0, ack: true });
84
+ }
85
+
86
+ // ------------------------------------------------------
87
+ // Benutzerdefinierte Normaldruck-Bereiche (bar)
88
+ // ------------------------------------------------------
89
+
90
+ // Unterer Normaldruck-Grenzwert (bar)
91
+ await adapter.setObjectNotExistsAsync('pump.pressure.normal_min_bar', {
92
+ type: 'state',
93
+ common: {
94
+ name: 'Normaldruck MIN (bar)',
95
+ desc: 'Unterer Grenzwert des Normaldruck-Bereichs. Werte unterhalb gelten als zu niedrig.',
96
+ type: 'number',
97
+ role: 'level',
98
+ unit: 'bar',
99
+ read: true,
100
+ write: true,
101
+ min: 0,
102
+ max: 2,
103
+ def: 0.2,
104
+ persist: true,
105
+ },
106
+ native: {},
107
+ });
108
+
109
+ const minExisting = await adapter.getStateAsync('pump.pressure.normal_min_bar');
110
+ if (minExisting === null || minExisting === undefined) {
111
+ await adapter.setStateAsync('pump.pressure.normal_min_bar', { val: 0.2, ack: true });
112
+ }
113
+
114
+ // Oberer Normaldruck-Grenzwert (bar)
115
+ await adapter.setObjectNotExistsAsync('pump.pressure.normal_max_bar', {
116
+ type: 'state',
117
+ common: {
118
+ name: 'Normaldruck MAX (bar)',
119
+ desc: 'Oberer Grenzwert des Normaldruck-Bereichs. Werte oberhalb gelten als erhöht.',
120
+ type: 'number',
121
+ role: 'level',
122
+ unit: 'bar',
123
+ read: true,
124
+ write: true,
125
+ min: 0,
126
+ max: 2,
127
+ def: 0.8,
128
+ persist: true,
129
+ },
130
+ native: {},
131
+ });
132
+
133
+ const maxExisting = await adapter.getStateAsync('pump.pressure.normal_max_bar');
134
+ if (maxExisting === null || maxExisting === undefined) {
135
+ await adapter.setStateAsync('pump.pressure.normal_max_bar', { val: 0.8, ack: true });
136
+ }
137
+
138
+ // ------------------------------------------------------
139
+ // Optional vorbereitete States für spätere Erweiterungen
140
+ // ------------------------------------------------------
141
+
142
+ // Zustand / Bewertung
143
+ await adapter.setObjectNotExistsAsync('pump.pressure.status_text', {
144
+ type: 'state',
145
+ common: {
146
+ name: 'Statusmeldung',
147
+ desc: 'Bewertung des Drucks (z. B. Normal / Hoch / Rückspülen nötig)',
148
+ type: 'string',
149
+ role: 'text',
150
+ read: true,
151
+ write: false,
152
+ persist: false,
153
+ },
154
+ native: {},
155
+ });
156
+
157
+ // Erweiterte Diagnoseausgabe
158
+ await adapter.setObjectNotExistsAsync('pump.pressure.status_text_diagnostic', {
159
+ type: 'state',
160
+ common: {
161
+ name: 'Diagnose',
162
+ desc: 'Ausführliche Diagnose basierend auf Trend- und Lernwerten',
163
+ type: 'string',
164
+ role: 'text',
165
+ read: true,
166
+ write: false,
167
+ persist: false,
168
+ },
169
+ native: {},
170
+ });
171
+
172
+ // Letzte Aktualisierung
173
+ await adapter.setObjectNotExistsAsync('pump.pressure.last_update', {
174
+ type: 'state',
175
+ common: {
176
+ name: 'Letzte Aktualisierung',
177
+ desc: 'Zeitstempel der letzten Druckaktualisierung',
178
+ type: 'string',
179
+ role: 'date',
180
+ read: true,
181
+ write: false,
182
+ persist: false,
183
+ },
184
+ native: {},
185
+ });
186
+
187
+ // ------------------------------------------------------
188
+ // Lernwerte-Unterordner anlegen
189
+ // ------------------------------------------------------
190
+ await adapter.setObjectNotExistsAsync('pump.pressure.learning', {
191
+ type: 'channel',
192
+ common: {
193
+ name: 'Lernwerte (Druckverhalten der Pumpe)',
194
+ },
195
+ native: {},
196
+ });
197
+
198
+ // Unterer Lernwert (Wohlfühl-Minimum)
199
+ await adapter.setObjectNotExistsAsync('pump.pressure.learning.learned_min_bar', {
200
+ type: 'state',
201
+ common: {
202
+ name: 'Gelerntes Minimum (bar)',
203
+ desc: 'Niedrigster Druck, der im stabilen Betrieb häufig auftritt',
204
+ type: 'number',
205
+ unit: 'bar',
206
+ role: 'value',
207
+ read: true,
208
+ write: true,
209
+ persist: true,
210
+ },
211
+ native: {},
212
+ });
213
+
214
+ // Oberer Lernwert (Wohlfühl-Maximum)
215
+ await adapter.setObjectNotExistsAsync('pump.pressure.learning.learned_max_bar', {
216
+ type: 'state',
217
+ common: {
218
+ name: 'Gelerntes Maximum (bar)',
219
+ desc: 'Höchster Druck, der im stabilen Betrieb häufig auftritt',
220
+ type: 'number',
221
+ unit: 'bar',
222
+ role: 'value',
223
+ read: true,
224
+ write: true,
225
+ persist: true,
226
+ },
227
+ native: {},
228
+ });
229
+
230
+ // Gleitender Mittelwert
231
+ await adapter.setObjectNotExistsAsync('pump.pressure.learning.avg_bar', {
232
+ type: 'state',
233
+ common: {
234
+ name: 'Gleitender Durchschnittsdruck (bar)',
235
+ desc: 'Berechneter Mittelwert der letzten Messungen',
236
+ type: 'number',
237
+ unit: 'bar',
238
+ role: 'value',
239
+ read: true,
240
+ write: true,
241
+ persist: true,
242
+ },
243
+ native: {},
244
+ });
245
+
246
+ // Trend: Anstieg
247
+ await adapter.setObjectNotExistsAsync('pump.pressure.learning.trend_rising', {
248
+ type: 'state',
249
+ common: {
250
+ name: 'Trend Steigend (0–1)',
251
+ desc: 'Gibt an, wie stark der Druck steigt',
252
+ type: 'number',
253
+ role: 'indicator',
254
+ read: true,
255
+ write: true,
256
+ persist: false,
257
+ },
258
+ native: {},
259
+ });
260
+
261
+ // Trend: Abfall
262
+ await adapter.setObjectNotExistsAsync('pump.pressure.learning.trend_falling', {
263
+ type: 'state',
264
+ common: {
265
+ name: 'Trend Fallend (0–1)',
266
+ desc: 'Gibt an, wie stark der Druck fällt',
267
+ type: 'number',
268
+ role: 'indicator',
269
+ read: true,
270
+ write: true,
271
+ persist: false,
272
+ },
273
+ native: {},
274
+ });
275
+
276
+ // Trend: Stabilität
277
+ await adapter.setObjectNotExistsAsync('pump.pressure.learning.trend_stability', {
278
+ type: 'state',
279
+ common: {
280
+ name: 'Trend Stabilität (0–1)',
281
+ desc: 'Gibt an, wie stabil der Druck insgesamt ist',
282
+ type: 'number',
283
+ role: 'indicator',
284
+ read: true,
285
+ write: true,
286
+ persist: false,
287
+ },
288
+ native: {},
289
+ });
290
+
291
+ // Reset des Lernsystems
292
+ await adapter.setObjectNotExistsAsync('pump.pressure.learning.reset', {
293
+ type: 'state',
294
+ common: {
295
+ name: 'Lernsystem zurücksetzen',
296
+ desc: 'Setzt alle Lernwerte (Min/Max/Avg/Trend) auf den aktuellen Druck zurück',
297
+ type: 'boolean',
298
+ role: 'button',
299
+ read: true,
300
+ write: true,
301
+ def: false,
302
+ persist: false,
303
+ },
304
+ native: {},
305
+ });
306
+
307
+ adapter.log.debug('[pumpStates4] Drucksensor-States erfolgreich erstellt oder geprüft.');
308
+ }
309
+
310
+ module.exports = {
311
+ createPumpStates4,
312
+ };
package/main.js CHANGED
@@ -14,6 +14,7 @@ const statisticsHelperMonth = require('./lib/helpers/statisticsHelperMonth');
14
14
  const pumpHelper = require('./lib/helpers/pumpHelper');
15
15
  const pumpHelper2 = require('./lib/helpers/pumpHelper2');
16
16
  const pumpHelper3 = require('./lib/helpers/pumpHelper3');
17
+ const pumpHelper4 = require('./lib/helpers/pumpHelper4');
17
18
  const speechHelper = require('./lib/helpers/speechHelper');
18
19
  const consumptionHelper = require('./lib/helpers/consumptionHelper');
19
20
  const solarHelper = require('./lib/helpers/solarHelper');
@@ -29,6 +30,7 @@ const { createTemperatureStates } = require('./lib/stateDefinitions/temperatureS
29
30
  const { createPumpStates } = require('./lib/stateDefinitions/pumpStates');
30
31
  const { createPumpStates2 } = require('./lib/stateDefinitions/pumpStates2');
31
32
  const { createPumpStates3 } = require('./lib/stateDefinitions/pumpStates3');
33
+ const { createPumpStates4 } = require('./lib/stateDefinitions/pumpStates4');
32
34
  const { createSolarStates } = require('./lib/stateDefinitions/solarStates');
33
35
  const { createPhotovoltaicStates } = require('./lib/stateDefinitions/photovoltaicStates');
34
36
  const { createGeneralStates } = require('./lib/stateDefinitions/generalStates');
@@ -62,6 +64,7 @@ class Poolcontrol extends utils.Adapter {
62
64
  await createPumpStates(this);
63
65
  await createPumpStates2(this);
64
66
  await createPumpStates3(this);
67
+ await createPumpStates4(this);
65
68
 
66
69
  // --- Temperaturverwaltung ---
67
70
  await createTemperatureStates(this);
@@ -115,6 +118,7 @@ class Poolcontrol extends utils.Adapter {
115
118
  pumpHelper.init(this);
116
119
  pumpHelper2.init(this);
117
120
  pumpHelper3.init(this);
121
+ pumpHelper4.init(this);
118
122
  speechHelper.init(this);
119
123
  consumptionHelper.init(this);
120
124
  solarHelper.init(this);
@@ -150,6 +154,9 @@ class Poolcontrol extends utils.Adapter {
150
154
  if (pumpHelper3.cleanup) {
151
155
  pumpHelper3.cleanup();
152
156
  }
157
+ if (pumpHelper4.cleanup) {
158
+ pumpHelper4.cleanup();
159
+ }
153
160
  if (speechHelper.cleanup) {
154
161
  speechHelper.cleanup();
155
162
  }
@@ -221,6 +228,12 @@ class Poolcontrol extends utils.Adapter {
221
228
  this.log.warn(`[pumpHelper3] Fehler in handleStateChange: ${e.message}`);
222
229
  }
223
230
 
231
+ try {
232
+ pumpHelper4.handleStateChange(id, state);
233
+ } catch (e) {
234
+ this.log.warn(`[pumpHelper4] Fehler in handleStateChange: ${e.message}`);
235
+ }
236
+
224
237
  try {
225
238
  speechHelper.handleStateChange(id, state);
226
239
  } catch (e) {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "iobroker.poolcontrol",
3
- "version": "0.6.4",
3
+ "version": "0.7.0",
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",