iobroker.sprinklecontrol 0.2.13 → 0.2.15

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.
Files changed (71) hide show
  1. package/{Lizenz → LICENSE} +21 -21
  2. package/README.md +62 -110
  3. package/admin/i18n/de/translations.json +134 -0
  4. package/admin/i18n/en/translations.json +134 -0
  5. package/admin/i18n/es/translations.json +134 -0
  6. package/admin/i18n/fr/translations.json +134 -0
  7. package/admin/i18n/it/translations.json +134 -0
  8. package/admin/i18n/nl/translations.json +134 -0
  9. package/admin/i18n/pl/translations.json +134 -0
  10. package/admin/i18n/pt/translations.json +134 -0
  11. package/admin/i18n/ru/translations.json +134 -0
  12. package/admin/i18n/uk/translations.json +134 -0
  13. package/admin/i18n/zh-cn/translations.json +134 -0
  14. package/admin/index_m.html +904 -907
  15. package/admin/index_m.js +779 -779
  16. package/admin/style.css +453 -158
  17. package/admin/words.js +143 -136
  18. package/io-package.json +268 -201
  19. package/lib/adapter-config.d.ts +19 -16
  20. package/lib/evaporation.js +455 -412
  21. package/lib/myConfig.js +372 -359
  22. package/lib/sendMessageText.js +162 -199
  23. package/lib/tools.js +148 -125
  24. package/lib/valveControl.js +931 -887
  25. package/main.js +2045 -1828
  26. package/package.json +50 -32
  27. package/.gitattributes +0 -2
  28. package/admin/admin.d.ts +0 -1
  29. package/docs/de/img/E-Mail.jpg +0 -0
  30. package/docs/de/img/Extraeinstellungen.jpg +0 -0
  31. package/docs/de/img/Pumpeneinstellung.jpg +0 -0
  32. package/docs/de/img/Pushover.jpg +0 -0
  33. package/docs/de/img/Select_ID.jpg +0 -0
  34. package/docs/de/img/Telegram.jpg +0 -0
  35. package/docs/de/img/Ventil-Haupteinstellung.jpg +0 -0
  36. package/docs/de/img/Ventil-Pumpeneinstellung.jpg +0 -0
  37. package/docs/de/img/WhatsApp.jpg +0 -0
  38. package/docs/de/img/Zeiteinstellung.jpg +0 -0
  39. package/docs/de/img/addTime.jpg +0 -0
  40. package/docs/de/img/analog.jpg +0 -0
  41. package/docs/de/img/ber-verdunstung.jpg +0 -0
  42. package/docs/de/img/bew-einstellung.jpg +0 -0
  43. package/docs/de/img/bew-feste-tage.jpg +0 -0
  44. package/docs/de/img/bistabil.jpg +0 -0
  45. package/docs/de/img/bodenf-analog.jpg +0 -0
  46. package/docs/de/img/bodenf-bistabil.jpg +0 -0
  47. package/docs/de/img/calculation.jpg +0 -0
  48. package/docs/de/img/control.jpg +0 -0
  49. package/docs/de/img/einschaltpunkt-giessen.jpg +0 -0
  50. package/docs/de/img/festeTage.jpg +0 -0
  51. package/docs/de/img/main.jpg +0 -0
  52. package/docs/de/img/main_tab.jpg +0 -0
  53. package/docs/de/img/max-bodenfeuchtigkeit.jpg +0 -0
  54. package/docs/de/img/schaltverhalten.jpg +0 -0
  55. package/docs/de/img/sprinklecontrol.png +0 -0
  56. package/docs/de/img/verdunstDiagra.jpg +0 -0
  57. package/docs/de/img/verdunstung.jpg +0 -0
  58. package/docs/de/img/zus-bew-einstellung.jpg +0 -0
  59. package/docs/de/sprinklecontrol.md +0 -656
  60. package/docs/en/img/schaltverhalten.jpg +0 -0
  61. package/docs/en/img/screenshot1.jpg +0 -0
  62. package/docs/en/img/screenshot2.jpg +0 -0
  63. package/docs/en/img/screenshot3.jpg +0 -0
  64. package/docs/en/img/screenshot4.jpg +0 -0
  65. package/docs/en/img/screenshot5.jpg +0 -0
  66. package/docs/en/img/screenshot6.jpg +0 -0
  67. package/docs/en/img/screenshot7.jpg +0 -0
  68. package/docs/en/img/screenshot8.jpg +0 -0
  69. package/docs/en/img/sprinklecontrol.png +0 -0
  70. package/docs/en/sprinklecontrol.md +0 -230
  71. package/main.test.js +0 -30
