iobroker.poolcontrol 1.3.25 → 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.
@@ -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
@@ -13,12 +13,10 @@
13
13
  "Skin or eye irritation when swimming": "Haut- oder Augenreizungen beim Schwimmen",
14
14
  "Water values are often unstable": "Wasserwerte sind häufig instabil",
15
15
  "Issue unclear / not specific": "Problem unklar / nicht eindeutig",
16
-
17
16
  "Important:": "Wichtig:",
18
17
  "Always proceed step by step: measure first → correct in small steps → let the water circulate → measure again.": "Immer schrittweise vorgehen: erst messen → klein korrigieren → umwälzen lassen → erneut messen.",
19
18
  "Always follow the product instructions (concentration and pool volume may differ).": "Immer die Angaben auf dem Produkt beachten (Konzentration und Beckenvolumen können abweichen).",
20
19
  "If you are unsure, correct more slowly rather than forcing it.": "Wenn du unsicher bist: lieber langsamer korrigieren als mit Gewalt.",
21
-
22
20
  "Problem: pH value is too low (water is too acidic).": "Problem: pH-Wert ist zu niedrig (Wasser ist zu sauer).",
23
21
  "What this means:": "Was das bedeutet:",
24
22
  "A pH value that is too low can attack materials (metals / built-in parts) more strongly and can increase irritation during bathing. It can also make the overall water balance unstable.": "Ein zu niedriger pH-Wert kann Materialien (Metalle / Einbauteile) stärker angreifen und Reizungen beim Baden begünstigen. Außerdem kann die Wasserbalance instabil werden.",
@@ -29,7 +27,6 @@
29
27
  "Typical corrective direction:": "Übliche Lösungsrichtung:",
30
28
  "Raise the pH gradually with a pH increaser / pH plus product.": "pH-Wert schrittweise mit pH-Plus anheben.",
31
29
  "If the pH keeps dropping again, also check total alkalinity / the buffer, otherwise every pH correction will only hold for a short time.": "Wenn der pH-Wert immer wieder fällt, auch die Alkalinität prüfen, sonst hält die Korrektur nicht lange.",
32
-
33
30
  "Problem: pH value is too high (water is too alkaline).": "Problem: pH-Wert ist zu hoch (Wasser ist zu basisch).",
34
31
  "If the pH value is too high, chlorine / disinfection often works less effectively and cloudiness / precipitation (for example lime) can occur more easily. Eyes and skin may also become irritated.": "Bei zu hohem pH-Wert wirkt Chlor schlechter und es kann zu Trübungen oder Ausfällungen (z. B. Kalk) kommen. Auch Augen und Haut können gereizt werden.",
35
32
  "Strong outgassing / a lot of bubbling (CO₂ escapes → pH rises)": "Starkes Ausgasen / viel Sprudel (CO₂ entweicht → pH steigt)",
@@ -37,7 +34,6 @@
37
34
  "Some pools / materials can initially push the pH upward": "Einige Becken oder Materialien können den pH-Wert anfangs erhöhen",
38
35
  "Lower the pH in small steps with a pH reducer / pH minus product.": "pH-Wert in kleinen Schritten mit pH-Minus senken.",
39
36
  "If the pH repeatedly rises again, also consider alkalinity / water hardness, because this strongly affects stability.": "Wenn der pH-Wert immer wieder steigt, auch Alkalinität und Wasserhärte prüfen.",
40
-
41
37
  "Problem: Chlorine level is too low.": "Problem: Chlorwert ist zu niedrig.",
42
38
  "Too little effective chlorine can allow germs and algae to multiply more easily. Often the first signs are tired-looking water: less clarity, more odor, and deposits forming faster.": "Zu wenig wirksames Chlor lässt Keime und Algen leichter wachsen. Erste Anzeichen sind oft trübes Wasser, Geruch und schnellerer Belag.",
43
39
  "A lot of sun / UV exposure (chlorine breaks down faster)": "Viel Sonne / UV (Chlor baut sich schneller ab)",
@@ -45,7 +41,6 @@
45
41
  "Unfavorable pH value (chlorine works less effectively)": "Ungünstiger pH-Wert (Chlor wirkt schlechter)",
46
42
  "Bring disinfection back into a normal range and check the pH in parallel.": "Desinfektion wieder in den Normalbereich bringen und pH prüfen.",
