iobroker.sprinklecontrol 0.2.12 → 0.2.14

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