@@ -1,888 +1,932 @@
1
- 'use strict';
2
-
3
- const myConfig = require('./myConfig.js');
4
- const addTime = require('./tools.js').addTime;
5
- const formatTime = require('./tools').formatTime;
6
- const sendMessageText = require('./sendMessageText.js'); // sendMessageText
7
-
8
- /**
9
- * The adapter instance
10
- * @type {ioBroker.Adapter}
11
- */
12
- let adapter;
13
-
14
- /**
15
- * Thread-list
16
- * → Auflistung aller aktiver Sprenger-Kreise
17
- * @type {array}
18
- */
19
- const threadList = [];
20
-
21
- /** @type {boolean} */
22
- let boostReady = true,
23
- /** @type {boolean} */
24
- boostOn = false,
25
- /** @type {any | undefined} */
26
- boostListTimer,
27
- /**
28
- * maximal zulässige Anzahl der eingeschalteten Ventile
29
- * @type {number} */
30
- maxParallel,
31
- /* Control of the cistern pump */
32
- /** Füllstand der Zisterne
33
- * @type {number}*/
34
- fillLevelCistern = 0;
35
-
36
- const currentPumpUse = {
37
- /**
38
- * Pumpen aktive?
39
- * @type {boolean} */
40
- enable: false,
41
- /**
42
- * Zisterne aktive?
43
- * @type {boolean} */
44
- pumpCistern: false,
45
- /**
46
- * Pumpen-Bezeichnung; z.B. "hm-rpc.0.MEQ1810129.1.STATE"
47
- * @type {string} */
48
- pumpName: '',
49
- /**
50
- * Pumpenleistung in l/h
51
- * @type {number} */
52
- pumpPower: 0
53
- };
54
-
55
-
56
- /*==============================================================================================================================================*/
57
- /* interne Funktionen */
58
- /*----------------------------------------------------------------------------------------------------------------------------------------------*/
59
-
60
- /**
61
- * Sprinkle (sprinkleName) delete
62
- * Ventil (sprinkleName) löschen
63
- * @param {array.<{sprinkleName: string}>} killList
64
- */
65
- function delList (killList) {
66
- for(const sprinkleName of killList) {
67
- let bValveFound = false; // Ventil gefunden
68
- for(let counter = 0, // Loop über das Array
69
- lastArray = (threadList.length - 1); // entsprechend der Anzahl der Eintragungen
70
- counter <= lastArray;
71
- counter++) {
72
- const entry = threadList[counter].sprinkleName;
73
- if ((sprinkleName === entry) || bValveFound) {
74
- if (sprinkleName === entry) bValveFound = true;
75
- if (counter !== lastArray) threadList[counter] = threadList[counter + 1];
76
- }
77
- }
78
- /* If a valve is found, delete the last array (entry). Wenn Ventil gefunden letzten Array (Auftrag) löschen */
79
- if (bValveFound) {
80
- threadList.pop();
81
- adapter.log.debug(`delList=> order deleted ID: ${sprinkleName} ( rest orders: ${threadList.length} )`);
82
- }
83
- }
84
-
85
- } // End delList
86
-
87
- /*----------------------------------------------------------------------------------------------------------------------------------------------*/
88
-
89
- /**
90
- * Activation of the booster (all other active valves are deactivated for the duration of the boost so that the sprinkler can be extended with the maximum possible pressure)
91
- * => aktivierung des Boosters (alle anderen aktiven Ventile werden für die Zeit des Boosts deaktiviert um den maximalen möglichen Druck zum Ausfahren der Sprenger zu ermöglichen)
92
- * @param {number} sprinkleID
93
- */
94
- function boostList (sprinkleID) {
95
- boostReady = false;
96
- boostOn = true;
97
- for(const entry of threadList) {
98
- if (entry.enabled) {
99
- if (entry.sprinkleID === sprinkleID) { // Booster
100
- /* Zustand des Ventils im Thread < 0 > off, < 1 > wait, < 2 > on, < 3 > break, <<< 4 >>> Boost(on), < 5 > off(Boost) */
101
- adapter.setState('sprinkle.' + entry.sprinkleName + '.sprinklerState', {
102
- val: 4,
103
- ack: true
104
- });
105
- //valveState(entry, 'Boost(on)');
106
- entry.times.boostTime2 = setTimeout(() => {
107
- /* Zustand des Ventils im Thread < 0 > off, < 1 > wait, <<< 2 >>> on, < 3 > break, < 4 > Boost(on), < 5 > off(Boost) */
108
- adapter.setState('sprinkle.' + entry.sprinkleName + '.sprinklerState', {
109
- val: 2,
110
- ack: true
111
- });
112
- entry.times.boostTime2 = null;
113
- },31000);
114
- } else { // rest der Ventile
115
- // in die Zwangspause (myBreak = true)
116
- entry.times.boostTime1 = setTimeout(() => {
117
- /* Zustand des Ventils im Thread < 0 > off, < 1 > wait, < 2 > on, < 3 > break, < 4 > Boost(on), <<< 5 >>> off(Boost) */
118
- adapter.setState('sprinkle.' + entry.sprinkleName + '.sprinklerState', {
119
- val: 5,
120
- ack: true
121
- });
122
- entry.myBreak = true;
123
- // valveOnOff(entry, false, '#2.1 Set: off(Boost), ID: ');
124
- entry.times.boostTime1 = null;
125
- },250);
126
- // aus der Zwangspause holen (myBreak = false)
127
- entry.times.boostTime2 = setTimeout(() => {
128
- /* Zustand des Ventils im Thread < 0 > off, < 1 > wait, <<< 2 >>> on, < 3 > break, < 4 > Boost(on), < 5 > off(Boost) */
129
- adapter.setState('sprinkle.' + entry.sprinkleName + '.sprinklerState', {
130
- val: 2,
131
- ack: true
132
- });
133
- // valveOnOff(entry, true, '#2.2 Set: on, ID: ');
134
- entry.myBreak = false;
135
- entry.times.boostTime2 = null;
136
- },31000);
137
- }
138
- }
139
- }
140
- boostListTimer = setTimeout(() => {
141
- boostOn = false;
142
- updateList();
143
- },32000);
144
- } // End boostList
145
-
146
- /*----------------------------------------------------------------------------------------------------------------------------------------------*/
147
-
148
- /**
149
- * If boostOn is ended by entering "runningTime = 0", normal operation should be restored. (Delete timer)
150
- * → Wenn boostOn über die Eingabe "runningTime = 0" beendet wird, so soll zum Normalen ablauf wieder zurückgekehrt werden. (Löschen der Timer)
151
- * @param {number} sprinkleID
152
- */
153
- function boostKill (sprinkleID) {
154
- for(const entry of threadList) {
155
- if (entry.enabled) {
156
- if (entry.sprinkleID === sprinkleID) {
157
- /* booster wird gekillt */
158
- boostOn = false;
159
- if(entry.times.boostTime2) {
160
- clearTimeout(entry.times.boostTime2);
161
- entry.times.boostTime2 = null;
162
- }
163
- } else {
164
- /* normaler weiterbetrieb für den Rest */
165
- if (entry.times.boostTime1) {
166
- clearTimeout(entry.times.boostTime1);
167
- entry.times.boostTime1 = null;
168
- adapter.log.debug(`boostKill => ID: ${entry.sprinkleName} => boostTime2 (Ende) gelöscht)`);
169
- }
170
- if (entry.times.boostTime2) {
171
- clearTimeout(entry.times.boostTime2);
172
- entry.times.boostTime2 = null;
173
- /* Zustand des Ventils im Thread < 0 > off, < 1 > wait, <<< 2 >>> on, < 3 > break, < 4 > Boost(on), < 5 > off(Boost) */
174
- adapter.setState('sprinkle.' + entry.sprinkleName + '.sprinklerState', {
175
- val: 2,
176
- ack: true
177
- });
178
- // valveOnOff(entry, true, '#2.3 Set: on, ID: ');
179
- }
180
- }
181
- }
182
- }
183
- } // End boostKill
184
-
185
- /*----------------------------------------------------------------------------------------------------------------------------------------------*/
186
-
187
- /**
188
- * Schaltintervall der Ventile, Schaltabstand ist in der Config hinterlegt
189
- * @returns {Promise<unknown>}
190
- */
191
- const valveDelay = () => {
192
- return new Promise (
193
- resolve => setTimeout (resolve, parseInt(adapter.config.switchingDistance))
194
- );
195
- };
196
- /**
197
- * Ausschalten der Ventile mit Schaltabstand
198
- * @param {array} threadList Auflistung aller aktiver Sprenger-Kreise
199
- * @param {number} parallel aktuelle Anzahl der eingeschalteten Ventile
200
- * @returns {Promise<void>}
201
- */
202
- const switchTheValvesOffOn = async (threadList, parallel) => {
203
- /**Sammlung von.sprinkleName die am Ende von updateList gelöscht werden
204
- * @type {array} - */
205
- const killList = [];
206
- for (const entry of threadList) { // ausschalten der Ventile
207
- if ((!entry.enabled // Ventile ausgeschaltet z.B. Intervall-Beregnung
208
- || entry.enabled && entry.myBreak // || in Pause z.B. Boost
209
- || entry.killSprinkle) // || Bewässerung erledigt
210
- && entry.enabled !== entry.enabledState // && Ventil nicht aktuell
211
- ) {
212
- adapter.setForeignState(entry.idState, {
213
- val: false,
214
- ack: false
215
- }, (err) => {
216
- if (err) {
217
- return err;
218
- } else {
219
- adapter.log.info(`Set (${myConfig.config[entry.sprinkleID].methodControlSM}) ID: ${entry.sprinkleName}, value: ${entry.enabled}`);
220
- }
221
- });
222
- entry.enabledState = entry.enabled;
223
- /* Ventil aus threadList löschen → Aufgabe beendet und sind nicht in der Pause */
224
- if (entry.killSprinkle) {
225
- killList.push(entry.sprinkleName);
226
- }
227
- await valveDelay ();
228
- }
229
- }
230
- if (currentPumpUse.pumpName !== '') {
231
- setPumpOnOff(parallel > 0);
232
- await valveDelay ();
233
- }
234
- if (adapter.config.triggerControlVoltage) {
235
- setVoltageOnOff(parallel>0);
236
- await valveDelay ();
237
- }
238
-
239
-
240
- for (const entry of threadList) { // einschalten der Ventile
241
- if (entry.enabled // intern eingeschaltet
242
- && !entry.myBreak // && keine Pause
243
- && !entry.killSprinkle // Bewässerung noch nicht erledigt
244
- && entry.enabled !== entry.enabledState // && Ventil nicht aktuell
245
- ) {
246
- adapter.setForeignState(entry.idState, {
247
- val: true,
248
- ack: false
249
- }, (err) => {
250
- if (err) {
251
- return err;
252
- } else {
253
- adapter.log.info(`Set Valve (${myConfig.config[entry.sprinkleID].methodControlSM}) ID: ${entry.sprinkleName}, value: ${entry.enabled}, duration: ${addTime(entry.wateringTime,'')}`);
254
- }
255
- });
256
- entry.enabledState = entry.enabled;
257
- await valveDelay ();
258
- }
259
- }
260
-
261
- delList(killList); // erledigte Bewässerungsaufgaben aus der threadList löschen
262
- };
263
-
264
- /*----------------------------------------------------------------------------------------------------------------------------------------------*/
265
-
266
- /**
267
- * Control of the active irrigation circuits so that the maximum pump capacity (l / h) is achieved and the maximum number of irrigation circuits is not exceeded.
268
- * => Steuerung der aktiven Bewässerungskreise, damit die maximale Pumpenkapazität (l / h) erreicht wird und die maximale Anzahl der Bewässerungskreise nicht überschritten wird.
269
- */
270
- function updateList () {
271
- /* während des Boost eines Kreises ist ein zuschalten von Sprengern nicht möglich */
272
- if (boostOn) {return;}
273
-
274
- /**
275
- * aktuelle Rest-Pumpenleistung
276
- * @type {number}
277
- */
278
- let curFlow = currentPumpUse.pumpPower, /* adapter.config.triggerMainPumpPower; */
279
- /**
280
- * aktuelle Anzahl der eingeschalteten Ventile
281
- * @type {number}
282
- */
283
- parallel = 0;
284
-
285
- /**
286
- * Sortierfunktion mySortDescending absteigende Sortierung
287
- * @param a
288
- * @param b
289
- * @returns {number}
290
- */
291
- function mySortDescending(a, b) {
292
- return a.pipeFlow > b.pipeFlow ? -1 :
293
- a.pipeFlow < b.pipeFlow ? 1 :
294
- 0;
295
- }
296
- /**
297
- * Sortierfunktion mySortAscending aufsteigende Sortierung
298
- * @param a
299
- * @param b
300
- * @returns {number}
301
- */
302
- function mySortAscending(a, b) {
303
- return a.pipeFlow < b.pipeFlow ? -1 :
304
- a.pipeFlow > b.pipeFlow ? 1 :
305
- 0;
306
- }
307
- /**
308
- * Handling von Ventilen, Zeiten, Verbrauchsmengen im 1s Takt
309
- * @param {object} entry
310
- */
311
- function countSprinkleTime(entry) {
312
- /* --- function beenden wenn ---*/
313
- if (boostOn && !(myConfig.config[entry.sprinkleID].booster) // boost-On && kein aktuelles Boost-Ventil
314
- ) {
315
- return;
316
- }
317
- entry.count ++;
318
- if ((entry.count < entry.wateringTime) // Zeit noch nicht abgelaufen?
319
- && (!entry.calcOn // Vergleich nur bei Berechnung der Verdunstung
320
- || !entry.autoOn // Vergleich nur bei Automatik
321
- || (myConfig.config[entry.sprinkleID].soilMoisture.val < myConfig.config[entry.sprinkleID].soilMoisture.maxIrrigation)) // Bodenfeuchte noch nicht erreicht? (z.B. beim Regen)
322
- ) { /* Zeit läuft */
323
- adapter.setState('sprinkle.' + entry.sprinkleName + '.countdown', {
324
- val: addTime(entry.wateringTime - entry.count, ''),
325
- ack: true
326
- });
327
- /* Alle 15s die Bodenfeuchte anpassen */
328
- if (entry.calcOn // Vergleich nur bei Berechnung der Verdunstung
329
- && !(entry.count % 15) // alle 15s ausführen
330
- ) {
331
- myConfig.addSoilMoistVal(entry.sprinkleID, entry.soilMoisture15s);
332
- }
333
- /* Intervall-Beregnung wenn angegeben (onOffTime > 0) */
334
- if ((entry.onOffTime > 0) && !(entry.count % entry.onOffTime)) {
335
- adapter.log.info(`Intervall-Beregnung, onOffTime: ${entry.onOffTime}, count: ${entry.count}, count % onOffTime: ${entry.count % entry.onOffTime}`);
336
- entry.enabled = false;
337
- entry.myBreak = true;
338
- /* Zustand des Ventils im Thread < 0 > off, < 1 > wait, < 2 > on, <<< 3 >>> break, < 4 > Boost(on), < 5 > off(Boost) */
339
- adapter.setState('sprinkle.' + entry.sprinkleName + '.sprinklerState', {
340
- val: 3,
341
- ack: true
342
- });
343
- updateList();
344
- clearInterval(entry.countdown);
345
- entry.onOffTimeoutOff = setTimeout(()=>{
346
- entry.myBreak = false;
347
- /* Zustand des Ventils im Thread < 0 > off, <<< 1 >>> wait, < 2 > on, < 3 > break, < 4 > Boost(on), < 5 > off(Boost) */
348
- adapter.setState('sprinkle.' + entry.sprinkleName + '.sprinklerState', {
349
- val: 1,
350
- ack: true
351
- });
352
- updateList();
353
- },1000 * (entry.onOffTime < 600 ? 600 : entry.onOffTime)); // 600 sek Pause (10 min)
354
- }
355
- } else { /* zeit abgelaufen => Ventil ausschalten */
356
- /* Zustand des Ventils im Thread <<< 0 >>> off, < 1 > wait, < 2 > on, < 3 > break, < 4 > Boost(on), < 5 > off(Boost) */
357
- entry.enabled = false;
358
- adapter.setState('sprinkle.' + entry.sprinkleName + '.sprinklerState', {
359
- val: 0,
360
- ack: true
361
- });
362
- adapter.setState('sprinkle.' + entry.sprinkleName + '.runningTime', {
363
- val: '0',
364
- ack: true
365
- });
366
- adapter.setState('sprinkle.' + entry.sprinkleName + '.countdown', {
367
- val: '0',
368
- ack: true
369
- });
370
-
371
- /* Wenn in der Konfiguration Bodenfeuchte = 100 % gesetzt ist und Auto-Bewässerung aktive, dann Bodenfeuchte = 100 % setzen*/
372
- if (entry.autoOn && entry.calcOn && myConfig.config[entry.sprinkleID].endIrrigation) {
373
- myConfig.setSoilMoistPct100(entry.sprinkleID);
374
- }
375
- /* Verbrauchswerte in der Historie aktualisieren */
376
- addConsumedAndTime(entry);
377
- /* Booster zurücksetzen */
378
- if (myConfig.config[entry.sprinkleID].booster) {
379
- if (boostOn) {boostKill(entry.sprinkleID);}
380
- boostReady = true;
381
- adapter.log.debug(`ID: ${entry.sprinkleName} UpdateList Sprinkle Off: boostReady = ${boostReady}`);
382
- }
383
- /* Zeiten löschen */
384
- clearInterval(entry.countdown);
385
- /*clearTimeout(entry.onOffTimeoutOn);*/
386
- clearTimeout(entry.onOffTimeoutOff);
387
- /* Ventil aus threadList löschen → Aufgabe beendet */
388
- //delList(entry.sprinkleName);
389
- entry.killSprinkle = true;
390
- updateList();
391
- }
392
- }
393
-
394
- // ermitteln von curPipe und der anzahl der parallelen Stränge
395
- for(const entry of threadList){
396
- if (entry.enabled && !entry.killSprinkle) {
397
- curFlow -= entry.pipeFlow; // // ermitteln der RestFörderkapazität
398
- parallel ++; // Anzahl der Bewässerungsstellen um 1 erhöhen
399
- }
400
- }
401
-
402
- if (curFlow < 0) {
403
- /* - wenn beim Umschalten der Pumpen die Förderleistung zu gering → Ventile deaktivieren - */
404
- // aufsteigend sortieren nach der Verbrauchsmenge
405
- threadList.sort(mySortAscending);
406
-
407
- for(const entry of threadList) {
408
- if (entry.enabled // eingeschaltet
409
- && !entry.killSprinkle // && Aufgabe noch nicht erledigt
410
- && (curFlow < 0)
411
- ) { // && Förderleistung der Pumpe zu gering
412
- entry.enabled = false; // ausgeschaltet merken
413
- clearInterval(entry.countdown); // Zähler für Countdown, Verbrauchsmengen, usw. löschen
414
- curFlow += entry.pipeFlow; // ermitteln der RestFörderkapazität
415
- parallel--; // Anzahl der Bewässerungsstellen um 1 verringern
416
- /* Zustand des Ventils im Thread < 0 > off, <<< 1 >>> wait, < 2 > on, < 3 > break, < 4 > Boost(on), < 5 > off(Boost) */
417
- adapter.setState('sprinkle.' + entry.sprinkleName + '.sprinklerState', {
418
- val: 1,
419
- ack: true
420
- });
421
- // valveOnOff(entry, false, '#2.6 Set: wait, ID: ');
422
- adapter.log.info(`Set Valve ID: ${entry.sprinkleName} Pump delivery rate too low, wait! curFlow ${curFlow} parallel: ${parallel}`);
423
- }
424
- }
425
- }
426
-
427
- // absteigend sortieren nach der Verbrauchsmenge
428
- threadList.sort(mySortDescending);
429
-
430
- // einschalten der Bewässerungsventile nach Verbrauchsmenge und maximaler Anzahl
431
- for(const entry of threadList) {
432
- if (!entry.enabled // ausgeschaltet
433
- && !entry.killSprinkle // && Aufgabe noch nicht erledigt
434
- && !entry.myBreak // && nicht in der Pause
435
- && (curFlow >= entry.pipeFlow) // && noch genügend Förderleistung der Pumpe
436
- && (parallel < maxParallel) // && maxParallel noch nicht erreicht
437
- && ((boostReady) || !(myConfig.config[entry.sprinkleID].booster)) // nur einer mit boostFunction darf aktive sein
438
- ) {
439
- entry.enabled = true; // einschalten merken
440
- if (myConfig.config[entry.sprinkleID].booster) {
441
- boostReady = false;
442
- adapter.log.debug(`ID: ${entry.sprinkleName}UpdateList sprinkle On: boostReady = ${boostReady}`);
443
- setTimeout(() => {
444
- boostList(entry.sprinkleID);
445
- }, 50);
446
- }
447
- curFlow -= entry.pipeFlow; // ermitteln der RestFörderkapazität
448
- parallel++; // Anzahl der Bewässerungsstellen um 1 erhöhen
449
- /* Zustand des Ventils im Thread < 0 > off, < 1 > wait, <<< 2 >>> on, < 3 > break, < 4 > Boost(on), < 5 > off(Boost) */
450
- adapter.setState('sprinkle.' + entry.sprinkleName + '.sprinklerState', {
451
- val: 2,
452
- ack: true
453
- });
454
- // valveOnOff(entry, true, '#2.7 Set: on, ID: ');
455
- /* countdown starten */
456
- if (!entry.startTime) {
457
- entry.startTime = new Date();
458
- }
459
- entry.countdown = setInterval(() => {
460
- countSprinkleTime(entry);
461
- }, 1000); // 1000 = 1s
462
-
463
- }
464
- }
465
-
466
- adapter.setState('control.parallelOfMax', {
467
- val: parallel + ' : ' + maxParallel,
468
- ack: true
469
- });
470
- adapter.setState('control.restFlow', {
471
- val: '' + curFlow + ' (' + currentPumpUse.pumpPower + ' ' + (currentPumpUse.pumpCistern ? 'Zisterne' : 'Grundwasser') + ')',
472
- ack: true
473
- });
474
-
475
- switchTheValvesOffOn(threadList, parallel).then(err => {
476
- if (err) {
477
- adapter.log.error('Error - Set (false) err: ' + err);
478
- sendMessageText.sendMessage('Error - Set (fase) err: ' + err);
479
- }
480
- });
481
- } // End updateList
482
-
483
- /* --------------------------------------------------------------------------------------------------------------------------------------------------------------------- */
484
-
485
- /**
486
- * +++++ Set the current pump for irrigation +++++
487
- * Festlegen der aktuellen Pumpe zur Bewässerung
488
- */
489
- function setActualPump () {
490
- if (adapter.config.cisternSettings === true) {
491
- /* Zisternen-Bewässerung Einstellung in der config (2. Pumpe) aktiviert */
492
- if (currentPumpUse.enable === true) {
493
- /* Bewässerungspumpen aktiv */
494
- if ((fillLevelCistern < parseFloat(adapter.config.triggerMinCisternLevel)) && (currentPumpUse.pumpCistern === true)) {
495
- /* (Zisterne unter Minimum) && (ZisternenPumpe läuft) */
496
- adapter.setForeignState(currentPumpUse.pumpName, {
497
- val: false,
498
- ack: false
499
- }); // Pumpe Zisterne Aus
500
- currentPumpUse.pumpCistern = false;
501
- currentPumpUse.pumpName = adapter.config.triggerMainPump || '';
502
- currentPumpUse.pumpPower = parseInt(adapter.config.triggerMainPumpPower);
503
- adapter.setForeignState(currentPumpUse.pumpName, {
504
- val: true,
505
- ack: false
506
- }); // Hauptpumpe Ein
507
- adapter.log.info('Pump change (cistern empty) Cistern pump off => main pump on');
508
- updateList(); // Wasserverbrauch an Pumpenleistung anpassen
509
- }
510
- if (fillLevelCistern < parseFloat(adapter.config.triggerMinCisternLevel)) {
511
- adapter.setState('info.cisternState', {
512
- val: 'Cistern empty: ' + fillLevelCistern + ' % (' + adapter.config.triggerMinCisternLevel + ' %)',
513
- ack: true
514
- });
515
- } else {
516
- adapter.setState('info.cisternState', {
517
- val: 'Cistern filled: ' + fillLevelCistern + ' % (' + adapter.config.triggerMinCisternLevel + ' %)',
518
- ack: true
519
- });
520
- }
521
- } else {
522
- /* Bewässerungspumpen inaktiv */
523
- if ((fillLevelCistern > parseFloat(adapter.config.triggerMinCisternLevel)) && (adapter.config.triggerCisternPump) && (adapter.config.triggerCisternPumpPower)) {
524
- /* Zisterne voll && triggerCisternPump && triggerCisternPumpPower vorhanden*/
525
- adapter.setState('info.cisternState', {
526
- val: 'Cistern filled: ' + fillLevelCistern + ' % (' + adapter.config.triggerMinCisternLevel + ' %)',
527
- ack: true
528
- });
529
- currentPumpUse.pumpCistern = true;
530
- currentPumpUse.pumpName = adapter.config.triggerCisternPump || '';
531
- currentPumpUse.pumpPower = parseInt(adapter.config.triggerCisternPumpPower);
532
- adapter.setState('control.restFlow', {
533
- val: currentPumpUse.pumpPower + ' (' + currentPumpUse.pumpPower + ' Zisterne)',
534
- ack: true
535
- });
536
- } else {
537
- adapter.setState('info.cisternState', {
538
- val: 'Cistern empty: ' + fillLevelCistern + ' % (' + adapter.config.triggerMinCisternLevel + ' %)',
539
- ack: true
540
- });
541
- currentPumpUse.pumpCistern = false;
542
- currentPumpUse.pumpName = adapter.config.triggerMainPump || '';
543
- currentPumpUse.pumpPower = parseInt(adapter.config.triggerMainPumpPower);
544
- adapter.setState('control.restFlow', {
545
- val: currentPumpUse.pumpPower + ' (' + currentPumpUse.pumpPower + ' Grundwasser)',
546
- ack: true
547
- });
548
- }
549
- }
550
- } else {
551
- /* Pumpe AUS => Zisternen-Bewässerung nicht aktiviert */
552
- if (adapter.config.triggerCisternPump) {
553
- adapter.setState('info.cisternState', {
554
- val: 'Cistern settings are not active!' + ((fillLevelCistern > 0)?(' level sensor: ' + fillLevelCistern + '%' + ((adapter.config.triggerMinCisternLevel !== '')?(' (' + adapter.config.triggerMinCisternLevel + '%)'):(''))):('')),
555
- ack: true
556
- });
557
- }
558
- }
559
- } // End setActualPump
560
-
561
- /**
562
- * Switching the pump on or off
563
- * => Ein bzw. ausschaltern der Pumpe
564
- * @param {boolean} pumpOnOff ; Pumpe on = true
565
- */
566
- function setPumpOnOff(pumpOnOff) {
567
- if (currentPumpUse.pumpName !== '') {
568
- adapter.getForeignState(currentPumpUse.pumpName, (err, state) => {
569
- if (state) {
570
- if (pumpOnOff) {
571
- if (state.val === false) {
572
- adapter.setForeignState(currentPumpUse.pumpName, {
573
- val: true,
574
- ack: false
575
- });
576
- currentPumpUse.enable = true;
577
- adapter.log.info('Set (pump) on');
578
- }
579
- } else {
580
- if (state.val !== false) {
581
- adapter.setForeignState(currentPumpUse.pumpName, {
582
- val: false,
583
- ack: false
584
- });
585
- currentPumpUse.enable = false;
586
- adapter.log.info('Set (pump) off');
587
- }
588
- }
589
- } else if (err) {
590
- adapter.log.error(`triggerMainPump ${currentPumpUse.pumpName} is not available (ist nicht erreichbar): ${err}`);
591
- }
592
- });
593
- }
594
- } // End setPumpOnOff
595
-
596
- /**
597
- * Switching the control voltage on or off
598
- * => Ein bzw. ausschaltern der Steuerspannung
599
- * @param {boolean} voltageOnOff - Voltage on = true
600
- */
601
- function setVoltageOnOff(voltageOnOff) {
602
- if (adapter.config.triggerControlVoltage !== '') {
603
- adapter.getForeignState(adapter.config.triggerControlVoltage, (err, state) => {
604
- if (state) {
605
- if (voltageOnOff) {
606
- if (state.val === false) {
607
- adapter.setForeignState(adapter.config.triggerControlVoltage, {
608
- val: true,
609
- ack: false
610
- });
611
- adapter.log.info('Set (voltage) on');
612
- }
613
- } else {
614
- if (state.val !== false) {
615
- adapter.setForeignState(adapter.config.triggerControlVoltage, {
616
- val: false ,
617
- ack: false
618
- });
619
- adapter.log.info('Set (voltage) off');
620
- }
621
- }
622
- } else if (err) {
623
- adapter.log.error('triggerControlVoltage is not available (ist nicht erreichbar): ' + err);
624
- }
625
- });
626
- }
627
- }
628
-
629
- /**
630
- * Adding the consumption data to the history
631
- * => Hinzufügen der Verbrauchsdaten zur History
632
- * @param entry - array mit den Daten des aktiven Ventils
633
- */
634
- function addConsumedAndTime(entry) {
635
- adapter.setState('sprinkle.' + entry.sprinkleName + '.history.lastConsumed', {
636
- val: Math.round(entry.litersPerSecond * entry.count),
637
- ack: true
638
- });
639
- adapter.setState('sprinkle.' + entry.sprinkleName + '.history.lastRunningTime', {
640
- val: addTime(entry.count, ''),
641
- ack: true
642
- });
643
- adapter.setState('sprinkle.' + entry.sprinkleName + '.history.lastOn', {
644
- val: formatTime(adapter, entry.startTime, 'dd.mm. hh:mm'),
645
- ack: true
646
- });
647
- adapter.getState('sprinkle.' + entry.sprinkleName + '.history.curCalWeekConsumed', (err, state) => {
648
- if (state) {
649
- adapter.setState('sprinkle.' + entry.sprinkleName + '.history.curCalWeekConsumed', {
650
- val: (state.val) + Math.round(entry.litersPerSecond * entry.count),
651
- ack: true
652
- });
653
- }
654
- });
655
- adapter.getState('sprinkle.' + entry.sprinkleName + '.history.curCalWeekRunningTime', (err, state) => {
656
- if (state) {
657
- adapter.setState('sprinkle.' + entry.sprinkleName + '.history.curCalWeekRunningTime', {
658
- val: addTime(state.val, entry.count),
659
- ack: true
660
- });
661
- }
662
- });
663
- } // End addConsumedAndTime
664
-
665
-
666
- /*++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++*/
667
- /* externe Funktionen */
668
- /*++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++*/
669
-
670
- /**
671
- * -- externe Funktionen -> initValveControl - addList - clearEntireList - setFillLevelCistern --
672
- * @type {{clearEntireList(): void, initValveControl(ioBroker.Adapter): void, setFillLevelCistern(number): void, addList(Array<{auto: Boolean, sprinkleID: Number, wateringTime: Number}>): void}}
673
- */
674
- const valveControl = {
675
- /**
676
- * Initialize the start configuration of ventilControl
677
- * => Initialisieren Sie die Startkonfiguration von ventilControl
678
- * @param {ioBroker.Adapter} myAdapter
679
- */
680
- initValveControl (myAdapter) {
681
- adapter = adapter || myAdapter;
682
- currentPumpUse.pumpCistern = false;
683
- currentPumpUse.pumpName = adapter.config.triggerMainPump || '';
684
- currentPumpUse.pumpPower = parseInt(adapter.config.triggerMainPumpPower) || 0;
685
- maxParallel = parseInt(adapter.config.maximumParallelValves);
686
- /* Objekt control.restFlow befüllen */
687
- adapter.setState('control.restFlow', {
688
- val: currentPumpUse.pumpPower + ' (' + currentPumpUse.pumpPower + ' ' + (currentPumpUse.pumpCistern ? 'Zisterne' : 'Grundwasser') + ')',
689
- ack: true
690
- });
691
- /* Objekt control.parallelOfMax befüllen */
692
- adapter.setState('control.parallelOfMax', {
693
- val: 0 + ' : ' + adapter.config.maximumParallelValves,
694
- ack: true
695
- });
696
- /* Pumpe ausschalter, wenn vorhanden */
697
- if (adapter.config.triggerMainPump !== '') {
698
- adapter.getState('adapter.config.triggerMainPump', (err, state) => {
699
- if (state) {
700
- adapter.setState(adapter.config.triggerMainPump, {
701
- val: false,
702
- ack: false
703
- });
704
- }
705
- });
706
- }
707
- /* Pumpe (Zisterne) ausschalter, wenn vorhanden */
708
- if (adapter.config.triggerCisternPump !== '') {
709
- adapter.getState('adapter.config.triggerCisternPump', (err, state) => {
710
- if (state) {
711
- adapter.setState(adapter.config.triggerCisternPump, {
712
- val: false,
713
- ack: false
714
- });
715
- }
716
- });
717
- }
718
- /* alle Ventile (.name = "hm-rpc.0.MEQ1234567.3.STATE") in einem definierten Zustand (false) versetzen*/
719
- const result = adapter.config.events;
720
- if (result) {
721
- for (const res of result) {
722
- adapter.getState(res.name, (err, state) => {
723
- if (state) {
724
- adapter.setState(res.name, {
725
- val: false,
726
- ack: false
727
- });
728
- }
729
- });
730
- }
731
- }
732
- }, // End initValveControl
733
-
734
- /**
735
- * Add Sprinkle
736
- * → Sprinkle hinzufügen
737
- * - auto Automatik == (true), Handbetrieb == (false)
738
- * - sprinkleID → zugriff auf myConfig.config[sprinkleID]. xyz
739
- * - wateringTime → Bewässerungszeit in min
740
- * @param {Array.<{auto: Boolean, sprinkleID: Number, wateringTime: Number}>} sprinkleList
741
- */
742
- addList (sprinkleList) {
743
- //
744
- for (const res of sprinkleList) {
745
- const sprinkleName = myConfig.config[res.sprinkleID].objectName;
746
- /**
747
- * add done
748
- * → hinzufügen erledigt (Sprenger bereits aktive)
749
- * @type {boolean}
750
- */
751
- let addDone = false;
752
- // schauen ob der Sprenger schon in der threadList ist
753
- if (threadList) {
754
- for (const entry of threadList) {
755
- if (entry.sprinkleID === res.sprinkleID) {
756
- if (entry.wateringTime === res.wateringTime) {
757
- // adapter.setState('sprinkle.' + sprinkleName + '.runningTime', {val: addTime(wateringTime, ''), ack: false});
758
- return;
759
- }
760
- entry.wateringTime = res.wateringTime;
761
- entry.autoOn = res.auto; // auto: = true autostart; = false Handbetrieb
762
- adapter.setState('sprinkle.' + sprinkleName + '.runningTime', {
763
- val: addTime(res.wateringTime, ''),
764
- ack: true
765
- });
766
- addDone = true; // Sprinkle found
767
- adapter.log.debug(`update ID: ${entry.sprinkleName} new time: ${addTime(res.wateringTime, '')}`);
768
- break;
769
- }
770
- }
771
- }
772
-
773
- if (!addDone) {
774
- if (res.wateringTime <= 0) {
775
- adapter.setState('sprinkle.' + sprinkleName + '.runningTime', {
776
- val: '0',
777
- ack: true
778
- });
779
- return;
780
- }
781
- const newThread = {};
782
- /** @type {number} */ newThread.sprinkleID = res.sprinkleID; // Array[0...]
783
- /** @type {string} */ newThread.sprinkleName = sprinkleName; // z.B "Blumenbeet"
784
- /** @type {string} */ newThread.idState = myConfig.config[res.sprinkleID].idState; // z.B. "hm-rpc.0.MEQ1810129.1.STATE"
785
- /** @type {number} */ newThread.wateringTime = res.wateringTime; // Bewässerungszeit
786
- /** @type {number} */ newThread.pipeFlow = myConfig.config[res.sprinkleID].pipeFlow; // Wasserverbrauch
787
- /** @type {number} */ newThread.count = 0; // Zähler im Sekundentakt
788
- /** @type {boolean} */ newThread.calcOn = (myConfig.config[res.sprinkleID].methodControlSM === 'calculation'); // Anpassung der Bodenfeuchte true/false
789
- /** @type {boolean} */ newThread.enabled = false; // Ventil softwaremäßig ein
790
- /** @type {boolean} */ newThread.enabledState = false; // Ventil hardwaremäßig ein
791
- /** @type {boolean} */ newThread.myBreak = false; // meine Pause
792
- /** @type {boolean} */ newThread.killSprinkle = false; // Löschauftrag ausführen am Ende in threadList
793
- /** @type {number} */ newThread.litersPerSecond = myConfig.config[res.sprinkleID].pipeFlow / 3600; // Wasserverbrauchsmenge pro Sekunde
794
- /** @type {number} */ newThread.onOffTime = myConfig.config[res.sprinkleID].wateringInterval;
795
- /** @type {boolean} */ newThread.autoOn = res.auto;
796
- /** @type {number} */ newThread.soilMoisture15s = 15 * (myConfig.config[res.sprinkleID].soilMoisture.maxIrrigation - myConfig.config[res.sprinkleID].soilMoisture.triggersIrrigation)
797
- / (60 * myConfig.config[res.sprinkleID].wateringTime);
798
- /** @type {any} */ newThread.times = []; // hinterlegen der verschiedenen Zeiten von timeout für gezieltes späteres löschen
799
- /** @type {any} */ newThread.times.boostTime1 = null; // boost start
800
- /** @type {any} */ newThread.times.boostTime2 = null; // boost ende
801
- /** @type {number} */ newThread.id = threadList.length || 0;
802
- threadList.push(newThread);
803
- /* Zustand des Ventils im Thread < 0 > off, <<< 1 >>> wait, < 2 > on, < 3 > break, < 4 > Boost(on), < 5 > off(Boost) */
804
- adapter.setState('sprinkle.' + sprinkleName + '.sprinklerState', {
805
- val: 1,
806
- ack: true
807
- });
808
- adapter.setState('sprinkle.' + sprinkleName + '.runningTime', {
809
- val: addTime(res.wateringTime, ''),
810
- ack: true
811
- });
812
- adapter.log.debug(`ID: ${sprinkleName} new order created: ${JSON.stringify(threadList[newThread.id])}`);
813
- }
814
- }
815
- updateList();
816
- }, // End addList
817
-
818
- /**
819
- * switch off all devices, when close the adapter
820
- * => Beim Beenden des adapters alles ausschalten
821
- */
822
- clearEntireList () {
823
- setVoltageOnOff(false);
824
- setPumpOnOff(false);
825
- if (boostListTimer) {
826
- clearTimeout(boostListTimer);
827
- }
828
- // let bValveFound = false; // Ventil gefunden
829
- for (let counter = threadList.length - 1; // Loop über das Array
830
- counter >= 0;
831
- counter--) {
832
- const entry = threadList[counter];
833
- if (entry.enabledState) {
834
- adapter.log.info(`Set Valve (SprinkleControl: off) ID: ${entry.sprinkleName}, value: false`);
835
- adapter.setForeignState(entry.idState, {
836
- val: false,
837
- ack: true
838
- });
839
- }
840
- /* Zustand des Ventils im Thread <<< 0 >>> off, < 1 > wait, < 2 > on, < 3 > break, < 4 > Boost(on), < 5 > off(Boost) */
841
- adapter.setState('sprinkle.' + entry.sprinkleName + '.sprinklerState', {
842
- val: 0,
843
- ack: true
844
- });
845
- adapter.setState('sprinkle.' + entry.sprinkleName + '.runningTime', {
846
- val: '0',
847
- ack: true
848
- });
849
- adapter.setState('sprinkle.' + entry.sprinkleName + '.countdown', {
850
- val: '0',
851
- ack: true
852
- });
853
- // valveOnOff(myEntry, false, '#2.0 Set: off, ID: ');
854
- /* Verbrauchswerte in der Historie aktualisieren */
855
- addConsumedAndTime(entry);
856
- /* del timer countdown */
857
- clearInterval(entry.countdown);
858
- /* del timer onOffTimeoutOff */
859
- clearTimeout(entry.onOffTimeoutOff);
860
- /* del timer newThread.times.boostTime1 */
861
- if (entry.times.boostTime1) {
862
- clearTimeout(entry.times.boostTime1);
863
- entry.times.boostTime1 = null;
864
- }
865
- /* del timer newThread.times.boostTime2 */
866
- if (entry.times.boostTime2) {
867
- clearTimeout(entry.times.boostTime2);
868
- entry.times.boostTime2 = null;
869
- }
870
- adapter.log.debug(`order deleted Stop all ID: ${entry.sprinkleName} ( rest orders: ${threadList.length} )`);
871
- threadList.pop(); // del last array
872
- }
873
- updateList();
874
- }, // End clearEntireList
875
-
876
- /**
877
- * Änderungen des Füllstands setzen + Vorrang der Pumpe setzen
878
- * @param {number} levelCistern
879
- */
880
- setFillLevelCistern (levelCistern) {
881
- fillLevelCistern = (typeof levelCistern === 'number') ? levelCistern : 0 ;
882
- setActualPump();
883
- } // End setFillLevelCistern
884
- } // End valveControl
885
-
886
- /*++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++*/
887
-
1
+ 'use strict';
2
+
3
+ const myConfig = require('./myConfig.js');
4
+ const addTime = require('./tools.js').addTime;
5
+ const formatTime = require('./tools').formatTime;
6
+ const sendMessageText = require('./sendMessageText.js'); // sendMessageText
7
+
8
+ /**
9
+ * The adapter instance
10
+ */
11
+ let adapter;
12
+
13
+ /**
14
+ * Thread-list
15
+ * → Auflistung aller aktiver Sprenger-Kreise
16
+ */
17
+ const threadList = [];
18
+
19
+ /** bereit für Boost */
20
+ let boostReady = true;
21
+ /** Boost ist aktive */
22
+ let boostOn = false;
23
+ /** Timer für die Länge des */
24
+ let boostListTimer;
25
+ /** maximal zulässige Anzahl der eingeschalteten Ventile */
26
+ let maxParallel = 0;
27
+ /** Füllstand der Zisterne */
28
+ let fillLevelCistern = 0;
29
+ let statusCistern = '';
30
+
31
+ const currentPumpUse = {
32
+ /** Pumpen aktive? */
33
+ enable: false,
34
+ /** Zisterne aktive? */
35
+ pumpCistern: false,
36
+ /** Pumpen-Bezeichnung; z.B. "hm-rpc.0.MEQ1810129.1.STATE" */
37
+ pumpName: '',
38
+ /** Pumpenleistung in l/h */
39
+ pumpPower: 0
40
+ };
41
+
42
+
43
+ /*==============================================================================================================================================*/
44
+ /* interne Funktionen */
45
+ /*----------------------------------------------------------------------------------------------------------------------------------------------*/
46
+
47
+ /**
48
+ * Sprinkle (sprinkleName) delete
49
+ * → Ventil (sprinkleName) löschen
50
+ *
51
+ * @param killList - sprinkleName der zu löschenden Objekte
52
+ */
53
+ function delList (killList) {
54
+ for(const sprinkleName of killList) {
55
+ let bValveFound = false; // Ventil gefunden
56
+ for(let counter = 0, // Loop über das Array
57
+ lastArray = (threadList.length - 1); // entsprechend der Anzahl der Eintragungen
58
+ counter <= lastArray;
59
+ counter++) {
60
+ const entry = threadList[counter].sprinkleName;
61
+ if ((sprinkleName === entry) || bValveFound) {
62
+ if (sprinkleName === entry) bValveFound = true;
63
+ if (counter !== lastArray) threadList[counter] = threadList[counter + 1];
64
+ }
65
+ }
66
+ /* If a valve is found, delete the last array (entry). Wenn Ventil gefunden letzten Array (Auftrag) löschen */
67
+ if (bValveFound) {
68
+ threadList.pop();
69
+ adapter.log.debug(`delList=> order deleted ID: ${sprinkleName} ( rest orders: ${threadList.length} )`);
70
+ }
71
+ }
72
+
73
+ } // End delList
74
+
75
+ /*----------------------------------------------------------------------------------------------------------------------------------------------*/
76
+
77
+ /**
78
+ * Activation of the booster (all other active valves are deactivated for the duration of the boost so that the sprinkler can be extended with the maximum possible pressure)
79
+ * => aktivierung des Boosters (alle anderen aktiven Ventile werden für die Zeit des Boosts deaktiviert um den maximalen möglichen Druck zum Ausfahren der Sprenger zu ermöglichen)
80
+ *
81
+ * @param sprinkleID
82
+ */
83
+ function boostList (sprinkleID) {
84
+ boostReady = false;
85
+ boostOn = true;
86
+ for(const entry of threadList) {
87
+ if (entry.enabled) {
88
+ if (entry.sprinkleID === sprinkleID) { // Booster
89
+ /* Zustand des Ventils im Thread < 0 > off, < 1 > wait, < 2 > on, < 3 > break, <<< 4 >>> Boost(on), < 5 > off(Boost) */
90
+ adapter.setState(`sprinkle.${entry.sprinkleName}.sprinklerState`, {
91
+ val: 4,
92
+ ack: true
93
+ });
94
+ //valveState(entry, 'Boost(on)');
95
+ entry.times.boostTime2 = setTimeout(() => {
96
+ /* Zustand des Ventils im Thread < 0 > off, < 1 > wait, <<< 2 >>> on, < 3 > break, < 4 > Boost(on), < 5 > off(Boost) */
97
+ adapter.setState(`sprinkle.${entry.sprinkleName}.sprinklerState`, {
98
+ val: 2,
99
+ ack: true
100
+ });
101
+ entry.times.boostTime2 = null;
102
+ },31000);
103
+ } else { // rest der Ventile
104
+ // in die Zwangspause (myBreak = true)
105
+ entry.times.boostTime1 = setTimeout(() => {
106
+ /* Zustand des Ventils im Thread < 0 > off, < 1 > wait, < 2 > on, < 3 > break, < 4 > Boost(on), <<< 5 >>> off(Boost) */
107
+ adapter.setState(`sprinkle.${entry.sprinkleName}.sprinklerState`, {
108
+ val: 5,
109
+ ack: true
110
+ });
111
+ entry.myBreak = true;
112
+ // valveOnOff(entry, false, '#2.1 Set: off(Boost), ID: ');
113
+ entry.times.boostTime1 = null;
114
+ },250);
115
+ // aus der Zwangspause holen (myBreak = false)
116
+ entry.times.boostTime2 = setTimeout(() => {
117
+ /* Zustand des Ventils im Thread < 0 > off, < 1 > wait, <<< 2 >>> on, < 3 > break, < 4 > Boost(on), < 5 > off(Boost) */
118
+ adapter.setState(`sprinkle.${entry.sprinkleName}.sprinklerState`, {
119
+ val: 2,
120
+ ack: true
121
+ });
122
+ // valveOnOff(entry, true, '#2.2 Set: on, ID: ');
123
+ entry.myBreak = false;
124
+ entry.times.boostTime2 = null;
125
+ },31000);
126
+ }
127
+ }
128
+ }
129
+ boostListTimer = setTimeout(() => {
130
+ boostOn = false;
131
+ updateList();
132
+ },32000);
133
+ } // End boostList
134
+
135
+ /*----------------------------------------------------------------------------------------------------------------------------------------------*/
136
+
137
+ /**
138
+ * If boostOn is ended by entering "runningTime = 0", normal operation should be restored. (Delete timer)
139
+ * → Wenn boostOn über die Eingabe "runningTime = 0" beendet wird, so soll zum Normalen ablauf wieder zurückgekehrt werden. (Löschen der Timer)
140
+ *
141
+ * @param sprinkleID
142
+ */
143
+ function boostKill (sprinkleID) {
144
+ for(const entry of threadList) {
145
+ if (entry.enabled) {
146
+ if (entry.sprinkleID === sprinkleID) {
147
+ /* booster wird gekillt */
148
+ boostOn = false;
149
+ if(entry.times.boostTime2) {
150
+ clearTimeout(entry.times.boostTime2);
151
+ entry.times.boostTime2 = null;
152
+ }
153
+ } else {
154
+ /* normaler weiterbetrieb für den Rest */
155
+ if (entry.times.boostTime1) {
156
+ clearTimeout(entry.times.boostTime1);
157
+ entry.times.boostTime1 = null;
158
+ adapter.log.debug(`boostKill => ID: ${entry.sprinkleName} => boostTime2 (Ende) gelöscht)`);
159
+ }
160
+ if (entry.times.boostTime2) {
161
+ clearTimeout(entry.times.boostTime2);
162
+ entry.times.boostTime2 = null;
163
+ /* Zustand des Ventils im Thread < 0 > off, < 1 > wait, <<< 2 >>> on, < 3 > break, < 4 > Boost(on), < 5 > off(Boost) */
164
+ adapter.setState(`sprinkle.${entry.sprinkleName}.sprinklerState`, {
165
+ val: 2,
166
+ ack: true
167
+ });
168
+ // valveOnOff(entry, true, '#2.3 Set: on, ID: ');
169
+ }
170
+ }
171
+ }
172
+ }
173
+ } // End boostKill
174
+
175
+ /*----------------------------------------------------------------------------------------------------------------------------------------------*/
176
+
177
+ /**
178
+ * Schaltintervall der Ventile, Schaltabstand ist in der Config hinterlegt
179
+ */
180
+ const valveDelay = () => {
181
+ return new Promise (
182
+ resolve => setTimeout (resolve, parseInt(adapter.config.switchingDistance))
183
+ );
184
+ };
185
+ /**
186
+ * Ausschalten der Ventile mit Schaltabstand
187
+ *
188
+ * @param threadList Auflistung aller aktiver Sprenger-Kreise
189
+ * @param parallel aktuelle Anzahl der eingeschalteten Ventile
190
+ * @returns
191
+ */
192
+ const switchTheValvesOffOn = async (threadList, parallel) => {
193
+ /**Sammlung von.sprinkleName die am Ende von updateList gelöscht werden
194
+ * @type {array} - killList
195
+ */
196
+ const killList = [];
197
+ for (const entry of threadList) { // ausschalten der Ventile
198
+ if ((!entry.enabled // Ventile ausgeschaltet z.B. Intervall-Beregnung
199
+ || entry.enabled && entry.myBreak // || in Pause z.B. Boost
200
+ || entry.killSprinkle) // || Bewässerung erledigt
201
+ && entry.enabled !== entry.enabledState // && Ventil nicht aktuell
202
+ ) {
203
+ adapter.setForeignState(entry.idState, {
204
+ val: false,
205
+ ack: false
206
+ }, (err) => {
207
+ if (err) {
208
+ return err;
209
+ } else {
210
+ adapter.log.info(`Set (${myConfig.config[entry.sprinkleID].methodControlSM}) ID: ${entry.sprinkleName}, value: ${entry.enabled}`);
211
+ }
212
+ });
213
+ entry.enabledState = entry.enabled;
214
+ /* Ventil aus threadList löschen → Aufgabe beendet und sind nicht in der Pause */
215
+ if (entry.killSprinkle) {
216
+ killList.push(entry.sprinkleName);
217
+ }
218
+ await valveDelay ();
219
+ }
220
+ }
221
+ if (currentPumpUse.pumpName !== '') {
222
+ setPumpOnOff(parallel > 0);
223
+ await valveDelay ();
224
+ }
225
+ if (adapter.config.triggerControlVoltage) {
226
+ setVoltageOnOff(parallel>0);
227
+ await valveDelay ();
228
+ }
229
+
230
+
231
+ for (const entry of threadList) { // einschalten der Ventile
232
+ if (entry.enabled // intern eingeschaltet
233
+ && !entry.myBreak // && keine Pause
234
+ && !entry.killSprinkle // Bewässerung noch nicht erledigt
235
+ && entry.enabled !== entry.enabledState // && Ventil nicht aktuell
236
+ ) {
237
+ adapter.setForeignState(entry.idState, {
238
+ val: true,
239
+ ack: false
240
+ }, (err) => {
241
+ if (err) {
242
+ return err;
243
+ } else {
244
+ adapter.log.info(`Set Valve (${myConfig.config[entry.sprinkleID].methodControlSM}) ID: ${entry.sprinkleName}, value: ${entry.enabled}, duration: ${addTime(entry.wateringTime)}`);
245
+ }
246
+ });
247
+ entry.enabledState = entry.enabled;
248
+ await valveDelay ();
249
+ }
250
+ }
251
+
252
+ delList(killList); // erledigte Bewässerungsaufgaben aus der threadList löschen
253
+ };
254
+
255
+ /*----------------------------------------------------------------------------------------------------------------------------------------------*/
256
+
257
+ /**
258
+ * Control of the active irrigation circuits so that the maximum pump capacity (l / h) is achieved and the maximum number of irrigation circuits is not exceeded.
259
+ * => Steuerung der aktiven Bewässerungskreise, damit die maximale Pumpenkapazität (l / h) erreicht wird und die maximale Anzahl der Bewässerungskreise nicht überschritten wird.
260
+ */
261
+ function updateList () {
262
+ /* während des Boost eines Kreises ist ein zuschalten von Sprengern nicht möglich */
263
+ if (boostOn) {
264
+ return;
265
+ }
266
+
267
+ /** aktuelle Rest-Pumpenleistung */
268
+ let curFlow = currentPumpUse.pumpPower, /* adapter.config.triggerMainPumpPower; */
269
+ /** aktuelle Anzahl der eingeschalteten Ventile */
270
+ parallel = 0;
271
+
272
+ /**
273
+ * Sortierfunktion mySortDescending absteigende Sortierung
274
+ *
275
+ * @param a
276
+ * @param b
277
+ * @returns
278
+ */
279
+ function mySortDescending(a, b) {
280
+ return (a.pipeFlow > b.pipeFlow) ? -1 :
281
+ (a.pipeFlow < b.pipeFlow) ? 1 :
282
+ 0;
283
+ }
284
+ /**
285
+ * Sortierfunktion mySortAscending aufsteigende Sortierung
286
+ *
287
+ * @param a
288
+ * @param b
289
+ * @returns
290
+ */
291
+ function mySortAscending(a, b) {
292
+ return (a.pipeFlow < b.pipeFlow) ? -1 :
293
+ (a.pipeFlow > b.pipeFlow) ? 1 :
294
+ 0;
295
+ }
296
+ /**
297
+ * Handling von Ventilen, Zeiten, Verbrauchsmengen im 1s Takt
298
+ *
299
+ * @param entry
300
+ */
301
+ function countSprinkleTime(entry) {
302
+ /* --- function beenden wenn ---*/
303
+ if (boostOn && !(myConfig.config[entry.sprinkleID].booster) // boost-On && kein aktuelles Boost-Ventil
304
+ ) {
305
+ return;
306
+ }
307
+ entry.count ++;
308
+ if ((entry.count < entry.wateringTime) // Zeit noch nicht abgelaufen?
309
+ && (!entry.calcOn // alles ausser Berechnung der Verdunstung
310
+ || !entry.autoOn // Handbetrieb
311
+ || (myConfig.config[entry.sprinkleID].soilMoisture.val < myConfig.config[entry.sprinkleID].soilMoisture.maxIrrigation)) // Bodenfeuchte noch nicht erreicht? (z.B. beim Regen)
312
+ ) { /* Zeit läuft */
313
+ adapter.setState(`sprinkle.${entry.sprinkleName}.countdown`, {
314
+ val: addTime(entry.wateringTime - entry.count),
315
+ ack: true
316
+ });
317
+ /* Alle 15s die Bodenfeuchte anpassen */
318
+ if (entry.calcOn // Vergleich nur bei Berechnung der Verdunstung
319
+ && !(entry.count % 15) // alle 15s ausführen
320
+ ) {
321
+ myConfig.addSoilMoistVal(entry.sprinkleID, entry.soilMoisture15s);
322
+ }
323
+ /* Intervall-Beregnung wenn angegeben (onOffTime > 0) */
324
+ if ((entry.onOffTime > 0) && !(entry.count % entry.onOffTime)) {
325
+ adapter.log.info(`Intervall-Beregnung, onOffTime: ${entry.onOffTime}, count: ${entry.count}`);
326
+ entry.enabled = false;
327
+ entry.myBreak = true;
328
+ /* Zustand des Ventils im Thread < 0 > off, < 1 > wait, < 2 > on, <<< 3 >>> break, < 4 > Boost(on), < 5 > off(Boost) */
329
+ adapter.setState(`sprinkle.${entry.sprinkleName}.sprinklerState`, {
330
+ val: 3,
331
+ ack: true
332
+ });
333
+ updateList();
334
+ clearInterval(entry.countdown);
335
+ entry.onOffTimeoutOff = setTimeout(()=>{
336
+ entry.myBreak = false;
337
+ /* Zustand des Ventils im Thread < 0 > off, <<< 1 >>> wait, < 2 > on, < 3 > break, < 4 > Boost(on), < 5 > off(Boost) */
338
+ adapter.setState(`sprinkle.${entry.sprinkleName}.sprinklerState`, {
339
+ val: 1,
340
+ ack: true
341
+ });
342
+ updateList();
343
+ },1000 * (entry.onOffTime < 600 ? 600 : entry.onOffTime)); // 600 sek Pause (10 min)
344
+ }
345
+ } else { /* zeit abgelaufen => Ventil ausschalten */
346
+ /* Zustand des Ventils im Thread <<< 0 >>> off, < 1 > wait, < 2 > on, < 3 > break, < 4 > Boost(on), < 5 > off(Boost) */
347
+ entry.enabled = false;
348
+ adapter.setState(`sprinkle.${entry.sprinkleName}.sprinklerState`, {
349
+ val: 0,
350
+ ack: true
351
+ });
352
+ adapter.setState(`sprinkle.${entry.sprinkleName}.runningTime`, {
353
+ val: '0',
354
+ ack: true
355
+ });
356
+ adapter.setState(`sprinkle.${entry.sprinkleName}.countdown`, {
357
+ val: '0',
358
+ ack: true
359
+ });
360
+
361
+ /* Wenn in der Konfiguration Bodenfeuchte = 100% gesetzt ist und Auto-Bewässerung aktive, dann Bodenfeuchte = 100% setzen*/
362
+ if (entry.autoOn && entry.calcOn && myConfig.config[entry.sprinkleID].endIrrigation) {
363
+ myConfig.setSoilMoistPct100(entry.sprinkleID);
364
+ }
365
+ /* Verbrauchswerte in der Historie aktualisieren */
366
+ addConsumedAndTime(entry);
367
+ /* Booster zurücksetzen */
368
+ if (myConfig.config[entry.sprinkleID].booster) {
369
+ if (boostOn) {
370
+ boostKill(entry.sprinkleID);
371
+ }
372
+ boostReady = true;
373
+ adapter.log.debug(`ID: ${entry.sprinkleName} UpdateList Sprinkle Off: boostReady = ${boostReady}`);
374
+ }
375
+ /* Zeiten löschen */
376
+ clearInterval(entry.countdown);
377
+ /*clearTimeout(entry.onOffTimeoutOn);*/
378
+ clearTimeout(entry.onOffTimeoutOff);
379
+ /* Ventil aus threadList löschen → Aufgabe beendet */
380
+ //delList(entry.sprinkleName);
381
+ entry.killSprinkle = true;
382
+ updateList();
383
+ }
384
+ }
385
+
386
+ // ermitteln von curPipe und der anzahl der parallelen Stränge
387
+ for(const entry of threadList){
388
+ if (entry.enabled && !entry.killSprinkle) {
389
+ curFlow -= entry.pipeFlow; // // ermitteln der RestFörderkapazität
390
+ parallel ++; // Anzahl der Bewässerungsstellen um 1 erhöhen
391
+ }
392
+ }
393
+
394
+ if (curFlow < 0) {
395
+ /* - wenn beim Umschalten der Pumpen die Förderleistung zu gering → Ventile deaktivieren - */
396
+ // aufsteigend sortieren nach der Verbrauchsmenge
397
+ threadList.sort(mySortAscending);
398
+
399
+ for(const entry of threadList) {
400
+ if (entry.enabled // eingeschaltet
401
+ && !entry.killSprinkle // && Aufgabe noch nicht erledigt
402
+ && (curFlow < 0) // && Förderleistung der Pumpe zu gering
403
+ ) {
404
+ entry.enabled = false; // ausgeschaltet merken
405
+ clearInterval(entry.countdown); // Zähler für Countdown, Verbrauchsmengen, usw. löschen
406
+ curFlow += entry.pipeFlow; // ermitteln der RestFörderkapazität
407
+ parallel--; // Anzahl der Bewässerungsstellen um 1 verringern
408
+ /* Zustand des Ventils im Thread < 0 > off, <<< 1 >>> wait, < 2 > on, < 3 > break, < 4 > Boost(on), < 5 > off(Boost) */
409
+ adapter.setState(`sprinkle.${entry.sprinkleName}.sprinklerState`, {
410
+ val: 1,
411
+ ack: true
412
+ });
413
+ // valveOnOff(entry, false, '#2.6 Set: wait, ID: ');
414
+ adapter.log.info(`Set Valve ID: ${entry.sprinkleName} Pump delivery rate too low, wait! curFlow ${curFlow} parallel: ${parallel}`);
415
+ }
416
+ }
417
+ }
418
+
419
+ // absteigend sortieren nach der Verbrauchsmenge
420
+ threadList.sort(mySortDescending);
421
+
422
+ // einschalten der Bewässerungsventile nach Verbrauchsmenge und maximaler Anzahl
423
+ for(const entry of threadList) {
424
+ // adapter.log.info(`Ventile Ein: enabled: ${!entry.enabled} && killSprinkle: ${!entry.killSprinkle} && myBreak: ${!entry.myBreak} && Flow: ${(curFlow >= entry.pipeFlow)} && ||: ${(parallel < maxParallel)} && noBoost: ${((boostReady) || !(myConfig.config[entry.sprinkleID].booster))}`);
425
+ if (!entry.enabled // ausgeschaltet
426
+ && !entry.killSprinkle // && Aufgabe noch nicht erledigt
427
+ && !entry.myBreak // && nicht in der Pause
428
+ && (curFlow >= entry.pipeFlow) // && noch genügend Förderleistung der Pumpe
429
+ && (parallel < maxParallel) // && maxParallel noch nicht erreicht
430
+ && ((boostReady) || !(myConfig.config[entry.sprinkleID].booster)) // nur einer mit boostFunction darf aktive sein
431
+ ) {
432
+ entry.enabled = true; // einschalten merken
433
+ if (myConfig.config[entry.sprinkleID].booster) {
434
+ boostReady = false;
435
+ adapter.log.debug(`ID: ${entry.sprinkleName}UpdateList sprinkle On: boostReady = ${boostReady}`);
436
+ setTimeout(() => {
437
+ boostList(entry.sprinkleID);
438
+ }, 50);
439
+ }
440
+ curFlow -= entry.pipeFlow; // ermitteln der RestFörderkapazität
441
+ parallel++; // Anzahl der Bewässerungsstellen um 1 erhöhen
442
+ /* Zustand des Ventils im Thread < 0 > off, < 1 > wait, <<< 2 >>> on, < 3 > break, < 4 > Boost(on), < 5 > off(Boost) */
443
+ adapter.setState(`sprinkle.${entry.sprinkleName}.sprinklerState`, {
444
+ val: 2,
445
+ ack: true
446
+ });
447
+ // valveOnOff(entry, true, '#2.7 Set: on, ID: ');
448
+ /* countdown starten */
449
+ if (!entry.startTime) {
450
+ entry.startTime = new Date();
451
+ }
452
+ entry.countdown = setInterval(() => {
453
+ countSprinkleTime(entry);
454
+ }, 1000); // 1000 = 1s
455
+
456
+ }
457
+ }
458
+
459
+ adapter.setState('control.parallelOfMax', {
460
+ val: `${parallel} : ${maxParallel}`,
461
+ ack: true
462
+ });
463
+ adapter.setState('control.restFlow', {
464
+ val: `${curFlow} (${currentPumpUse.pumpPower} ${currentPumpUse.pumpCistern ? 'Zisterne' : 'Grundwasser'})`,
465
+ ack: true
466
+ });
467
+
468
+ switchTheValvesOffOn(threadList, parallel).then(err => {
469
+ if (err) {
470
+ adapter.log.error(`Error - Set (false) err: ${err}`);
471
+ sendMessageText.sendMessage(`Error - Set (fase) err: ${err}`);
472
+ }
473
+ });
474
+ } // End updateList
475
+
476
+ /* --------------------------------------------------------------------------------------------------------------------------------------------------------------------- */
477
+
478
+ /**
479
+ * +++++ Set the current pump for irrigation +++++
480
+ * → Festlegen der aktuellen Pumpe zur Bewässerung
481
+ */
482
+ function setActualPump () {
483
+ if (adapter.config.cisternSettings === true) {
484
+ /* Zisternen-Bewässerung Einstellung in der config (2. Pumpe) aktiviert */
485
+ if (currentPumpUse.enable === true) {
486
+ /* Bewässerungspumpen aktiv */
487
+ if ((fillLevelCistern < parseFloat(adapter.config.triggerMinCisternLevel)) && (currentPumpUse.pumpCistern === true)) {
488
+ /* (Zisterne unter Minimum) && (ZisternenPumpe läuft) */
489
+ adapter.setForeignState(currentPumpUse.pumpName, {
490
+ val: false,
491
+ ack: false
492
+ }); // Pumpe Zisterne Aus
493
+ if (currentPumpUse.pumpCistern === true && !sendMessageText.onlySendError()) {
494
+ sendMessageText.sendMessage(`Pump change (cistern empty) Cistern pump off => main pump on`);
495
+ }
496
+ currentPumpUse.pumpCistern = false;
497
+ currentPumpUse.pumpName = adapter.config.triggerMainPump || '';
498
+ currentPumpUse.pumpPower = parseInt(adapter.config.triggerMainPumpPower);
499
+ adapter.setForeignState(currentPumpUse.pumpName, {
500
+ val: true,
501
+ ack: false
502
+ }); // Hauptpumpe Ein
503
+ adapter.log.info('Pump change (cistern empty) Cistern pump off => main pump on');
504
+ updateList(); // Wasserverbrauch an Pumpenleistung anpassen
505
+ }
506
+ if (fillLevelCistern < parseFloat(adapter.config.triggerMinCisternLevel)) {
507
+ statusCistern = `Cistern empty: ${fillLevelCistern} % (${adapter.config.triggerMinCisternLevel + 5} %)`;
508
+ adapter.setState('info.cisternState', {
509
+ val: statusCistern,
510
+ ack: true
511
+ });
512
+ } else {
513
+ statusCistern = `Cistern filled: ${fillLevelCistern} % (${adapter.config.triggerMinCisternLevel} %)`;
514
+ adapter.setState('info.cisternState', {
515
+ val: statusCistern,
516
+ ack: true
517
+ });
518
+ }
519
+ } else {
520
+ /* Bewässerungspumpen inaktiv */
521
+ if ((fillLevelCistern >= (parseFloat(adapter.config.triggerMinCisternLevel) + 5)) && (adapter.config.triggerCisternPump) && (adapter.config.triggerCisternPumpPower)) {
522
+ /* Zisterne voll && triggerCisternPump && triggerCisternPumpPower vorhanden*/
523
+ statusCistern = `Cistern filled: ${fillLevelCistern} % (${adapter.config.triggerMinCisternLevel} %)`;
524
+ adapter.setState('info.cisternState', {
525
+ val: statusCistern,
526
+ ack: true
527
+ });
528
+ if (currentPumpUse.pumpCistern === false && !sendMessageText.onlySendError()) {
529
+ sendMessageText.sendMessage(`Cistern filled: ${fillLevelCistern} % (${adapter.config.triggerMinCisternLevel} %)`);
530
+ }
531
+ currentPumpUse.pumpCistern = true;
532
+ currentPumpUse.pumpName = adapter.config.triggerCisternPump || '';
533
+ currentPumpUse.pumpPower = parseInt(adapter.config.triggerCisternPumpPower);
534
+ adapter.setState('control.restFlow', {
535
+ val: `${currentPumpUse.pumpPower} (${currentPumpUse.pumpPower} Zisterne)`,
536
+ ack: true
537
+ });
538
+ } else {
539
+ statusCistern = `Cistern empty: ${fillLevelCistern} % (${adapter.config.triggerMinCisternLevel + 5} %)`;
540
+ adapter.setState('info.cisternState', {
541
+ val: statusCistern,
542
+ ack: true
543
+ });
544
+ currentPumpUse.pumpCistern = false;
545
+ currentPumpUse.pumpName = adapter.config.triggerMainPump || '';
546
+ currentPumpUse.pumpPower = parseInt(adapter.config.triggerMainPumpPower);
547
+ adapter.setState('control.restFlow', {
548
+ val: `${currentPumpUse.pumpPower} (${currentPumpUse.pumpPower} Grundwasser)`,
549
+ ack: true
550
+ });
551
+ }
552
+ }
553
+ } else {
554
+ /* Pumpe AUS => Zisternen-Bewässerung nicht aktiviert */
555
+ if (adapter.config.triggerCisternPump) {
556
+ statusCistern = `Cistern settings are not active!${(fillLevelCistern > 0)?(` level sensor: ${fillLevelCistern}%${(adapter.config.triggerMinCisternLevel !== '')?(`${adapter.config.triggerMinCisternLevel}%`):('')}`):('')}`;
557
+ adapter.setState('info.cisternState', {
558
+ val: statusCistern,
559
+ ack: true
560
+ });
561
+ }
562
+ }
563
+ } // End setActualPump
564
+
565
+ /**
566
+ * Switching the pump on or off
567
+ * => Ein bzw. ausschaltern der Pumpe
568
+ *
569
+ * @param pumpOnOff ; Pumpe on = true
570
+ */
571
+ function setPumpOnOff(pumpOnOff) {
572
+ if (currentPumpUse.pumpName !== '') {
573
+ adapter.getForeignState(currentPumpUse.pumpName, (err, state) => {
574
+ if (state) {
575
+ if (pumpOnOff) {
576
+ if (state.val === false) {
577
+ adapter.setForeignState(currentPumpUse.pumpName, {
578
+ val: true,
579
+ ack: false
580
+ });
581
+ currentPumpUse.enable = true;
582
+ adapter.log.info('Set (pump) on');
583
+ }
584
+ } else {
585
+ if (state.val !== false) {
586
+ adapter.setForeignState(currentPumpUse.pumpName, {
587
+ val: false,
588
+ ack: false
589
+ });
590
+ currentPumpUse.enable = false;
591
+ adapter.log.info('Set (pump) off');
592
+ }
593
+ }
594
+ } else if (err) {
595
+ adapter.log.error(`triggerMainPump ${currentPumpUse.pumpName} is not available (ist nicht erreichbar): ${err}`);
596
+ }
597
+ });
598
+ }
599
+ } // End setPumpOnOff
600
+
601
+ /**
602
+ * Switching the control voltage on or off
603
+ * => Ein bzw. ausschaltern der Steuerspannung
604
+ *
605
+ * @param voltageOnOff - Voltage on = true
606
+ */
607
+ function setVoltageOnOff(voltageOnOff) {
608
+ if (adapter.config.triggerControlVoltage !== '') {
609
+ adapter.getForeignState(adapter.config.triggerControlVoltage, (err, state) => {
610
+ if (state) {
611
+ if (voltageOnOff) {
612
+ if (state.val === false) {
613
+ adapter.setForeignState(adapter.config.triggerControlVoltage, {
614
+ val: true,
615
+ ack: false
616
+ });
617
+ adapter.log.info('Set (voltage) on');
618
+ }
619
+ } else {
620
+ if (state.val !== false) {
621
+ adapter.setForeignState(adapter.config.triggerControlVoltage, {
622
+ val: false,
623
+ ack: false
624
+ });
625
+ adapter.log.info('Set (voltage) off');
626
+ }
627
+ }
628
+ } else if (err) {
629
+ adapter.log.error(`triggerControlVoltage is not available (ist nicht erreichbar): ${err}`);
630
+ }
631
+ });
632
+ }
633
+ }
634
+
635
+ /**
636
+ * Adding the consumption data to the history
637
+ * => Hinzufügen der Verbrauchsdaten zur History
638
+ *
639
+ * @param entry - array mit den Daten des aktiven Ventils
640
+ */
641
+ function addConsumedAndTime(entry) {
642
+ adapter.setState(`sprinkle.${entry.sprinkleName}.history.lastConsumed`, {
643
+ val: Math.round(entry.litersPerSecond * entry.count),
644
+ ack: true
645
+ });
646
+ adapter.setState(`sprinkle.${entry.sprinkleName}.history.lastRunningTime`, {
647
+ val: addTime(entry.count),
648
+ ack: true
649
+ });
650
+ adapter.setState(`sprinkle.${entry.sprinkleName}.history.lastOn`, {
651
+ val: formatTime(adapter, entry.startTime, 'dd.mm. hh:mm'),
652
+ ack: true
653
+ });
654
+ adapter.getState(`sprinkle.${entry.sprinkleName}.history.curCalWeekConsumed`, (err, state) => {
655
+ if (state) {
656
+ adapter.setState(`sprinkle.${entry.sprinkleName}.history.curCalWeekConsumed`, {
657
+ val: (state.val) + Math.round(entry.litersPerSecond * entry.count),
658
+ ack: true
659
+ });
660
+ }
661
+ });
662
+ adapter.getState(`sprinkle.${entry.sprinkleName}.history.curCalWeekRunningTime`, (err, state) => {
663
+ if (state) {
664
+ adapter.setState(`sprinkle.${entry.sprinkleName}.history.curCalWeekRunningTime`, {
665
+ val: addTime(state.val, entry.count),
666
+ ack: true
667
+ });
668
+ }
669
+ });
670
+ } // End addConsumedAndTime
671
+
672
+
673
+ /*++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++*/
674
+ /* externe Funktionen */
675
+ /*++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++*/
676
+
677
+ /**
678
+ * -- externe Funktionen -> initValveControl - addList - clearEntireList - setFillLevelCistern --
679
+ *
680
+ */
681
+ const valveControl = {
682
+ /**
683
+ * Initialize the start configuration of ventilControl
684
+ * => Initialisieren Sie die Startkonfiguration von ventilControl
685
+ *
686
+ * @param myAdapter
687
+ */
688
+ initValveControl (myAdapter) {
689
+ adapter = myAdapter;
690
+ currentPumpUse.pumpCistern = false;
691
+ currentPumpUse.pumpName = adapter.config.triggerMainPump || '';
692
+ currentPumpUse.pumpPower = parseInt(adapter.config.triggerMainPumpPower) || 0;
693
+ maxParallel = parseInt(adapter.config.maximumParallelValves);
694
+ /* Objekt control.restFlow befüllen */
695
+ adapter.setState('control.restFlow', {
696
+ val: `${currentPumpUse.pumpPower} (${currentPumpUse.pumpPower} ${currentPumpUse.pumpCistern ? 'Zisterne' : 'Grundwasser'})`,
697
+ ack: true
698
+ });
699
+ /* Objekt control.parallelOfMax befüllen */
700
+ adapter.setState('control.parallelOfMax', {
701
+ val: `0 : ${adapter.config.maximumParallelValves}`,
702
+ ack: true
703
+ });
704
+ /* 24V ausschalter, wenn vorhanden */
705
+ if (adapter.config.triggerControlVoltage !== '') {
706
+ adapter.getForeignState('adapter.config.triggerControlVoltage', (err, state) => {
707
+ if (state && state.val === true) {
708
+ adapter.setForeignState(adapter.config.triggerControlVoltage, {
709
+ val: false,
710
+ ack: false
711
+ });
712
+ }
713
+ });
714
+ }
715
+ /* Pumpe ausschalter, wenn vorhanden */
716
+ if (adapter.config.triggerMainPump !== '') {
717
+ adapter.getForeignState('adapter.config.triggerMainPump', (err, state) => {
718
+ if (state && state.val === true) {
719
+ adapter.setForeignState(adapter.config.triggerMainPump, {
720
+ val: false,
721
+ ack: false
722
+ });
723
+ }
724
+ });
725
+ }
726
+ /* Pumpe (Zisterne) ausschalter, wenn vorhanden */
727
+ if (adapter.config.triggerCisternPump !== '') {
728
+ adapter.getForeignState('adapter.config.triggerCisternPump', (err, state) => {
729
+ if (state && state.val === true) {
730
+ adapter.setForeignState(adapter.config.triggerCisternPump, {
731
+ val: false,
732
+ ack: false
733
+ });
734
+ }
735
+ });
736
+ }
737
+ /* alle Ventile (.name = "hm-rpc.0.MEQ1234567.3.STATE") in einem definierten Zustand (false) versetzen*/
738
+ const result = adapter.config.events;
739
+ if (result) {
740
+ for (const res of result) {
741
+ adapter.getForeignState(res.name, (err, state) => {
742
+ if (state && state.val === true) {
743
+ adapter.setForeignState(res.name, {
744
+ val: false,
745
+ ack: false
746
+ });
747
+ }
748
+ });
749
+ }
750
+ }
751
+ }, // End initValveControl
752
+
753
+ /**
754
+ * Add Sprinkle→ Sprinkle hinzufügen
755
+ *
756
+ * @param sprinkleList
757
+ * @param sprinkleList[].auto - auto Automatik == (true), Handbetrieb == (false)
758
+ * @param sprinkleList[].sprinkleID - sprinkleID → zugriff auf myConfig.config[sprinkleID]. xyz
759
+ * @param sprinkleList[].wateringTime - wateringTime → Bewässerungszeit in min
760
+ */
761
+ addList (sprinkleList) {
762
+ //
763
+ for (const res of sprinkleList) {
764
+ const sprinkleName = myConfig.config[res.sprinkleID].objectName;
765
+ /**
766
+ * add done
767
+ * hinzufügen erledigt (Sprenger bereits aktive)
768
+ */
769
+ let addDone = false;
770
+ // schauen ob der Sprenger schon in der threadList ist
771
+ if (threadList) {
772
+ for (const entry of threadList) {
773
+ if (entry.sprinkleID === res.sprinkleID) {
774
+ if (entry.wateringTime === res.wateringTime) {
775
+ // adapter.setState('sprinkle.' + sprinkleName + '.runningTime', {val: addTime(wateringTime, ''), ack: false});
776
+ return;
777
+ }
778
+ entry.wateringTime = res.wateringTime;
779
+ entry.autoOn = res.auto; // auto: = true autostart; = false Handbetrieb
780
+ adapter.setState(`sprinkle.${sprinkleName}.runningTime`, {
781
+ val: addTime(res.wateringTime),
782
+ ack: true
783
+ });
784
+ addDone = true; // Sprinkle found
785
+ adapter.log.debug(`update ID: ${entry.sprinkleName} new time: ${addTime(res.wateringTime)}`);
786
+ break;
787
+ }
788
+ }
789
+ }
790
+
791
+ if (!addDone) {
792
+ if (res.wateringTime <= 0) {
793
+ adapter.setState(`sprinkle.${sprinkleName}.runningTime`, {
794
+ val: '0',
795
+ ack: true
796
+ });
797
+ return;
798
+ }
799
+
800
+ let pipeFlow = myConfig.config[res.sprinkleID].pipeFlow;
801
+
802
+ // Kontrolle des Ventilverbrauchs(muss kleiner als Pumpenleistung sein, da Ventil sonst nicht starten kann)
803
+ if (adapter.config.cisternSettings === true) {
804
+ if ((pipeFlow > adapter.config.triggerMainPumpPower) || (pipeFlow > adapter.config.triggerCisternPumpPower)) {
805
+ if (pipeFlow > adapter.config.triggerMainPumpPower) adapter.log.warn(`Emergency irrigation! ${sprinkleName}: Valve consumption > pump performance (main pump)`);
806
+ if (pipeFlow > adapter.config.triggerCisternPumpPower) adapter.log.warn(`Emergency irrigation! ${sprinkleName}: Valve consumption > pump performance (cistern pump)`);
807
+ pipeFlow = (adapter.config.triggerMainPumpPower < adapter.config.triggerCisternPumpPower) ? adapter.config.triggerMainPumpPower : adapter.config.triggerCisternPumpPower;
808
+ }
809
+ } else {
810
+ if (pipeFlow > adapter.config.triggerMainPumpPower) {
811
+ adapter.log.warn(`Emergency irrigation! ${sprinkleName}: Valve consumption > pump performance (main pump)`);
812
+ pipeFlow = adapter.config.triggerMainPumpPower;
813
+ }
814
+ }
815
+
816
+ const newThread = {
817
+ sprinkleID: res.sprinkleID, // Array[0...]
818
+ sprinkleName: sprinkleName, // z.B "Blumenbeet"
819
+ idState: myConfig.config[res.sprinkleID].idState, // z.B. "hm-rpc.0.MEQ1810129.1.STATE"
820
+ wateringTime: res.wateringTime, // Bewässerungszeit in s (Sekunden)
821
+ pipeFlow: pipeFlow, // Wasserverbrauch in l/h
822
+ count: 0, // Zähler im Sekundentakt
823
+ calcOn: (myConfig.config[res.sprinkleID].methodControlSM === 'calculation'), // Anpassung der Bodenfeuchte true/false
824
+ enabled: false, // Ventil softwaremäßig ein
825
+ enabledState: false, // Ventil hardwaremäßig ein
826
+ myBreak: false, // meine Pause
827
+ killSprinkle: false, // Löschauftrag ausführen am Ende in threadList
828
+ litersPerSecond: pipeFlow / 3600, // Wasserverbrauchsmenge pro Sekunde
829
+ onOffTime: myConfig.config[res.sprinkleID].wateringInterval,
830
+ autoOn: res.auto,
831
+ soilMoisture15s: 15 * (myConfig.config[res.sprinkleID].soilMoisture.maxIrrigation - myConfig.config[res.sprinkleID].soilMoisture.triggersIrrigation)
832
+ / (60 * myConfig.config[res.sprinkleID].wateringTime),
833
+ times: {
834
+ boostTime1: null, // boost start
835
+ boostTime2: null, // boost ende
836
+ }, // hinterlegen der verschiedenen Zeiten von timeout für gezieltes späteres löschen
837
+ id: threadList.length || 0,
838
+ };
839
+ threadList.push(newThread);
840
+ /* Zustand des Ventils im Thread < 0 > off, <<< 1 >>> wait, < 2 > on, < 3 > break, < 4 > Boost(on), < 5 > off(Boost) */
841
+ adapter.setState(`sprinkle.${sprinkleName}.sprinklerState`, {
842
+ val: 1,
843
+ ack: true
844
+ });
845
+ adapter.setState(`sprinkle.${sprinkleName}.runningTime`, {
846
+ val: addTime(res.wateringTime),
847
+ ack: true
848
+ });
849
+ adapter.log.debug(`ID: ${sprinkleName} new order created: ${JSON.stringify(threadList[newThread.id])}`);
850
+ }
851
+ }
852
+ updateList();
853
+ }, // End addList
854
+
855
+ /**
856
+ * switch off all devices, when close the adapter
857
+ * => Beim Beenden des adapters alles ausschalten
858
+ */
859
+ clearEntireList () {
860
+ setVoltageOnOff(false);
861
+ setPumpOnOff(false);
862
+ if (boostListTimer) {
863
+ boostReady = true;
864
+ boostOn = false;
865
+ clearTimeout(boostListTimer);
866
+ }
867
+ // let bValveFound = false; // Ventil gefunden
868
+ for (let counter = threadList.length - 1; // Loop über das Array
869
+ counter >= 0;
870
+ counter--) {
871
+ const entry = threadList[counter];
872
+ if (entry.enabledState) {
873
+ adapter.log.info(`Set Valve (SprinkleControl: off) ID: ${entry.sprinkleName}, value: false`);
874
+ adapter.setForeignState(entry.idState, {
875
+ val: false,
876
+ ack: false
877
+ });
878
+ }
879
+ /* Zustand des Ventils im Thread <<< 0 >>> off, < 1 > wait, < 2 > on, < 3 > break, < 4 > Boost(on), < 5 > off(Boost) */
880
+ adapter.setState(`sprinkle.${entry.sprinkleName}.sprinklerState`, {
881
+ val: 0,
882
+ ack: true
883
+ });
884
+ adapter.setState(`sprinkle.${entry.sprinkleName}.runningTime`, {
885
+ val: '0',
886
+ ack: true
887
+ });
888
+ adapter.setState(`sprinkle.${entry.sprinkleName}.countdown`, {
889
+ val: '0',
890
+ ack: true
891
+ });
892
+ // valveOnOff(myEntry, false, '#2.0 Set: off, ID: ');
893
+ /* Verbrauchswerte in der Historie aktualisieren */
894
+ addConsumedAndTime(entry);
895
+ /* del timer countdown */
896
+ clearInterval(entry.countdown);
897
+ /* del timer onOffTimeoutOff */
898
+ clearTimeout(entry.onOffTimeoutOff);
899
+ /* del timer newThread.times.boostTime1 */
900
+ if (entry.times.boostTime1) {
901
+ clearTimeout(entry.times.boostTime1);
902
+ entry.times.boostTime1 = null;
903
+ }
904
+ /* del timer newThread.times.boostTime2 */
905
+ if (entry.times.boostTime2) {
906
+ clearTimeout(entry.times.boostTime2);
907
+ entry.times.boostTime2 = null;
908
+ }
909
+ adapter.log.debug(`order deleted Stop all ID: ${entry.sprinkleName} ( rest orders: ${threadList.length} )`);
910
+ threadList.pop(); // del last array
911
+ }
912
+ updateList();
913
+ }, // End clearEntireList
914
+
915
+ /**
916
+ * Änderungen des Füllstands setzen + Vorrang der Pumpe setzen
917
+ *
918
+ * @param levelCistern
919
+ */
920
+ setFillLevelCistern (levelCistern) {
921
+ fillLevelCistern = (typeof levelCistern === 'number') ? levelCistern : 0;
922
+ setActualPump();
923
+ }, // End setFillLevelCistern
924
+
925
+ getStatusCistern () {
926
+ return statusCistern;
927
+ }
928
+ }; // End valveControl
929
+
930
+ /*++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++*/
931
+
888
932
  module.exports = valveControl;