47
43
  "If chlorine keeps dropping permanently, also check causes such as organic load, filter condition, and circulation.": "Wenn Chlor dauerhaft sinkt, Ursachen wie Belastung, Filterzustand und Umwälzung prüfen.",
48
-
49
44
  "Problem: Chlorine level is too high.": "Problem: Chlorwert ist zu hoch.",
50
45
  "A chlorine level that is too high can irritate eyes and skin, cause strong odor, and put more stress on materials and covers. In this situation, adding even more chemicals is almost never the right direction.": "Zu viel Chlor kann Augen und Haut reizen, starken Geruch verursachen und Materialien belasten. Mehr Chemie ist hier fast nie sinnvoll.",
51
46
  "Too much chlorine added / shock treatment too high": "Zu viel Chlor zugegeben / Schockbehandlung zu hoch",
@@ -53,7 +48,21 @@
53
48
  "Do not add more chlorine.": "Kein weiteres Chlor zugeben.",
54
49
  "Let time do the work (sun / UV reduces chlorine) and keep the water circulating well.": "Zeit wirken lassen (Sonne / UV baut Chlor ab) und gut umwälzen.",
55
50
  "Only evaluate further corrections (for example pH) once chlorine is back in a normal range.": "Weitere Korrekturen erst vornehmen, wenn Chlor wieder im Normalbereich ist.",
56
-
51
+ "Problem: Chlorine level does not rise despite dosing.": "Problem: Chlorwert steigt trotz Dosierung nicht.",
52
+ "This often happens when the water has a high chlorine demand: the added chlorine is consumed immediately before a stable free chlorine level can remain measurable.": "Das passiert häufig, wenn das Wasser einen hohen Chlorbedarf hat: Zugegebenes Chlor wird sofort verbraucht, bevor ein stabiler freier Chlorwert messbar bleibt.",
53
+ "High organic load (dirt, biofilm, heavy use)": "Hohe organische Belastung (Schmutz, Biofilm, starke Nutzung)",
54
+ "Early / invisible algae growth": "Beginnender oder noch nicht sichtbarer Algenwuchs",
55
+ "Strongly incorrect pH value (effectiveness reduced)": "Deutlich falscher pH-Wert (Wirkung reduziert)",
56
+ "First check the basic values (pH, filter / circulation, visual inspection for deposits).": "Zuerst die Grundwerte prüfen (pH, Filter / Umwälzung, Sichtprüfung auf Beläge).",
57
+ "Get the water clean again (clean / backwash the filter, brush the pool), so chlorine is not only being consumed immediately.": "Das Wasser wieder sauber bekommen (Filter reinigen / rückspülen, Becken bürsten), damit Chlor nicht sofort wieder verbraucht wird.",
58
+ "If no effect can be measured permanently, there is often an oxidative demand present (chlorine is immediately bound / broken down) – then only consistent reduction of the causes and measuring again will help.": "Wenn dauerhaft keine Wirkung messbar ist, liegt oft ein oxidativer Bedarf vor (Chlor wird sofort gebunden / abgebaut). Dann hilft nur, die Ursachen konsequent zu reduzieren und erneut zu messen.",
59
+ "Problem: Strong chlorine smell despite the measured value.": "Problem: Starker Chlorgeruch trotz Messwert.",
60
+ "The typical indoor pool smell often does not come from too much good chlorine, but from bound chlorine compounds (chloramines). These are formed when chlorine combines with nitrogen / organic matter from sweat and similar contamination.": "Der typische Schwimmbadgeruch kommt oft nicht von zu viel wirksamem Chlor, sondern von gebundenen Chlorverbindungen (Chloraminen). Diese entstehen, wenn Chlor mit Stickstoff / organischen Stoffen aus Schweiß und ähnlichen Verunreinigungen reagiert.",
61
+ "High bathing load / organic input": "Hohe Badebelastung / organischer Eintrag",
62
+ "Too little fresh water / breakdown / filter care": "Zu wenig Frischwasser / Abbau / Filterpflege",
63
+ "Poor ventilation (for indoor pools) increases the perceived smell": "Schlechte Lüftung (bei Innenpools) verstärkt den wahrgenommenen Geruch",
64
+ "The goal is to reduce bound contamination: good circulation / filtration, pool cleaning, and possibly a fresh water share.": "Ziel ist, gebundene Verunreinigungen zu reduzieren: gute Umwälzung / Filtration, Beckenreinigung und gegebenenfalls ein Frischwasseranteil.",
65
+ "Also check the pH in parallel, because an incorrect pH further reduces disinfection performance.": "Parallel auch den pH-Wert prüfen, weil ein falscher pH-Wert die Desinfektionsleistung weiter reduziert.",
57
66
  "Problem: Water is green.": "Problem: Wasser ist grün.",
