iobroker.poolcontrol 1.3.26 → 1.3.27

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,16 @@ New features are added regularly – please refer to the changelog.
252
263
  ---
253
264
 
254
265
  ## Changelog
266
+ ### 1.3.27 (2026-05-30)
267
+
268
+ - Added new Chemistry Tools section
269
+ - Added pH Plus Calculator
270
+ - Added pH Minus Calculator
271
+ - Added Salt Calculator
272
+ - Added validation, result texts and error handling for all calculators
273
+ - Added automatic pool volume prefill support
274
+ - Expanded documentation and function overviews
275
+
255
276
  ### 1.3.26 (2026-05-28)
256
277
 
257
278
  - Reworked multiple warnings and review notes from official ioBroker checks
@@ -282,13 +303,6 @@ New features are added regularly – please refer to the changelog.
282
303
  - Switched temperature helper timers to ioBroker adapter timers
283
304
  - Improved visibility and troubleshooting for missing or delayed temperature updates
284
305
 
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
306
  ## Archived Release History
293
307
 
294
308
  For older releases and archived version history see:
package/io-package.json CHANGED
@@ -1,8 +1,21 @@
1
1
  {
2
2
  "common": {
3
3
  "name": "poolcontrol",
4
- "version": "1.3.26",
4
+ "version": "1.3.27",
5
5
  "news": {
6
+ "1.3.27": {
7
+ "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.",
8
+ "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.",
9
+ "ru": "Добавлены новые химические инструменты с калькулятором pH плюс, калькулятором pH минус и калькулятором соли. Добавлена ​​проверка, тексты результатов, обработка ошибок и автоматическое предварительное заполнение объема пула. Расширенная документация и обзоры функций.",
10
+ "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.",
11
+ "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.",
12
+ "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.",
13
+ "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.",
14
+ "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.",
15
+ "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.",
16
+ "uk": "Додано нові хімічні інструменти з калькулятором pH Plus, калькулятором pH мінус і калькулятором солі. Додано перевірку, тексти результатів, обробку помилок і автоматичне попереднє заповнення обсягу пулу. Розширена документація та огляд функцій.",
17
+ "zh-cn": "添加了新的化学工具,包括 pH 加计算器、pH 减计算器和盐计算器。添加了验证、结果文本、错误处理和自动池容量预填充。扩展的文档和功能概述。"
18
+ },
6
19
  "1.3.26": {
7
20
  "en": "Reworked several warnings and review notes from official ioBroker checks. Various small improvements and internal cleanups.",
8
21
  "de": "Mehrere Warnungen und Review-Hinweise aus offiziellen ioBroker-Checks überarbeitet. Diverse kleine Verbesserungen und interne Bereinigungen.",
@@ -54,19 +67,6 @@
54
67
  "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
68
  "uk": "Розширено діагностику температури: додано нові діагностичні стани для всіх датчиків температури (останнє дійсне значення, позначка часу, хвилини з останнього значення, статус джерела). Додано механізм самовідновлення/відновлення для зупинених або відсутніх температурних подій. Помічник температури оновлено до таймерів, сумісних із ioBroker.",
56
69
  "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;
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
  }
package/lib/i18n/es.json CHANGED
@@ -347,5 +347,23 @@
347
347
  "Current ORP value": "Valor actual de ORP",
348
348
  "pH reference": "Referencia de pH",
349
349
  "Status": "Estado",
350
- "ORP evaluation is disabled.": "La evaluación de ORP está desactivada."
350
+ "ORP evaluation is disabled.": "La evaluación de ORP está desactivada.",
351
+
352
+ "Calculated amount": "Cantidad calculada",
353
+ "Reference value based on common manufacturer dosage.": "Valor de referencia basado en la dosificación habitual del fabricante.",
354
+ "Follow the manufacturer instructions and re-measure afterwards.": "Siga las instrucciones del fabricante y vuelva a medir después.",
355
+
356
+ "Pool volume must be greater than 0 liters.": "El volumen de la piscina debe ser superior a 0 litros.",
357
+ "Current pH value must be between 0 and 14.": "El valor actual de pH debe estar entre 0 y 14.",
358
+ "Target pH value must be between 0 and 14.": "El valor objetivo de pH debe estar entre 0 y 14.",
359
+ "Dosage factor must be greater than 0 grams.": "El factor de dosificación debe ser superior a 0 gramos.",
360
+
361
+ "For pH Plus, the target pH value must be higher than the current pH value.": "Para pH Plus, el valor objetivo de pH debe ser superior al valor actual de pH.",
362
+ "For pH Minus, the target pH value must be lower than the current pH value.": "Para pH Minus, el valor objetivo de pH debe ser inferior al valor actual de pH.",
363
+ "Current salt concentration must be 0 ppm or higher.": "La concentración actual de sal debe ser de 0 ppm o superior.",
364
+
365
+ "Target salt concentration must be higher than the current salt concentration.": "La concentración objetivo de sal debe ser superior a la concentración actual de sal.",
366
+ "Reference value for raising the salt concentration in pool water.": "Valor de referencia para aumentar la concentración de sal en el agua de la piscina.",
367
+ "Follow the salt system manufacturer instructions and add salt gradually.": "Siga las instrucciones del fabricante del sistema de sal y añada sal gradualmente.",
368
+ "salt": "sal"
351
369
  }
package/lib/i18n/fr.json CHANGED
@@ -347,5 +347,23 @@
347
347
  "Current ORP value": "Valeur ORP actuelle",
348
348
  "pH reference": "Référence pH",
349
349
  "Status": "État",
350
- "ORP evaluation is disabled.": "L'évaluation ORP est désactivée."
350
+ "ORP evaluation is disabled.": "L'évaluation ORP est désactivée.",
351
+
352
+ "Calculated amount": "Quantité calculée",
353
+ "Reference value based on common manufacturer dosage.": "Valeur de référence basée sur le dosage habituel du fabricant.",
354
+ "Follow the manufacturer instructions and re-measure afterwards.": "Veuillez suivre les instructions du fabricant et mesurer à nouveau ensuite.",
355
+
356
+ "Pool volume must be greater than 0 liters.": "Le volume de la piscine doit être supérieur à 0 litre.",
357
+ "Current pH value must be between 0 and 14.": "La valeur actuelle du pH doit être comprise entre 0 et 14.",
358
+ "Target pH value must be between 0 and 14.": "La valeur cible du pH doit être comprise entre 0 et 14.",
359
+ "Dosage factor must be greater than 0 grams.": "Le facteur de dosage doit être supérieur à 0 gramme.",
360
+
361
+ "For pH Plus, the target pH value must be higher than the current pH value.": "Pour pH Plus, la valeur cible du pH doit être supérieure à la valeur actuelle du pH.",
362
+ "For pH Minus, the target pH value must be lower than the current pH value.": "Pour pH Minus, la valeur cible du pH doit être inférieure à la valeur actuelle du pH.",
363
+ "Current salt concentration must be 0 ppm or higher.": "La concentration actuelle en sel doit être de 0 ppm ou plus.",
364
+
365
+ "Target salt concentration must be higher than the current salt concentration.": "La concentration cible en sel doit être supérieure à la concentration actuelle en sel.",
366
+ "Reference value for raising the salt concentration in pool water.": "Valeur de référence pour augmenter la concentration en sel dans l’eau de la piscine.",
367
+ "Follow the salt system manufacturer instructions and add salt gradually.": "Respectez les instructions du fabricant du système au sel et ajoutez le sel progressivement.",
368
+ "salt": "sel"
351
369
  }
package/lib/i18n/it.json CHANGED
@@ -347,5 +347,23 @@
347
347
  "Current ORP value": "Valore ORP attuale",
348
348
  "pH reference": "Riferimento pH",
349
349
  "Status": "Stato",
350
- "ORP evaluation is disabled.": "La valutazione ORP è disattivata."
350
+ "ORP evaluation is disabled.": "La valutazione ORP è disattivata.",
351
+
352
+ "Calculated amount": "Quantità calcolata",
353
+ "Reference value based on common manufacturer dosage.": "Valore di riferimento basato sul dosaggio standard del produttore.",
354
+ "Follow the manufacturer instructions and re-measure afterwards.": "Seguire le istruzioni del produttore ed effettuare una nuova misurazione successivamente.",
355
+
356
+ "Pool volume must be greater than 0 liters.": "Il volume della piscina deve essere maggiore di 0 litri.",
357
+ "Current pH value must be between 0 and 14.": "Il valore attuale del pH deve essere compreso tra 0 e 14.",
358
+ "Target pH value must be between 0 and 14.": "Il valore target del pH deve essere compreso tra 0 e 14.",
359
+ "Dosage factor must be greater than 0 grams.": "Il fattore di dosaggio deve essere maggiore di 0 grammi.",
360
+
361
+ "For pH Plus, the target pH value must be higher than the current pH value.": "Per pH Plus, il valore target del pH deve essere superiore al valore attuale del pH.",
362
+ "For pH Minus, the target pH value must be lower than the current pH value.": "Per pH Minus, il valore target del pH deve essere inferiore al valore attuale del pH.",
363
+ "Current salt concentration must be 0 ppm or higher.": "La concentrazione attuale di sale deve essere pari o superiore a 0 ppm.",
364
+
365
+ "Target salt concentration must be higher than the current salt concentration.": "La concentrazione target di sale deve essere superiore alla concentrazione attuale di sale.",
366
+ "Reference value for raising the salt concentration in pool water.": "Valore di riferimento per aumentare la concentrazione di sale nell’acqua della piscina.",
367
+ "Follow the salt system manufacturer instructions and add salt gradually.": "Seguire le istruzioni del produttore del sistema a sale e aggiungere il sale gradualmente.",
368
+ "salt": "sale"
351
369
  }