iobroker.poolcontrol 1.3.26 → 1.3.28

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
@@ -48,7 +48,7 @@ It provides automation for pumps, heating, solar and photovoltaic control as wel
48
48
  - Priority and block logic
49
49
  - Status section under `solar.extended.*`
50
50
 
51
- - **Photovoltaic Control (since v0.6.0)**
51
+ - **Photovoltaic Control**
52
52
  - Pump control based on PV surplus and household consumption
53
53
  - Start logic using configurable surplus margins
54
54
  - Optional overrun during cloudy phases
@@ -56,7 +56,7 @@ It provides automation for pumps, heating, solar and photovoltaic control as wel
56
56
  - Supports external energy object IDs
57
57
  - Pump mode: `Automatic (PV)`
58
58
 
59
- - **Heating / Heat Pump Control (test phase)**
59
+ - **Heating / Heat Pump Control**
60
60
  - Automatic control of heating rod or heat pump
61
61
  - Configurable target and safety temperatures
62
62
  - Optional pump prerun and overrun
@@ -102,7 +102,7 @@ It provides automation for pumps, heating, solar and photovoltaic control as wel
102
102
  - Automatic reset after completed backwash
103
103
  - PV integration for circulation targets
104
104
 
105
- - **Pressure Sensor Integration (since v0.7.x)**
105
+ - **Pressure Sensor Integration**
106
106
  - Real-time pressure measurement
107
107
  - Trend analysis
108
108
  - Learning average values
@@ -176,6 +176,17 @@ It provides automation for pumps, heating, solar and photovoltaic control as wel
176
176
  - No chlorine control
177
177
  - No automatic dosing
178
178
 
179
+ **Chemistry Tools**
180
+ - pH Plus calculator
181
+ - pH Minus calculator
182
+ - Salt calculator
183
+ - Manual calculation helpers
184
+ - Pool volume prefill support
185
+ - Optional manual value override
186
+ - Result texts with validation and error handling
187
+ -No automatic chemical dosing
188
+ - Informational only
189
+
179
190
  - **AI System**
180
191
  - Weather hints (Open-Meteo)
181
192
  - Pool recommendations
@@ -252,6 +263,22 @@ New features are added regularly – please refer to the changelog.
252
263
  ---
253
264
 
254
265
  ## Changelog
266
+ ### 1.3.28 (2026-06-03)
267
+
268
+ - Added configurable startup power check timeout for pump monitoring (`pump.startup_power_check_timeout_sec`).
269
+ - Default behavior remains unchanged (5 seconds).
270
+ - Improved compatibility with delayed power measurements from smart plugs and power meters.
271
+
272
+ ### 1.3.27 (2026-05-30)
273
+
274
+ - Added new Chemistry Tools section
275
+ - Added pH Plus Calculator
276
+ - Added pH Minus Calculator
277
+ - Added Salt Calculator
278
+ - Added validation, result texts and error handling for all calculators
279
+ - Added automatic pool volume prefill support
280
+ - Expanded documentation and function overviews
281
+
255
282
  ### 1.3.26 (2026-05-28)
256
283
 
257
284
  - Reworked multiple warnings and review notes from official ioBroker checks
@@ -270,25 +297,6 @@ New features are added regularly – please refer to the changelog.
270
297
  - Improved README and changelog structure
271
298
  - Repository checker recommendations reviewed
272
299
 
273
- ### 1.3.23 (2026-05-26)
274
-
275
- - Added extended temperature diagnostics for all temperature sensors:
276
- - last valid value
277
- - last valid value timestamp
278
- - minutes since last value
279
- - source status (`ok`, `warning`, `not_received`, `invalid_timestamp`)
280
- - Added automatic recovery mechanism for stalled temperature updates
281
- - Recovery runs only when a sensor enters warning state and uses cooldown protection
282
- - Switched temperature helper timers to ioBroker adapter timers
283
- - Improved visibility and troubleshooting for missing or delayed temperature updates
284
-
285
- ### 1.3.22 (2026-05-24)
286
-
287
- - Improved ORP pH reference synchronization
288
- - ORP helper now updates pH reference independently from ORP value processing
289
- - Immediate update of ORP pH reference when pH enabled state or pH value changes
290
- - Fixed missing pH reference updates when ORP evaluation was blocked, invalid or waiting for measurement conditions
291
-
292
300
  ## Archived Release History
293
301
 
294
302
  For older releases and archived version history see:
package/io-package.json CHANGED
@@ -1,8 +1,34 @@
1
1
  {
2
2
  "common": {
3
3
  "name": "poolcontrol",
4
- "version": "1.3.26",
4
+ "version": "1.3.28",
5
5
  "news": {
6
+ "1.3.28": {
7
+ "en": "Added configurable startup power check timeout for pump monitoring (5-10 seconds). Improved compatibility with delayed power measurements from smart plugs and power meters.",
8
+ "de": "Konfigurierbares Timeout für die Leistungsprüfung nach Pumpenstart (5-10 Sekunden) hinzugefügt. Kompatibilität mit verzögert aktualisierten Leistungswerten von Smart-Steckdosen und Leistungsmessern verbessert.",
9
+ "ru": "Добавлен настраиваемый тайм-аут проверки мощности при запуске для мониторинга насоса (5–10 секунд). Улучшена совместимость с измерениями мощности с задержкой от интеллектуальных розеток и измерителей мощности.",
10
+ "pt": "Adicionado tempo limite de verificação de energia de inicialização configurável para monitoramento da bomba (5 a 10 segundos). Compatibilidade aprimorada com medições de energia atrasadas de plugues inteligentes e medidores de energia.",
11
+ "nl": "Configureerbare time-out voor stroomcontrole bij opstarten toegevoegd voor pompbewaking (5-10 seconden). Verbeterde compatibiliteit met vertraagde stroommetingen van slimme stekkers en stroommeters.",
12
+ "fr": "Ajout d'un délai d'expiration configurable pour la vérification de l'alimentation au démarrage pour la surveillance de la pompe (5 à 10 secondes). Compatibilité améliorée avec les mesures de puissance retardées des prises intelligentes et des compteurs de puissance.",
13
+ "it": "Aggiunto timeout configurabile del controllo dell'alimentazione all'avvio per il monitoraggio della pompa (5-10 secondi). Compatibilità migliorata con misurazioni di potenza ritardate da prese intelligenti e misuratori di potenza.",
14
+ "es": "Se agregó un tiempo de espera de verificación de energía de inicio configurable para el monitoreo de la bomba (5-10 segundos). Compatibilidad mejorada con mediciones de potencia retrasadas de enchufes inteligentes y medidores de potencia.",
15
+ "pl": "Dodano konfigurowalny limit czasu sprawdzania mocy rozruchowej dla monitorowania pompy (5-10 sekund). Poprawiona kompatybilność z opóźnionymi pomiarami mocy z inteligentnych wtyczek i mierników mocy.",
16
+ "uk": "Додано настроюваний тайм-аут перевірки живлення при запуску для моніторингу насоса (5-10 секунд). Покращена сумісність із затримкою вимірювання потужності від розумних розеток і лічильників.",
17
+ "zh-cn": "添加了用于泵监控的可配置启动电源检查超时(5-10 秒)。改进了与智能插头和功率计的延迟功率测量的兼容性。"
18
+ },
19
+ "1.3.27": {
20
+ "en": "Added new Chemistry Tools with pH Plus Calculator, pH Minus Calculator and Salt Calculator. Added validation, result texts, error handling and automatic pool volume prefill. Expanded documentation and function overviews.",
21
+ "de": "Neuen Bereich Chemistry Tools mit pH-Plus-Rechner, pH-Minus-Rechner und Salz-Rechner hinzugefügt. Validierungen, Ergebnistexte, Fehlerbehandlung und automatische Vorbelegung des Poolvolumens ergänzt. Dokumentation und Funktionsübersichten erweitert.",
22
+ "ru": "Добавлены новые химические инструменты с калькулятором pH плюс, калькулятором pH минус и калькулятором соли. Добавлена ​​проверка, тексты результатов, обработка ошибок и автоматическое предварительное заполнение объема пула. Расширенная документация и обзоры функций.",
23
+ "pt": "Adicionadas novas ferramentas de química com calculadora pH Plus, calculadora pH menos e calculadora de sal. Adicionados validação, textos de resultados, tratamento de erros e preenchimento automático do volume do pool. Documentação expandida e visões gerais de funções.",
24
+ "nl": "Nieuwe chemiehulpmiddelen toegevoegd met pH Plus-calculator, pH-minuscalculator en Zoutcalculator. Validatie, resultaatteksten, foutafhandeling en automatisch vooraf invullen van poolvolumes toegevoegd. Uitgebreide documentatie en functieoverzichten.",
25
+ "fr": "Ajout de nouveaux outils de chimie avec calculateur de pH Plus, calculateur de pH Moins et calculateur de sel. Ajout de la validation, des textes de résultats, de la gestion des erreurs et du pré-remplissage automatique du volume du pool. Documentation étendue et aperçus des fonctions.",
26
+ "it": "Aggiunti nuovi strumenti di chimica con calcolatore di pH più, calcolatore di pH meno e calcolatore di sale. Aggiunta convalida, testi dei risultati, gestione degli errori e precompilazione automatica del volume del pool. Documentazione estesa e panoramiche delle funzioni.",
27
+ "es": "Se agregaron nuevas herramientas de química con calculadora de pH Plus, calculadora de pH Minus y calculadora de sal. Se agregó validación, textos de resultados, manejo de errores y precarga automática del volumen del grupo. Documentación ampliada y descripciones generales de funciones.",
28
+ "pl": "Dodano nowe narzędzia chemiczne z kalkulatorem pH Plus, kalkulatorem pH Minus i kalkulatorem soli. Dodano weryfikację, teksty wyników, obsługę błędów i automatyczne wstępne wypełnianie objętości puli. Rozszerzona dokumentacja i przeglądy funkcji.",
29
+ "uk": "Додано нові хімічні інструменти з калькулятором pH Plus, калькулятором pH мінус і калькулятором солі. Додано перевірку, тексти результатів, обробку помилок і автоматичне попереднє заповнення обсягу пулу. Розширена документація та огляд функцій.",
30
+ "zh-cn": "添加了新的化学工具,包括 pH 加计算器、pH 减计算器和盐计算器。添加了验证、结果文本、错误处理和自动池容量预填充。扩展的文档和功能概述。"
31
+ },
6
32
  "1.3.26": {
7
33
  "en": "Reworked several warnings and review notes from official ioBroker checks. Various small improvements and internal cleanups.",
8
34
  "de": "Mehrere Warnungen und Review-Hinweise aus offiziellen ioBroker-Checks überarbeitet. Diverse kleine Verbesserungen und interne Bereinigungen.",
@@ -41,32 +67,6 @@
41
67
  "pl": "Zaktualizowane komponenty repozytorium i opiekuna: zaktualizowane zależności skryptu wydania, ulepszona struktura README/dziennika zmian i sprawdzone sugestie dotyczące sprawdzania repozytorium",
42
68
  "uk": "Оновлений репозиторій і компоненти супроводжувача: оновлені залежності сценарію випуску, покращена структура README/журналу змін і переглянуті пропозиції перевірки сховища",
43
69
  "zh-cn": "更新了存储库和维护者组件:更新了发布脚本依赖项,改进了自述文件/变更日志结构并审查了存储库检查器建议"
44
- },
45
- "1.3.23": {
46
- "en": "Temperature diagnostics extended: Added new diagnostic states for all temperature sensors (last valid value, timestamp, minutes since last value, source status). Added self-healing/recovery mechanism for stalled or missing temperature events. Temperature helper updated to ioBroker-compliant timers.",
47
- "de": "Temperaturbereich erweitert: Neue Diagnosewerte für alle Temperatursensoren ergänzt (letzter gültiger Wert, Zeitstempel, Minuten seit letztem Wert, Quellenstatus). Zusätzlich Selbstheilungs-/Recovery-Mechanismus für ausgefallene oder hängende Temperatur-Events ergänzt. Temperaturhelper auf ioBroker-konforme Timer umgestellt.",
48
- "ru": "Расширенная диагностика температуры: добавлены новые диагностические состояния для всех датчиков температуры (последнее действительное значение, временная метка, минуты с момента последнего значения, состояние источника). Добавлен механизм самовосстановления/восстановления при остановке или отсутствии температурных событий. Помощник по температуре обновлен до таймеров, совместимых с ioBroker.",
49
- "pt": "Diagnóstico de temperatura estendido: Adicionados novos estados de diagnóstico para todos os sensores de temperatura (último valor válido, carimbo de data/hora, minutos desde o último valor, status da fonte). Adicionado mecanismo de autocorreção/recuperação para eventos de temperatura paralisados ​​ou ausentes. Auxiliar de temperatura atualizado para temporizadores compatíveis com ioBroker.",
50
- "nl": "Temperatuurdiagnostiek uitgebreid: Nieuwe diagnostische statussen toegevoegd voor alle temperatuursensoren (laatste geldige waarde, tijdstempel, minuten sinds laatste waarde, bronstatus). Zelfherstel-/herstelmechanisme toegevoegd voor vastgelopen of ontbrekende temperatuurgebeurtenissen. Temperatuurhelper bijgewerkt naar ioBroker-compatibele timers.",
51
- "fr": "Diagnostics de température étendus : ajout de nouveaux états de diagnostic pour tous les capteurs de température (dernière valeur valide, horodatage, minutes depuis la dernière valeur, état de la source). Ajout d'un mécanisme d'auto-guérison/récupération pour les événements de température bloqués ou manquants. Assistant de température mis à jour avec des minuteries conformes à ioBroker.",
52
- "it": "Diagnostica della temperatura estesa: aggiunti nuovi stati diagnostici per tutti i sensori di temperatura (ultimo valore valido, timestamp, minuti dall'ultimo valore, stato della sorgente). Aggiunto meccanismo di autoriparazione/recupero per eventi di temperatura bloccati o mancanti. Assistente per la temperatura aggiornato ai timer compatibili con ioBroker.",
53
- "es": "Diagnóstico de temperatura extendido: se agregaron nuevos estados de diagnóstico para todos los sensores de temperatura (último valor válido, marca de tiempo, minutos desde el último valor, estado de fuente). Se agregó un mecanismo de autorreparación/recuperación para eventos de temperatura estancados o faltantes. Asistente de temperatura actualizado a temporizadores compatibles con ioBroker.",
54
- "pl": "Rozszerzona diagnostyka temperatury: Dodano nowe stany diagnostyczne dla wszystkich czujników temperatury (ostatnia ważna wartość, znacznik czasu, minuty od ostatniej wartości, stan źródła). Dodano mechanizm samonaprawy/odzyskiwania w przypadku zablokowania lub braku zdarzeń związanych z temperaturą. Pomocnik temperatury zaktualizowany do timerów zgodnych z ioBroker.",
55
- "uk": "Розширено діагностику температури: додано нові діагностичні стани для всіх датчиків температури (останнє дійсне значення, позначка часу, хвилини з останнього значення, статус джерела). Додано механізм самовідновлення/відновлення для зупинених або відсутніх температурних подій. Помічник температури оновлено до таймерів, сумісних із ioBroker.",
56
- "zh-cn": "温度诊断扩展:为所有温度传感器添加了新的诊断状态(最后一个有效值、时间戳、自最后一个值以来的分钟数、源状态)。添加了针对停滞或丢失温度事件的自我修复/恢复机制。温度助手更新为符合 ioBroker 标准的计时器。"
57
- },
58
- "1.3.22": {
59
- "en": "Improved ORP pH reference synchronization. The ORP helper now updates the pH reference independently from ORP value processing and immediately reflects pH state changes.",
60
- "de": "ORP-pH-Referenz verbessert. Der ORP-Helper aktualisiert die pH-Referenz jetzt unabhängig von der ORP-Wertverarbeitung und übernimmt pH-Änderungen sofort.",
61
- "ru": "Улучшена синхронизация эталонного значения pH ОВП. Помощник по ОВП теперь обновляет эталонное значение pH независимо от обработки значений ОВП и немедленно отражает изменения состояния pH.",
62
- "pt": "Sincronização de referência de pH ORP aprimorada. O auxiliar ORP agora atualiza a referência de pH independentemente do processamento do valor ORP e reflete imediatamente as alterações do estado de pH.",
63
- "nl": "Verbeterde ORP pH-referentiesynchronisatie. De ORP-helper werkt nu de pH-referentie onafhankelijk van de ORP-waardeverwerking bij en weerspiegelt onmiddellijk veranderingen in de pH-toestand.",
64
- "fr": "Synchronisation améliorée de la référence ORP pH. L'assistant ORP met désormais à jour la référence pH indépendamment du traitement de la valeur ORP et reflète immédiatement les changements d'état du pH.",
65
- "it": "Sincronizzazione del riferimento pH ORP migliorata. L'assistente ORP ora aggiorna il riferimento del pH indipendentemente dall'elaborazione del valore ORP e riflette immediatamente i cambiamenti dello stato del pH.",
66
- "es": "Sincronización de referencia de pH ORP mejorada. El asistente de ORP ahora actualiza la referencia de pH independientemente del procesamiento del valor de ORP y refleja inmediatamente los cambios en el estado del pH.",
67
- "pl": "Ulepszona synchronizacja odniesienia pH ORP. Pomocnik ORP aktualizuje teraz wartość odniesienia pH niezależnie od przetwarzania wartości ORP i natychmiast odzwierciedla zmiany stanu pH.",
68
- "uk": "Покращена синхронізація еталонного pH ОВП. Помічник ОВП тепер оновлює еталонний рН незалежно від обробки значення ОВП і негайно відображає зміни стану рН.",
69
- "zh-cn": "改进了 ORP pH 参考同步。 ORP 助手现在独立于 ORP 值处理更新 pH 参考值,并立即反映 pH 状态变化。"
70
70
  }
71
71
  },
72
72
  "titleLang": {
@@ -0,0 +1,413 @@
1
+ 'use strict';
2
+
3
+ const { I18n } = require('@iobroker/adapter-core');
4
+
5
+ const PH_STEP = 0.1;
6
+ const VOLUME_REFERENCE_L = 10000;
7
+
8
+ const chemistryToolsHelper = {
9
+ adapter: null,
10
+
11
+ /**
12
+ * @param {import('iobroker').Adapter} adapter - ioBroker adapter instance
13
+ */
14
+ init(adapter) {
15
+ this.adapter = adapter;
16
+
17
+ void this._subscribeStates();
18
+ void this._prefillCalculatorValues();
19
+
20
+ this.adapter.log.debug('[chemistryToolsHelper] Initialized');
21
+ },
22
+
23
+ async _subscribeStates() {
24
+ const ids = [
25
+ 'general.pool_size',
26
+ 'chemistry.ph.input.current_value',
27
+
28
+ 'chemistry.tools.ph_plus_calculator.05_calculate',
29
+ 'chemistry.tools.ph_plus_calculator.01_pool_volume_l',
30
+ 'chemistry.tools.ph_plus_calculator.02_current_ph',
31
+ 'chemistry.tools.ph_plus_calculator.03_target_ph',
32
+ 'chemistry.tools.ph_plus_calculator.04_grams_per_10000l_01ph',
33
+
34
+ 'chemistry.tools.ph_minus_calculator.05_calculate',
35
+ 'chemistry.tools.ph_minus_calculator.01_pool_volume_l',
36
+ 'chemistry.tools.ph_minus_calculator.02_current_ph',
37
+ 'chemistry.tools.ph_minus_calculator.03_target_ph',
38
+ 'chemistry.tools.ph_minus_calculator.04_grams_per_10000l_01ph',
39
+
40
+ 'chemistry.tools.salt_calculator.04_calculate',
41
+ 'chemistry.tools.salt_calculator.01_pool_volume_l',
42
+ 'chemistry.tools.salt_calculator.02_current_salt_ppm',
43
+ 'chemistry.tools.salt_calculator.03_target_salt_ppm',
44
+ ];
45
+
46
+ for (const id of ids) {
47
+ await this.adapter.subscribeStatesAsync(id);
48
+ }
49
+
50
+ this.adapter.log.debug('[chemistryToolsHelper] Own states subscribed');
51
+ },
52
+
53
+ /**
54
+ * @param {string} id - Changed state ID
55
+ * @param {ioBroker.State | null | undefined} state - Changed state
56
+ * @returns {Promise<void>}
57
+ */
58
+ async handleStateChange(id, state) {
59
+ if (!state) {
60
+ return;
61
+ }
62
+
63
+ try {
64
+ if (id.endsWith('general.pool_size') || id.endsWith('chemistry.ph.input.current_value')) {
65
+ await this._prefillCalculatorValues();
66
+ return;
67
+ }
68
+
69
+ if (
70
+ id.endsWith('chemistry.tools.ph_plus_calculator.05_calculate') &&
71
+ state.ack === false &&
72
+ state.val === true
73
+ ) {
74
+ await this._calculatePhPlus();
75
+ await this._setBool('chemistry.tools.ph_plus_calculator.05_calculate', false);
76
+ return;
77
+ }
78
+
79
+ if (
80
+ id.endsWith('chemistry.tools.ph_minus_calculator.05_calculate') &&
81
+ state.ack === false &&
82
+ state.val === true
83
+ ) {
84
+ await this._calculatePhMinus();
85
+ await this._setBool('chemistry.tools.ph_minus_calculator.05_calculate', false);
86
+ }
87
+
88
+ if (
89
+ id.endsWith('chemistry.tools.salt_calculator.04_calculate') &&
90
+ state.ack === false &&
91
+ state.val === true
92
+ ) {
93
+ await this._calculateSalt();
94
+ await this._setBool('chemistry.tools.salt_calculator.04_calculate', false);
95
+ }
96
+ } catch (err) {
97
+ this.adapter.log.warn(`[chemistryToolsHelper] Error in handleStateChange: ${err.message}`);
98
+ }
99
+ },
100
+
101
+ async _prefillCalculatorValues() {
102
+ const poolVolume = await this._readNumber('general.pool_size');
103
+ const currentPh = await this._readNumber('chemistry.ph.input.current_value');
104
+
105
+ await this._prefillNumberIfEmpty('chemistry.tools.ph_plus_calculator.01_pool_volume_l', poolVolume);
106
+ await this._prefillNumberIfEmpty('chemistry.tools.ph_minus_calculator.01_pool_volume_l', poolVolume);
107
+ await this._prefillNumberIfEmpty('chemistry.tools.salt_calculator.01_pool_volume_l', poolVolume);
108
+
109
+ await this._prefillNumberIfEmpty('chemistry.tools.ph_plus_calculator.02_current_ph', currentPh);
110
+ await this._prefillNumberIfEmpty('chemistry.tools.ph_minus_calculator.02_current_ph', currentPh);
111
+ },
112
+
113
+ /**
114
+ * Calculates the pH Plus amount using the manufacturer dosage formula.
115
+ *
116
+ * @returns {Promise<void>}
117
+ */
118
+ async _calculatePhPlus() {
119
+ const base = 'chemistry.tools.ph_plus_calculator';
120
+
121
+ const poolVolume = await this._readNumber(`${base}.01_pool_volume_l`);
122
+ const currentPh = await this._readNumber(`${base}.02_current_ph`);
123
+ const targetPh = await this._readNumber(`${base}.03_target_ph`);
124
+ const dosageFactor = await this._readNumber(`${base}.04_grams_per_10000l_01ph`);
125
+
126
+ const validation = this._validateInputs({
127
+ type: 'plus',
128
+ poolVolume,
129
+ currentPh,
130
+ targetPh,
131
+ dosageFactor,
132
+ });
133
+
134
+ if (!validation.valid) {
135
+ await this._writeInvalidResult(base, validation.error);
136
+ return;
137
+ }
138
+
139
+ const resultGrams = this._calculateAmountGrams(currentPh, targetPh, poolVolume, dosageFactor);
140
+ const roundedGrams = Math.round(resultGrams);
141
+
142
+ const resultText =
143
+ `${I18n.translate('Calculated amount')}: ${roundedGrams} g pH Plus. ` +
144
+ `${I18n.translate('Reference value based on common manufacturer dosage.')} ` +
145
+ `${I18n.translate('Follow the manufacturer instructions and re-measure afterwards.')}`;
146
+
147
+ await this._writeValidResult(base, roundedGrams, resultText);
148
+ },
149
+
150
+ /**
151
+ * Calculates the pH Minus amount using the manufacturer dosage formula.
152
+ *
153
+ * @returns {Promise<void>}
154
+ */
155
+ async _calculatePhMinus() {
156
+ const base = 'chemistry.tools.ph_minus_calculator';
157
+
158
+ const poolVolume = await this._readNumber(`${base}.01_pool_volume_l`);
159
+ const currentPh = await this._readNumber(`${base}.02_current_ph`);
160
+ const targetPh = await this._readNumber(`${base}.03_target_ph`);
161
+ const dosageFactor = await this._readNumber(`${base}.04_grams_per_10000l_01ph`);
162
+
163
+ const validation = this._validateInputs({
164
+ type: 'minus',
165
+ poolVolume,
166
+ currentPh,
167
+ targetPh,
168
+ dosageFactor,
169
+ });
170
+
171
+ if (!validation.valid) {
172
+ await this._writeInvalidResult(base, validation.error);
173
+ return;
174
+ }
175
+
176
+ const resultGrams = this._calculateAmountGrams(currentPh, targetPh, poolVolume, dosageFactor);
177
+ const roundedGrams = Math.round(resultGrams);
178
+
179
+ const resultText =
180
+ `${I18n.translate('Calculated amount')}: ${roundedGrams} g pH Minus. ` +
181
+ `${I18n.translate('Reference value based on common manufacturer dosage.')} ` +
182
+ `${I18n.translate('Follow the manufacturer instructions and re-measure afterwards.')}`;
183
+
184
+ await this._writeValidResult(base, roundedGrams, resultText);
185
+ },
186
+
187
+ /**
188
+ * Calculates the salt amount needed to raise the salt concentration.
189
+ *
190
+ * @returns {Promise<void>} Resolves when the salt calculation result has been written.
191
+ */
192
+ async _calculateSalt() {
193
+ const base = 'chemistry.tools.salt_calculator';
194
+
195
+ const poolVolume = await this._readNumber(`${base}.01_pool_volume_l`);
196
+ const currentSalt = await this._readNumber(`${base}.02_current_salt_ppm`);
197
+ const targetSalt = await this._readNumber(`${base}.03_target_salt_ppm`);
198
+
199
+ if (!Number.isFinite(poolVolume) || poolVolume <= 0) {
200
+ await this._writeInvalidSaltResult(base, I18n.translate('Pool volume must be greater than 0 liters.'));
201
+ return;
202
+ }
203
+
204
+ if (!Number.isFinite(currentSalt) || currentSalt < 0) {
205
+ await this._writeInvalidSaltResult(
206
+ base,
207
+ I18n.translate('Current salt concentration must be 0 ppm or higher.'),
208
+ );
209
+ return;
210
+ }
211
+
212
+ if (!Number.isFinite(targetSalt) || targetSalt <= currentSalt) {
213
+ await this._writeInvalidSaltResult(
214
+ base,
215
+ I18n.translate('Target salt concentration must be higher than the current salt concentration.'),
216
+ );
217
+ return;
218
+ }
219
+
220
+ const resultKg = ((targetSalt - currentSalt) * poolVolume) / 1000000;
221
+ const roundedKg = Math.round(resultKg * 10) / 10;
222
+
223
+ const resultText =
224
+ `${I18n.translate('Calculated amount')}: ${roundedKg} kg ${I18n.translate('salt')}. ` +
225
+ `${I18n.translate('Reference value for raising the salt concentration in pool water.')} ` +
226
+ `${I18n.translate('Follow the salt system manufacturer instructions and add salt gradually.')}`;
227
+
228
+ await this._writeValidSaltResult(base, roundedKg, resultText);
229
+ },
230
+
231
+ /**
232
+ * @param {object} input - Calculator input values
233
+ * @param {'plus' | 'minus'} input.type - Calculator type
234
+ * @param {number} input.poolVolume - Pool volume in liters
235
+ * @param {number} input.currentPh - Current pH value
236
+ * @param {number} input.targetPh - Target pH value
237
+ * @param {number} input.dosageFactor - Dosage factor in grams per 10,000 l and 0.1 pH
238
+ * @returns {{ valid: boolean, error: string }} Validation result with an optional translated error message.
239
+ */
240
+ _validateInputs(input) {
241
+ if (!Number.isFinite(input.poolVolume) || input.poolVolume <= 0) {
242
+ return { valid: false, error: I18n.translate('Pool volume must be greater than 0 liters.') };
243
+ }
244
+
245
+ if (!Number.isFinite(input.currentPh) || input.currentPh <= 0 || input.currentPh > 14) {
246
+ return { valid: false, error: I18n.translate('Current pH value must be between 0 and 14.') };
247
+ }
248
+
249
+ if (!Number.isFinite(input.targetPh) || input.targetPh <= 0 || input.targetPh > 14) {
250
+ return { valid: false, error: I18n.translate('Target pH value must be between 0 and 14.') };
251
+ }
252
+
253
+ if (!Number.isFinite(input.dosageFactor) || input.dosageFactor <= 0) {
254
+ return { valid: false, error: I18n.translate('Dosage factor must be greater than 0 grams.') };
255
+ }
256
+
257
+ if (input.type === 'plus' && input.targetPh <= input.currentPh) {
258
+ return {
259
+ valid: false,
260
+ error: I18n.translate('For pH Plus, the target pH value must be higher than the current pH value.'),
261
+ };
262
+ }
263
+
264
+ if (input.type === 'minus' && input.targetPh >= input.currentPh) {
265
+ return {
266
+ valid: false,
267
+ error: I18n.translate('For pH Minus, the target pH value must be lower than the current pH value.'),
268
+ };
269
+ }
270
+
271
+ return { valid: true, error: '' };
272
+ },
273
+
274
+ /**
275
+ * @param {number} currentPh - Current pH value
276
+ * @param {number} targetPh - Target pH value
277
+ * @param {number} poolVolume - Pool volume in liters
278
+ * @param {number} dosageFactor - Dosage factor in grams per 10,000 l and 0.1 pH
279
+ * @returns {number} Calculated amount in grams.
280
+ */
281
+ _calculateAmountGrams(currentPh, targetPh, poolVolume, dosageFactor) {
282
+ const phDifference = Math.abs(targetPh - currentPh);
283
+ const phSteps = phDifference / PH_STEP;
284
+ const volumeFactor = poolVolume / VOLUME_REFERENCE_L;
285
+
286
+ return phSteps * volumeFactor * dosageFactor;
287
+ },
288
+
289
+ /**
290
+ * @param {string} base - Base state path of the calculator
291
+ * @param {number} grams - Calculated amount in grams
292
+ * @param {string} resultText - Readable result text
293
+ * @returns {Promise<void>}
294
+ */
295
+ async _writeValidResult(base, grams, resultText) {
296
+ const now = Date.now();
297
+
298
+ await this._setNumber(`${base}.10_result_grams`, grams);
299
+ await this._setString(`${base}.11_result_text`, resultText);
300
+ await this._setBool(`${base}.12_valid`, true);
301
+ await this._setString(`${base}.13_last_error`, '');
302
+ await this._setNumber(`${base}.14_last_calculated_at`, now);
303
+ },
304
+
305
+ /**
306
+ * @param {string} base - Base state path of the calculator
307
+ * @param {string} errorText - Validation error text
308
+ * @returns {Promise<void>}
309
+ */
310
+ async _writeInvalidResult(base, errorText) {
311
+ const now = Date.now();
312
+
313
+ await this._setNumber(`${base}.10_result_grams`, 0);
314
+ await this._setString(`${base}.11_result_text`, errorText);
315
+ await this._setBool(`${base}.12_valid`, false);
316
+ await this._setString(`${base}.13_last_error`, errorText);
317
+ await this._setNumber(`${base}.14_last_calculated_at`, now);
318
+ },
319
+
320
+ /**
321
+ * @param {string} base - Base state path of the salt calculator
322
+ * @param {number} kg - Calculated salt amount in kilograms
323
+ * @param {string} resultText - Readable result text
324
+ * @returns {Promise<void>} Resolves when the valid salt result states have been updated.
325
+ */
326
+ async _writeValidSaltResult(base, kg, resultText) {
327
+ const now = Date.now();
328
+
329
+ await this._setNumber(`${base}.10_result_kg`, kg);
330
+ await this._setString(`${base}.11_result_text`, resultText);
331
+ await this._setBool(`${base}.12_valid`, true);
332
+ await this._setString(`${base}.13_last_error`, '');
333
+ await this._setNumber(`${base}.14_last_calculated_at`, now);
334
+ },
335
+
336
+ /**
337
+ * @param {string} base - Base state path of the salt calculator
338
+ * @param {string} errorText - Validation error text
339
+ * @returns {Promise<void>} Resolves when the invalid salt result states have been updated.
340
+ */
341
+ async _writeInvalidSaltResult(base, errorText) {
342
+ const now = Date.now();
343
+
344
+ await this._setNumber(`${base}.10_result_kg`, 0);
345
+ await this._setString(`${base}.11_result_text`, errorText);
346
+ await this._setBool(`${base}.12_valid`, false);
347
+ await this._setString(`${base}.13_last_error`, errorText);
348
+ await this._setNumber(`${base}.14_last_calculated_at`, now);
349
+ },
350
+
351
+ /**
352
+ * @param {string} id - Target state ID
353
+ * @param {number} newValue - Value to write if target is empty
354
+ * @returns {Promise<void>}
355
+ */
356
+ async _prefillNumberIfEmpty(id, newValue) {
357
+ if (!Number.isFinite(newValue) || newValue <= 0) {
358
+ return;
359
+ }
360
+
361
+ const currentValue = await this._readNumber(id);
362
+
363
+ if (currentValue > 0) {
364
+ return;
365
+ }
366
+
367
+ await this._setNumber(id, newValue);
368
+ },
369
+
370
+ /**
371
+ * @param {string} id - State ID
372
+ * @returns {Promise<number>} Numeric state value, or 0 if the state is missing or invalid.
373
+ */
374
+ async _readNumber(id) {
375
+ const state = await this.adapter.getStateAsync(id);
376
+ const value = Number(state?.val);
377
+ return Number.isFinite(value) ? value : 0;
378
+ },
379
+
380
+ /**
381
+ * @param {string} id - State ID
382
+ * @param {string} value - Value to write
383
+ * @returns {Promise<void>}
384
+ */
385
+ async _setString(id, value) {
386
+ await this.adapter.setStateChangedAsync(id, { val: String(value ?? ''), ack: true });
387
+ },
388
+
389
+ /**
390
+ * @param {string} id - State ID
391
+ * @param {number} value - Value to write
392
+ * @returns {Promise<void>}
393
+ */
394
+ async _setNumber(id, value) {
395
+ const numberValue = Number(value);
396
+ await this.adapter.setStateChangedAsync(id, { val: Number.isFinite(numberValue) ? numberValue : 0, ack: true });
397
+ },
398
+
399
+ /**
400
+ * @param {string} id - State ID
401
+ * @param {boolean} value - Value to write
402
+ * @returns {Promise<void>}
403
+ */
404
+ async _setBool(id, value) {
405
+ await this.adapter.setStateChangedAsync(id, { val: !!value, ack: true });
406
+ },
407
+
408
+ cleanup() {
409
+ this.adapter = null;
410
+ },
411
+ };
412
+
413
+ module.exports = chemistryToolsHelper;
@@ -250,13 +250,24 @@ const pumpHelper = {
250
250
  const power = this._parseNumber((await this.adapter.getStateAsync('pump.current_power'))?.val);
251
251
  const maxWatt = this._parseNumber((await this.adapter.getStateAsync('pump.pump_max_watt'))?.val);
252
252
 
253
- // --- NEU: Kulanzzeiten für Start/Stop ---
254
- const graceOnMs = 5000; // 5 Sekunden nach Start ignorieren
253
+ // NEU: Konfigurierbarer Sicherheits-Timeout für Leistungsprüfung nach Pumpenstart
254
+ const startupPowerCheckTimeoutState = await this.adapter.getStateAsync(
255
+ 'pump.startup_power_check_timeout_sec',
256
+ );
257
+
258
+ const startupPowerCheckTimeoutSec = Math.min(
259
+ 10,
260
+ Math.max(5, this._parseNumber(startupPowerCheckTimeoutState?.val) || 5),
261
+ );
262
+
263
+ const graceOnMs = startupPowerCheckTimeoutSec * 1000;
255
264
  const graceOffMs = 5000; // 5 Sekunden nach Stop ignorieren
256
265
  const now = Date.now();
257
266
 
258
267
  if (active === true && this._lastPumpStart && now - this._lastPumpStart < graceOnMs) {
259
- this.adapter.log.debug('[pumpHelper] Within start grace period – skipping error check');
268
+ this.adapter.log.debug(
269
+ `[pumpHelper] Within start grace period (${startupPowerCheckTimeoutSec}s) – skipping error check`,
270
+ );
260
271
  return;
261
272
  }
262
273
 
package/lib/i18n/de.json CHANGED
@@ -327,5 +327,20 @@
327
327
  "Current ORP value": "Aktueller ORP-Wert",
328
328
  "pH reference": "pH-Referenz",
329
329
  "Status": "Status",
330
- "ORP evaluation is disabled.": "ORP-Auswertung ist deaktiviert."
330
+ "ORP evaluation is disabled.": "ORP-Auswertung ist deaktiviert.",
331
+
332
+ "Calculated amount": "Berechnete Menge",
333
+ "Reference value based on common manufacturer dosage.": "Orientierungswert nach üblicher Herstellerdosierung.",
334
+ "Follow the manufacturer instructions and re-measure afterwards.": "Bitte Herstellerangaben beachten und anschließend erneut messen.",
335
+ "Pool volume must be greater than 0 liters.": "Das Poolvolumen muss größer als 0 Liter sein.",
336
+ "Current pH value must be between 0 and 14.": "Der aktuelle pH-Wert muss zwischen 0 und 14 liegen.",
337
+ "Target pH value must be between 0 and 14.": "Der Ziel-pH-Wert muss zwischen 0 und 14 liegen.",
338
+ "Dosage factor must be greater than 0 grams.": "Der Dosierfaktor muss größer als 0 Gramm sein.",
339
+ "For pH Plus, the target pH value must be higher than the current pH value.": "Für pH Plus muss der Ziel-pH-Wert höher als der aktuelle pH-Wert sein.",
340
+ "For pH Minus, the target pH value must be lower than the current pH value.": "Für pH Minus muss der Ziel-pH-Wert niedriger als der aktuelle pH-Wert sein.",
341
+ "Current salt concentration must be 0 ppm or higher.": "Die aktuelle Salzkonzentration muss 0 ppm oder höher sein.",
342
+ "Target salt concentration must be higher than the current salt concentration.": "Die Ziel-Salzkonzentration muss höher als die aktuelle Salzkonzentration sein.",
343
+ "Reference value for raising the salt concentration in pool water.": "Referenzwert für die Erhöhung der Salzkonzentration im Poolwasser.",
344
+ "Follow the salt system manufacturer instructions and add salt gradually.": "Bitte die Anweisungen des Salzsystem-Herstellers befolgen und das Salz allmählich hinzufügen.",
345
+ "salt": "Salz"
331
346
  }
package/lib/i18n/en.json CHANGED
@@ -351,5 +351,23 @@
351
351
  "pH reference": "pH reference",
352
352
  "Status": "Status",
353
353
  "Recommendation": "Recommendation",
354
- "ORP evaluation is disabled.": "ORP evaluation is disabled."
354
+ "ORP evaluation is disabled.": "ORP evaluation is disabled.",
355
+
356
+ "Calculated amount": "Calculated amount",
357
+ "Reference value based on common manufacturer dosage.": "Reference value based on common manufacturer dosage.",
358
+ "Follow the manufacturer instructions and re-measure afterwards.": "Follow the manufacturer instructions and re-measure afterwards.",
359
+
360
+ "Pool volume must be greater than 0 liters.": "Pool volume must be greater than 0 liters.",
361
+ "Current pH value must be between 0 and 14.": "Current pH value must be between 0 and 14.",
362
+ "Target pH value must be between 0 and 14.": "Target pH value must be between 0 and 14.",
363
+ "Dosage factor must be greater than 0 grams.": "Dosage factor must be greater than 0 grams.",
364
+
365
+ "For pH Plus, the target pH value must be higher than the current pH value.": "For pH Plus, the target pH value must be higher than the current pH value.",
366
+ "For pH Minus, the target pH value must be lower than the current pH value.": "For pH Minus, the target pH value must be lower than the current pH value.",
367
+ "Current salt concentration must be 0 ppm or higher.": "Current salt concentration must be 0 ppm or higher.",
368
+
369
+ "Target salt concentration must be higher than the current salt concentration.": "Target salt concentration must be higher than the current salt concentration.",
370
+ "Reference value for raising the salt concentration in pool water.": "Reference value for raising the salt concentration in pool water.",
371
+ "Follow the salt system manufacturer instructions and add salt gradually.": "Follow the salt system manufacturer instructions and add salt gradually.",
372
+ "salt": "salt"
355
373
  }