58
67
  "Green water is very often a sign of algae growth (often caused by too little effective chlorine and / or poor circulation).": "Grünes Wasser ist meist ein Zeichen für Algenwachstum (oft durch zu wenig Chlor oder schlechte Umwälzung).",
59
68
  "Free chlorine too low or already consumed": "Freies Chlor zu niedrig oder bereits verbraucht",
@@ -63,10 +72,57 @@
63
72
  "Check the water values (especially pH and chlorine) and improve circulation / filter condition.": "Wasserwerte prüfen (pH und Chlor) und Umwälzung / Filter verbessern.",
64
73
  "Brush / vacuum the pool thoroughly so deposits do not survive.": "Becken gründlich bürsten / absaugen.",
65
74
  "Then let the filter run patiently until the water becomes clear again.": "Dann geduldig filtern lassen, bis das Wasser wieder klar ist.",
66
-
75
+ "Problem: Water is cloudy / gray / milky.": "Problem: Wasser ist trüb / grau / milchig.",
76
+ "Cloudy water is often caused by fine suspended particles or chemical imbalances (for example pH / alkalinity / lime) that the filter cannot handle well.": "Trübes Wasser entsteht oft durch feine Schwebstoffe oder chemische Ungleichgewichte (zum Beispiel pH / Alkalinität / Kalk), die der Filter nicht gut erfassen kann.",
77
+ "Dirty filter / filter runtime too short": "Verschmutzter Filter / zu kurze Filterlaufzeit",
78
+ "pH / alkalinity outside the stable range": "pH / Alkalinität außerhalb des stabilen Bereichs",
79
+ "High water hardness (lime) – especially together with high pH": "Hohe Wasserhärte (Kalk), besonders zusammen mit hohem pH-Wert",
80
+ "Too little disinfection → biological cloudiness is possible": "Zu wenig Desinfektion → biologische Trübung ist möglich",
81
+ "Check the filter condition (cleaning / backwashing) and ensure sufficient circulation.": "Filterzustand prüfen (Reinigung / Rückspülen) und ausreichende Umwälzung sicherstellen.",
82
+ "Check the basic values pH / chlorine and stabilize them.": "Grundwerte pH / Chlor prüfen und stabilisieren.",
83
+ "If the cloudiness looks mineral (milky / white), also consider water hardness / balance.": "Wenn die Trübung mineralisch wirkt (milchig / weiß), auch Wasserhärte und Wasserbalance berücksichtigen.",
84
+ "Problem: Algae visible on walls or floor.": "Problem: Algen sichtbar an Wänden oder Boden.",
85
+ "Visible algae are a clear sign that disinfection / circulation was not sufficient or that local dead zones are forming (corners / pipes).": "Sichtbare Algen sind ein klares Zeichen, dass Desinfektion / Umwälzung nicht ausgereicht haben oder lokale Totzonen entstehen (Ecken / Leitungen).",
86
+ "Chlorine too low or already consumed": "Chlor zu niedrig oder bereits verbraucht",
87
+ "pH too high (chlorine works less effectively)": "pH zu hoch (Chlor wirkt schlechter)",
88
+ "Too little brushing / pool care, poor circulation": "Zu wenig Bürsten / Beckenpflege, schlechte Umwälzung",
89
+ "Mechanics first: brush / vacuum consistently, maintain the filter.": "Zuerst mechanisch arbeiten: konsequent bürsten / absaugen und den Filter pflegen.",
90
+ "Then stabilize the values (pH / chlorine) and improve circulation.": "Danach die Werte stabilisieren (pH / Chlor) und die Umwälzung verbessern.",
91
+ "The goal is to remove algae not only chemically, but also mechanically.": "Ziel ist, Algen nicht nur chemisch, sondern auch mechanisch zu entfernen.",
92
+ "Problem: Foam on the water surface.": "Problem: Schaum auf der Wasseroberfläche.",
93
+ "Foam is often caused by surfactants / organic matter (sunscreen, body care products, cleaning residues), some algaecides / polymers, or also air in the system. It is usually not a chlorine problem, but rather a substances-in-the-water problem.": "Schaum entsteht oft durch Tenside / organische Stoffe (Sonnencreme, Körperpflegeprodukte, Reinigungsreste), manche Algizide / Polymere oder auch Luft im System. Es ist meist kein Chlorproblem, sondern eher ein Problem durch Stoffe im Wasser.",
94
+ "A lot of lotions / oils in the water, high organic load": "Viele Lotionen / Öle im Wasser, hohe organische Belastung",
95
+ "Certain anti-algae products can promote foam": "Bestimmte Anti-Algen-Produkte können Schaum begünstigen",
96
+ "Air entering the system (small leaks) → foam appears stronger": "Lufteintrag im System (kleine Undichtigkeiten) → Schaum wirkt stärker",
97
+ "Skim off the surface, keep the filter clean, and reduce organic input.": "Oberfläche abschöpfen, Filter sauber halten und organischen Eintrag reduzieren.",
98
+ "Keep an eye on pH / chlorine as well (stable values help break down organic matter faster).": "Auch pH / Chlor im Blick behalten (stabile Werte helfen, organische Stoffe schneller abzubauen).",
99
+ "If foam remains permanently, systematically narrow down the source (products / contamination / technology).": "Wenn Schaum dauerhaft bleibt, die Ursache systematisch eingrenzen (Produkte / Verunreinigung / Technik).",
100
+ "Problem: Skin or eye irritation while swimming.": "Problem: Haut- oder Augenreizungen beim Schwimmen.",
101
+ "Irritation can have several causes: pH that is too high or very low, too high a disinfectant concentration, or bound chlorine compounds (chloramines) caused by organic contamination.": "Reizungen können mehrere Ursachen haben: einen zu hohen oder sehr niedrigen pH-Wert, eine zu hohe Desinfektionsmittelkonzentration oder gebundene Chlorverbindungen (Chloramine) durch organische Belastung.",
102
+ "pH outside the comfortable range": "pH außerhalb des angenehmen Bereichs",
103
+ "Chlorine clearly too high": "Chlor deutlich zu hoch",
104
+ "Chlorine smell / chloramines caused by strong organic input": "Chlorgeruch / Chloramine durch starken organischen Eintrag",
105
+ "Measure first (pH and chlorine) and bring the values back into a normal range.": "Zuerst messen (pH und Chlor) und die Werte wieder in einen normalen Bereich bringen.",
106
+ "Keep the pool / filter clean so that organic contamination does not turn into bound compounds.": "Becken / Filter sauber halten, damit organische Verunreinigungen nicht zu gebundenen Verbindungen werden.",
107
+ "If irritation is strong, pause swimming until the water values are stable.": "Bei starken Reizungen das Baden pausieren, bis die Wasserwerte stabil sind.",
108
+ "Problem: Water values are often unstable (change quickly).": "Problem: Wasserwerte sind häufig instabil (ändern sich schnell).",
109
+ "If pH / chlorine fluctuate strongly all the time, the water often lacks stability: the buffer (alkalinity) is too low / too high, the load changes frequently, or circulation / filtration is not constant enough.": "Wenn pH / Chlor ständig stark schwanken, fehlt dem Wasser oft Stabilität: Der Puffer (Alkalinität) ist zu niedrig / zu hoch, die Belastung ändert sich häufig oder Umwälzung / Filtration sind nicht konstant genug.",
110
+ "Total alkalinity too low → pH jumps / tips over quickly": "Gesamtalkalinität zu niedrig → pH springt / kippt schnell",
111
+ "Many small corrections without enough circulation time": "Viele kleine Korrekturen ohne ausreichende Umwälzzeit",
112
+ "Poor filter condition / filter times too short": "Schlechter Filterzustand / zu kurze Filterzeiten",
113
+ "Strong external influences (rain, heat, heavy use)": "Starke äußere Einflüsse (Regen, Hitze, intensive Nutzung)",
114
+ "First get the basics stable (buffer / filter / regularity), then only fine-tune.": "Zuerst die Grundlagen stabil bekommen (Puffer / Filter / Regelmäßigkeit), danach nur noch fein nachjustieren.",
115
+ "Always correct in small steps and allow enough time for mixing.": "Immer in kleinen Schritten korrigieren und genug Zeit zum Durchmischen lassen.",
116
+ "Problem: It is unclear what exactly is wrong.": "Problem: Es ist unklar, was genau nicht stimmt.",
117
+ "Procedure (minimal and sensible):": "Vorgehen (minimal und sinnvoll):",
118
+ "Visual check: Green? Cloudy? Deposits? Foam?": "Sichtprüfung: Grün? Trüb? Beläge? Schaum?",
119
+ "Two measured values are enough for the start: pH + free chlorine.": "Für den Anfang reichen zwei Messwerte: pH + freies Chlor.",
120
+ "Check the technology: Is circulation running? Is the filter clean? Is backwashing needed?": "Technik prüfen: Läuft die Umwälzung? Ist der Filter sauber? Ist Rückspülen nötig?",
121
+ "Typical rule of thumb:": "Typische Faustregel:",
122
+ "Visual problems (green / cloudy) are very often a combination of disinfection + pH + filtration.": "Sichtbare Probleme (grün / trüb) sind sehr oft eine Kombination aus Desinfektion + pH + Filtration.",
123
+ "Odor / irritation is often not too much chlorine, but incorrectly bound / organically loaded water.": "Geruch / Reizungen bedeuten oft nicht zu viel Chlor, sondern falsch gebundenes oder organisch belastetes Wasser.",
67
124
  "Note: This problem is not known yet.": "Hinweis: Dieses Problem ist noch nicht bekannt.",
68
125
  "Please select one of the existing items or use \"Issue unclear / not specific\".": "Bitte einen vorhandenen Punkt auswählen oder \"Problem unklar\" nutzen.",
69
-
70
126
  "Extended solar configuration invalid": "Konfiguration der erweiterten Solarsteuerung ungültig",
71
127
  "Solar Extended configuration invalid": "Konfiguration von Solar Extended ungültig",
72
128
  "Priority blocked by controlHelper": "Durch controlHelper blockiert",
@@ -102,7 +158,6 @@
102
158
  "extended blocked by actor write error": "Extended durch Aktor-Schreibfehler blockiert",
103
159
  "extended active": "Extended aktiv",
104
160
  "extended inactive": "Extended inaktiv",
105
-
106
161
  "solar_insights_summary_note_block_7": "Block 7 schreibt direkte Referenzwerte, geschätzte thermische Leistung, geschätzten Tagesertrag, geschätztes Effizienzverhältnis, die Nutzung des Rücklaufsensors sowie wetterbasierte Bewertungskennzeichen.",
107
162
  "solar_insights_label_mode": "Modus",
108
163
  "solar_insights_label_solar_ran_today": "Solar lief heute",
@@ -141,7 +196,6 @@
141
196
  "solar_insights_debug_block_7_updated": "Solar-Insights Block 7 aktualisiert. Verwendete Sensoren:",
142
197
  "solar_insights_debug_block_7_error": "Solar-Insights Block 7 Fehler:",
143
198
  "solar_insights_html_note_block_7": "Geschätzter Tagesertrag, geschätztes Effizienzverhältnis, Nutzung des Rücklaufsensors und wetterbasierte Bewertungskennzeichen sind aktiv.",
144
-
145
199
  "solar_log_value_unknown": "unbekannt",
146
200
  "solar_log_confidence_text": "Die aktuelle Vertrauensstufe dieser Bewertung liegt bei etwa %s Prozent.",
147
201
  "solar_log_text_no_runtime_today": "Solar ist heute nicht gelaufen, daher kann kein sinnvoller Solarertrag für den Tag angegeben werden.",
@@ -169,7 +223,6 @@
169
223
  "solar_log_text_weather_supported": "Für diese Bewertung ist eine wetterbasierte Plausibilisierung aktiv.",
170
224
  "solar_log_text_small_gain_detail": "Der aktuell geschätzte Tagesertrag liegt bei etwa %s Wh, das entspricht etwa %s kWh.",
171
225
  "solar_log_text_quality_level": "Die aktuelle Qualitätsstufe der Bewertung ist %s.",
172
-
173
226
  "photovoltaic_insights_calculation_mode_daily": "Tägliche PV-Überschussanalyse",
174
227
  "photovoltaic_insights_price_source_adapter_config": "Adapter-Konfiguration",
175
228
  "photovoltaic_insights_calculation_note_block_2": "Der Berechnungsblock bereitet die Quelleninformationen für die Photovoltaik-Insights-Analyse vor.",
@@ -188,7 +241,6 @@
188
241
  "photovoltaic_insights_debug_text_no_pv_runtime": "Aktuell wird keine PV-Laufzeit gezählt, weil entweder kein PV-Überschuss aktiv ist oder der Photovoltaik-Helper die Pumpe nicht besitzt.",
189
242
  "photovoltaic_insights_debug_reason_daily_reset": "Täglicher Reset",
190
243
  "photovoltaic_insights_debug_text_daily_reset": "Die täglichen Photovoltaik-Insights-Werte wurden zurückgesetzt.",
191
-
192
244
  "pH source state configured.": "pH-Quell-Datenpunkt konfiguriert.",
193
245
  "pH source state could not be subscribed.": "pH-Quell-Datenpunkt konnte nicht abonniert werden.",
194
246
  "No pH source state configured.": "Kein pH-Quell-Datenpunkt konfiguriert.",
@@ -219,12 +271,10 @@
219
271
  "pH mixing run finished. Pump was already running and was not switched off by the pH helper.": "pH-Mischlauf beendet. Die Pumpe lief bereits und wurde vom pH-Helper nicht ausgeschaltet.",
220
272
  "pH trend": "pH-Trend",
221
273
  "Overall pH status": "Gesamtstatus pH",
222
-
223
274
  "pH is rising very quickly. Check dosing, alkalinity and water balance.": "Der pH-Wert steigt sehr schnell an. Prüfe Dosierung, Alkalinität und Wasserbalance.",
224
275
  "pH is rising noticeably. Observe the trend and check alkalinity and water balance.": "Der pH-Wert steigt merklich an. Beobachte den Trend und prüfe Alkalinität und Wasserbalance.",
225
276
  "pH is slowly rising. Continue observing the trend.": "Der pH-Wert steigt langsam an. Beobachte den Trend weiter.",
226
277
  "pH is falling. This can be plausible after pH correction or fresh water.": "Der pH-Wert fällt. Das kann nach einer pH-Korrektur oder Frischwasserzugabe plausibel sein.",
227
-
228
278
  "Not enough pH history is available yet. Collect more valid measurements.": "Es sind noch nicht genügend pH-Historienwerte vorhanden. Sammle weitere gültige Messwerte.",
229
279
  "No TDS source state configured.": "Kein TDS-Quell-Datenpunkt konfiguriert.",
230
280
  "TDS source state configured.": "TDS-Quell-Datenpunkt konfiguriert.",
@@ -247,7 +297,7 @@
247
297
  "Not enough TDS history is available yet. Collect more valid measurements.": "Es sind noch nicht genügend TDS-Verlaufsdaten vorhanden. Weitere gültige Messwerte sammeln.",
248
298
  "TDS is falling. This can be plausible after fresh water or a partial water change.": "TDS sinkt. Das kann nach Frischwasser oder einem Teilwasserwechsel plausibel sein.",
249
299
  "Current TDS value": "Aktueller TDS-Wert",
250
- "not enough data": "nicht genug Daten",
300
+ "not enough data": "nicht genügend Daten",
251
301
  "Initial reference": "Initialer Referenzwert",
252
302
  "not set": "nicht gesetzt",
253
303
  "Delta since reference": "Differenz seit Referenzwert",
@@ -255,7 +305,6 @@
255
305
  "Trend status": "Trendstatus",
256
306
  "Overall status": "Gesamtstatus",
257
307
  "Recommendation": "Empfehlung",
258
-
259
308
  "No ORP source state configured.": "Kein ORP-Quelldatenpunkt konfiguriert.",
260
309
  "ORP source state configured.": "ORP-Quelldatenpunkt konfiguriert.",
261
310
  "ORP source state could not be subscribed.": "ORP-Quelldatenpunkt konnte nicht abonniert werden.",
@@ -275,10 +324,23 @@
275
324
  "ORP value is high. Check whether the measurement is plausible and evaluate the water values together.": "ORP-Wert hoch. Bitte prüfen, ob die Messung plausibel ist und die Wasserwerte gemeinsam bewerten.",
276
325
  "ORP value is within the configured reference range.": "ORP-Wert befindet sich im konfigurierten Referenzbereich.",
277
326
  "unknown": "unbekannt",
278
- "not enough data": "nicht genügend Daten",
279
327
  "Current ORP value": "Aktueller ORP-Wert",
280
328
  "pH reference": "pH-Referenz",
281
329
  "Status": "Status",
282
- "Recommendation": "Empfehlung",
283
- "ORP evaluation is disabled.": "ORP-Auswertung ist deaktiviert."
284
- }
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"
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
  }