iobroker.sprinklecontrol 1.0.0 → 1.0.2

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.
@@ -5,10 +5,7 @@ const asyncTime = require('timers/promises');
5
5
  // import {setTimeout} from 'timers/promises';
6
6
 
7
7
  const myConfig = require('./myConfig.js');
8
- //const sleep = require('./tools.js').sleep;
9
- const addTime = require('./tools.js').addTime;
10
- const formatTime = require('./tools').formatTime;
11
- const findAsync = require('./tools.js').findAsync;
8
+ const tools = require('./tools.js').tools;
12
9
  const sendMessageText = require('./sendMessageText.js'); // sendMessageText
13
10
 
14
11
  /**
@@ -34,11 +31,15 @@ let boostReady = true,
34
31
  * maximal zulässige Anzahl der eingeschalteten Ventile
35
32
  */
36
33
  maxParallel = 0,
37
- /* Control of the cistern pump */
38
34
  /**
39
35
  * Füllstand der Zisterne
40
36
  */
41
- fillLevelCistern = 0;
37
+ fillLevelCistern = 0,
38
+ /**
39
+ * Zeitliche Bewässerungseinschränkung EIN/AUS
40
+ */
41
+ timeBasedRestrictionEn = false;
42
+
42
43
  const updateListMarker = {funcActive: false, newStart: false, switchingDistance: 5000, cancelSwitchingDistance: {}};
43
44
 
44
45
  /**
@@ -57,7 +58,9 @@ const updateListMarker = {funcActive: false, newStart: false, switchingDistance:
57
58
  * - ac: AbortController
58
59
  */
59
60
 
60
- const currentPumpUse = {enable: false, name: '', idState: '', id: '', pumpCistern: false, intBreak: false, leadTime: 0, cancelLeadTime: {}, pumpPower: 0, restFlow: 0,ac: {},controller: {}};
61
+ const currentPumpUse = {enable: false, name: '', wateringTime: 0, id: '', pumpCistern: false, intBreak: false, leadTime: 0, cancelLeadTime: {}, pumpPower: 0, restFlow: 0,ac: {},controller: {}, control: {idState: undefined, idON_TIME: undefined, idACK: undefined, maker: undefined}};
62
+ let mainPumpControl = {idState: undefined, idON_TIME: undefined, idACK: undefined, maker: undefined};
63
+ let cisternPumpControl = {idState: undefined, idON_TIME: undefined, idACK: undefined, maker: undefined};
61
64
 
62
65
  /**
63
66
  * Steuerspannung 24V
@@ -66,7 +69,7 @@ const currentPumpUse = {enable: false, name: '', idState: '', id: '', pumpCister
66
69
  * - idState: Aktorerkennung "hm-rpc.0.MEQ1810129.1.STATE"
67
70
  * - controller: controlle von Zeiten und Abbruchsignalen
68
71
  */
69
- const controlVoltage ={enable: false, name: '24V', idState: undefined, ac: {}, controller: {}};
72
+ const controlVoltage ={enable: false, name: '24V',wateringTime: 0, ac: {}, controller: {}, control: {idState: undefined, idON_TIME: undefined, idACK: undefined, maker: undefined}};
70
73
 
71
74
  /**
72
75
  * Schaltabstand in ms
@@ -87,9 +90,26 @@ let switchingDistanceMS = 250;
87
90
  */
88
91
 
89
92
  const setValve = async (thread, val) => {
93
+ // beim Schalten im guten Vertrauen (Befehl ohne Antwort)
94
+ if (adapter.config.switchingBehavior === 'noResponse') {
95
+ try {
96
+ await adapter.setForeignStateAsync(thread.control.idState, {
97
+ val: val,
98
+ ack: false
99
+ });
100
+ thread.enable = val;
101
+ adapter.log.info(`setValve ${ thread.name }: ${ val }, ${ thread.wateringTime > 0 && val === true ? `${ tools.addTime(thread.wateringTime,'') }, ` : '' }`);
102
+ return thread.control.idState;
103
+ } catch (error) {
104
+ thread.enable = false;
105
+ thread.state = 'Error'
106
+ throw new Error(`setValve ${thread.name} error: ${error}`);
107
+ }
108
+ }
90
109
 
91
110
  const startTime = new Date();
92
111
  let result = undefined;
112
+ let updateStr = '';
93
113
 
94
114
  thread.ac.acSetValveCancelTimeout = new AbortController;
95
115
  /**
@@ -98,58 +118,75 @@ const setValve = async (thread, val) => {
98
118
  * @param {{val:boolean,ack:boolean}} state
99
119
  */
100
120
  thread.controller.ackTrue = (state) => {
101
- if (typeof state.val === 'boolean'
102
- && state.val === val
103
- && state.ack === true
121
+ if (
122
+ (thread.control.maker === 'standard') && (state?.val === val)
123
+ || (thread.control.maker === 'HM') && (val ? state?.val === true : state?.val === false)
124
+ || (thread.control.maker === 'HmIP') && (val ? state?.val === 1 : state?.val === 0)
104
125
  ) {
105
126
  adapter.log.debug(`setValve ${thread.name} => ackTrue`);
106
- result = thread.idState;
107
- thread.enable = val;
127
+ result = thread.control.idState;
128
+ thread.enable === val ? updateStr = ' (update)' : thread.enable = val;
108
129
  thread.ac.acSetValveCancelTimeout.abort();
109
130
  } else {
110
- adapter.log.warn(`setValve ${thread.name} => ackTrue val = ${val} state: ${JSON.stringify(state)}`);
131
+ adapter.log.debug(`setValve ${thread.name} => check ackTrue!
132
+ if => maker ${ thread.control.maker } === 'standard' && ${state?.val} === ${val}
133
+ || ${thread.control.maker} === 'HM' && ${val} ? ${state?.val} === true : ${state?.val} === false
134
+ || ${thread.control.maker} === 'HmIP' && ${val} ? ${state?.val} === 1 : ${state?.val} === 0}
135
+ `);
111
136
  }
112
137
  };
113
138
 
114
139
  //adapter.log.info(`Set Valve (async () => {...}`);
115
140
  try {
116
141
  // Ventil ansteuern
117
- const _setValve = await adapter.setForeignStateAsync(thread.idState, {
142
+ if (thread.control.idON_TIME !== null) {
143
+ await adapter.setForeignStateAsync(thread.control.idON_TIME, {
144
+ val: val ? Math.ceil(thread.wateringTime + 5) : 0,
145
+ ack: false
146
+ });
147
+ await asyncTime.setTimeout(200, undefined, undefined);
148
+ }
149
+
150
+ const _setValve = await adapter.setForeignStateAsync(thread.control.idState, {
118
151
  val: val,
119
152
  ack: false
120
153
  });
121
- if (_setValve === thread.idState) { // Auftrag ausgeführt
122
- await asyncTime.setTimeout(10000, undefined, { signal: thread.ac.acSetValveCancelTimeout.signal });
123
- const _getValve = await adapter.getForeignStateAsync(thread.idState);
124
- if (_getValve && typeof _getValve.val === 'boolean'
125
- && _getValve.val === val
126
- && _getValve.ack === true
154
+ if (_setValve === thread.control.idState) { // Auftrag ausgeführt
155
+ await asyncTime.setTimeout(3000, undefined, { signal: thread.ac.acSetValveCancelTimeout.signal }); // max. 3s warten auf Rückmeldung ackTrue
156
+ const _getValve = await adapter.getForeignStateAsync(thread.control.idState);
157
+ if (_getValve?.val === val
127
158
  ) {
128
- thread.enable = val;
129
- return thread.idState;
159
+ thread.enable === val ? updateStr = ' (update)' : thread.enable = val;;
160
+ return thread.control.idState;
130
161
  } else {
131
- adapter.log.debug(`setValve ${thread.name} _getValve: ${_getValve}`);
132
- throw new Error(`setForeignState not responding (ack === false)`);
162
+ throw new Error(` > was not switched! (${val}) Check the device! Reply: ${JSON.stringify(_getValve)}`);
133
163
  }
134
164
  } else {
135
- throw new Error(`set Valve was not switched`);
165
+ throw new Error(` command could not be sent`);
136
166
  }
137
167
  } catch (error) {
138
168
  // thread.controller.ackTrue wurde ausgelöst
139
169
  if (error.name !== `AbortError`) {
140
- await adapter.setForeignStateAsync(thread.idState, {
170
+ await adapter.setForeignStateAsync(thread.control.idState, {
141
171
  val: false,
142
172
  ack: false
143
173
  });
144
174
  thread.enable = false;
175
+ thread.state = 'Error'
145
176
  if (adapter.config.notificationEnabled) {
146
- sendMessageText.sendMessage(`setValve ${thread.name} (${thread.idState}) ${error}`);
177
+ sendMessageText.sendMessage(`setValve ${thread.name} (${thread.control.idState}) ${error}`);
147
178
  }
148
- throw new Error(`set Valve (${thread.idState}) ${error}`);
179
+ throw new Error(` > set Valve ${error}`);
149
180
  }
150
181
  } finally {
182
+ adapter.log.info(`setValve ${thread.name}:
183
+ ${val},
184
+ ${thread.control.maker !== 'standard' ? `${thread.control.maker}, ` : ''}
185
+ ${ (thread.wateringTime > 0 && val === true && thread.name !== 'Cistern pump' && thread.name !== 'Main pump' && thread.name !== '24V')
186
+ ? `${ tools.addTime(thread.wateringTime,'') }${ updateStr }, ` : ''}
187
+ processing time: ${(+new Date()) - +startTime}ms
188
+ `);
151
189
  if (thread.ac.acSetValveCancelTimeout.aborted === false) thread.ac.acSetValveCancelTimeout.abort();
152
- adapter.log.info(`setValve ${thread.name}: ${val}, ${thread.wateringTime > 0 && val === true ? `${ addTime(thread.wateringTime,'') }, ` : ''}processing time: ${(+new Date()) - +startTime}ms`);
153
190
  }
154
191
  adapter.setStateAsync(`sprinkle.${thread.name}.sprinklerState`, {
155
192
  val: thread.state,
@@ -221,6 +258,7 @@ const currentConsumption = async (write) => {
221
258
  if (entry.state === 'wait') pumpRequired = true; // state => wait
222
259
  if (entry.enable === true
223
260
  && entry.extBreak === false
261
+ && timeBasedRestrictionEn === false
224
262
  ) {
225
263
  curFlow -= entry.pipeFlow; // // ermitteln der RestFörderkapazität
226
264
  parallel ++; // Anzahl der Bewässerungsstellen um 1 erhöhen
@@ -228,7 +266,7 @@ const currentConsumption = async (write) => {
228
266
  }
229
267
  }
230
268
  }
231
-
269
+ // bei write schreiben der aktuellen Förderleistung der Pumpe und der Anzahl der parallelen Stränge
232
270
  if (write || currentPumpUse.intBreak) {
233
271
  adapter.setStateAsync('control.parallelOfMax', {
234
272
  val: `${parallel} : ${maxParallel}`,
@@ -291,7 +329,9 @@ const countSprinkleTime = async (entry) => {
291
329
  try {
292
330
  /* --- function beenden wenn ---*/
293
331
  if ((boostOn && !(myConfig.config[entry.sprinkleID].booster) // boost-On && kein aktuelles Boost-Ventil
294
- || entry.extBreak) // extBreak aktive
332
+ || entry.extBreak === true // extBreak aktive
333
+ || timeBasedRestrictionEn === true // zeitliche Bewässerungsbeschränkung aktiv
334
+ )
295
335
  ) {
296
336
  return;
297
337
  }
@@ -304,7 +344,7 @@ const countSprinkleTime = async (entry) => {
304
344
  || (myConfig.config[entry.sprinkleID].calculation.val < myConfig.config[entry.sprinkleID].calculation.maxIrrigation)) // Bodenfeuchte noch nicht erreicht? (z.B. beim Regen)
305
345
  ) { /* Zeit läuft */
306
346
  adapter.setStateAsync(`sprinkle.${entry.name}.countdown`, {
307
- val: addTime(entry.wateringTime - entry.count, ''),
347
+ val: tools.addTime(entry.wateringTime - entry.count, ''),
308
348
  ack: true
309
349
  });
310
350
 
@@ -319,7 +359,7 @@ const countSprinkleTime = async (entry) => {
319
359
  if ((entry.onOffTimeOff > 0) && !(entry.count % entry.onOffTimeOn)) {
320
360
  adapter.log.info(`Intervall-Beregnung, timeOn: ${entry.onOffTimeOn}s, count: ${entry.count}s, timeOff: ${entry.onOffTimeOff}s, count % onOffTime: ${entry.count % entry.onOffTimeOn}`);
321
361
  const _setValveOnOff = await setValve(entry, false);
322
- if (_setValveOnOff === entry.idState) {
362
+ if (_setValveOnOff === entry.control.idState) {
323
363
  entry.myBreak = true;
324
364
  /* Zustand des Ventils im Thread <<< 3 = Pause >>> (0:off; 1:wait; 2:on; 3:break; 4:Boost(on); 5:off(Boost); 6:Cistern empty; 7:extBreak) */
325
365
  entry.state = 'break';
@@ -464,16 +504,28 @@ const updateList = async () => {
464
504
  0;
465
505
  }
466
506
 
467
- // ermitteln von curPipe und der Anzahl der parallelen Stränge
507
+ // ermitteln der maximalen Laufzeit aller Ventile in der threadList
508
+ const sumOfWateringTime = Math.ceil(threadList.reduce(
509
+ (sum, entry) => sum + entry.wateringTime + (switchingDistanceMS / 1000), (currentPumpUse.leadTime + switchingDistanceMS) / 1000));
510
+
511
+ // Anpassung ON_Time wenn Laufzeit zu kurz
512
+ const adjustment = sumOfWateringTime > controlVoltage.wateringTime
513
+ && adapter.config.switchingBehavior === 'homematic'
514
+ ? true
515
+ : false;
516
+
468
517
  consumption = await currentConsumption(true);
469
518
  if (consumption.pumpRequired === true) { // Pumpe erforderlich
470
519
 
471
520
  // Spannungsversorgung einschalten bei Leistungsanforderung
472
521
  try {
473
522
  if (adapter.config.triggerControlVoltage
474
- && controlVoltage.enable === false) {
523
+ && controlVoltage.enable === false
524
+ || adjustment === true
525
+ ) {
526
+ controlVoltage.wateringTime = sumOfWateringTime > controlVoltage.wateringTime ? sumOfWateringTime + 5 : controlVoltage.wateringTime; // Schaltzeit der 24V Versorgung entsprechend der maximalen Laufzeit aller Ventile in der threadList anpassen
475
527
  const _controlVoltage = await setValve(controlVoltage, true);
476
- if (_controlVoltage === controlVoltage.idState) {
528
+ if (_controlVoltage === controlVoltage.control.idState) {
477
529
  adapter.setStateAsync(`info.supplyVoltage`, {
478
530
  val: controlVoltage.enable,
479
531
  ack: true
@@ -482,15 +534,18 @@ const updateList = async () => {
482
534
  }
483
535
  }
484
536
  } catch (error) {
485
- adapter.log.error(`Error trigger Control Voltage [${controlVoltage.idState}]: ${error}`);
537
+ adapter.log.error(`Error trigger Control Voltage [${controlVoltage.control.idState}]: ${error}`);
486
538
  }
487
539
 
488
540
  // Pumpe einschalten bei Leistungsanforderung
489
541
  try {
490
542
  if (adapter.config.pumpSelection !== 'noPump'
491
- && currentPumpUse.enable === false) {
543
+ && currentPumpUse.enable === false
544
+ || adjustment === true
545
+ ) {
546
+ currentPumpUse.wateringTime = sumOfWateringTime > currentPumpUse.wateringTime ? sumOfWateringTime : currentPumpUse.wateringTime; // Schaltzeit der Pumpe entsprechend der maximalen Laufzeit aller Ventile in der threadList anpassen
492
547
  const _currentPumpUse = await setValve(currentPumpUse, true);
493
- if (_currentPumpUse && _currentPumpUse === currentPumpUse.idState) {
548
+ if (_currentPumpUse && _currentPumpUse === currentPumpUse.control.idState) {
494
549
  timerOn = false;
495
550
  adapter.setStateAsync(currentPumpUse.id, {
496
551
  val: currentPumpUse.enable,
@@ -500,7 +555,7 @@ const updateList = async () => {
500
555
  }
501
556
  }
502
557
  } catch (error) {
503
- adapter.log.error(`Error trigger current Pump [${currentPumpUse.idState}]: ${error}`);
558
+ adapter.log.error(`Error trigger current Pump [${currentPumpUse.control.idState}]: ${error}`);
504
559
  }
505
560
  }
506
561
 
@@ -518,7 +573,7 @@ const updateList = async () => {
518
573
  ) {
519
574
  entry.state = 'wait'; //1
520
575
  const _setValve2 = await setValve(entry, false);
521
- if (_setValve2 === entry.idState) {
576
+ if (_setValve2 === entry.control.idState) {
522
577
  consumption = await currentConsumption(true);
523
578
  clearInterval(entry.countdown); // Zähler für Countdown, Verbrauchsmengen, usw. löschen
524
579
  entry.countdown = null;
@@ -536,7 +591,7 @@ const updateList = async () => {
536
591
  }
537
592
  }
538
593
 
539
- adapter.log.info(`curFlow: ${consumption.curFlow}, parallel: ${consumption.parallel}`);
594
+ adapter.log.debug(`curFlow: ${consumption.curFlow}, parallel: ${consumption.parallel}`);
540
595
  // absteigend sortieren nach der Verbrauchsmenge
541
596
  threadList.sort(mySortDescending);
542
597
 
@@ -547,6 +602,7 @@ const updateList = async () => {
547
602
  && !entry.killSprinkle // && Aufgabe noch nicht erledigt
548
603
  && !entry.myBreak // && nicht in der Pause Interval-Beregnung
549
604
  && !entry.extBreak // && nicht in der externen Pause (extBreak)
605
+ && !timeBasedRestrictionEn // && nicht in der zeitlichen Bewässerungsbeschränkung
550
606
  && (consumption.curFlow >= entry.pipeFlow) // && noch genügend Förderleistung der Pumpe
551
607
  && (consumption.parallel < maxParallel) // && maxParallel noch nicht erreicht
552
608
  && !boostOn // && Ventile nur einschalten, wenn kein Boost aktive
@@ -557,7 +613,7 @@ const updateList = async () => {
557
613
  timerOn === true ? await asyncTime.setTimeout(switchingDistanceMS, undefined, {signal: entry.ac.acUpdateListOn.signal}) : timerOn = true;
558
614
  entry.state = myConfig.config[entry.sprinkleID].booster ? 'boost(on)' : 'on'; // state 4 => boost(on) : 2 => on
559
615
  const _setValve3 = await setValve(entry, true);
560
- if (_setValve3 === entry.idState) {
616
+ if (_setValve3 === entry.control.idState) {
561
617
  consumption = await currentConsumption(true);
562
618
  if (myConfig.config[entry.sprinkleID].booster) {
563
619
  boostReady = false;
@@ -577,6 +633,7 @@ const updateList = async () => {
577
633
  }
578
634
  } else {
579
635
  entry.ac.acUpdateListOn.abort();
636
+ entry.killSprinkle = true;
580
637
  }
581
638
  }
582
639
  } catch (error) { //Fehler beim Einschalten der Bewässerungsventile
@@ -595,7 +652,7 @@ const updateList = async () => {
595
652
  ){
596
653
  entry.state = 'off(Boost)'; //state => 5/off(boost)
597
654
  const _setValve4 = await setValve(entry, false);
598
- if (_setValve4 === entry.idState){
655
+ if (_setValve4 === entry.control.idState){
599
656
  consumption = await currentConsumption(true);
600
657
  clearInterval(entry.countdown);
601
658
  entry.countdown = null;
@@ -617,7 +674,7 @@ const updateList = async () => {
617
674
  if (currentPumpUse.enable === true) {
618
675
  try {
619
676
  const _currentPumpUse = await setValve(currentPumpUse, false);
620
- if (_currentPumpUse === currentPumpUse.idState) {
677
+ if (_currentPumpUse === currentPumpUse.control.idState) {
621
678
  adapter.setStateAsync(currentPumpUse.id,{
622
679
  val: currentPumpUse.enable,
623
680
  ack: true
@@ -633,7 +690,7 @@ const updateList = async () => {
633
690
  if(controlVoltage.enable === true) {
634
691
  try {
635
692
  const _controlVoltage = await setValve(controlVoltage, false);
636
- if (_controlVoltage === controlVoltage.idState) {
693
+ if (_controlVoltage === controlVoltage.control.idState) {
637
694
  adapter.setStateAsync(`info.supplyVoltage`, {
638
695
  val: controlVoltage.enable,
639
696
  ack: true
@@ -642,7 +699,7 @@ const updateList = async () => {
642
699
 
643
700
  }
644
701
  } catch (error) {
645
- adapter.log.error(`Error trigger Control Voltage [${controlVoltage.idState}]: ${error}`);
702
+ adapter.log.error(`Error trigger Control Voltage [${controlVoltage.control.idState}]: ${error}`);
646
703
  }
647
704
  }
648
705
  }
@@ -742,7 +799,7 @@ const setActualPump = async () => {
742
799
  if ((fillLevelCistern < parseFloat(adapter.config.triggerMinCisternLevel)) && (currentPumpUse.pumpCistern === true)) {
743
800
  /* (Zisterne unter Minimum) && (ZisternenPumpe läuft) */
744
801
  const _setValveCisternPumpOff = await setValve(currentPumpUse, false); // Pumpe Zisterne Aus
745
- if (_setValveCisternPumpOff === currentPumpUse.idState) {
802
+ if (_setValveCisternPumpOff === currentPumpUse.control.idState) {
746
803
  await adapter.setStateAsync(currentPumpUse.id, { // set State cisternPump === false
747
804
  val: currentPumpUse.enable,
748
805
  ack: true
@@ -750,7 +807,7 @@ const setActualPump = async () => {
750
807
  }
751
808
  currentPumpUse.pumpCistern = false;
752
809
  currentPumpUse.name = 'Main pump';
753
- currentPumpUse.idState = adapter.config.triggerMainPump || '';
810
+ currentPumpUse.control = { ...mainPumpControl };
754
811
  currentPumpUse.id = 'info.mainPump';
755
812
  currentPumpUse.pumpPower = parseInt(adapter.config.triggerMainPumpPower);
756
813
  /* Hauptpumpe Ein */
@@ -778,7 +835,7 @@ const setActualPump = async () => {
778
835
  currentPumpUse.pumpCistern = true;
779
836
  currentPumpUse.name = 'Cistern pump';
780
837
  currentPumpUse.id = 'info.cisternPump';
781
- currentPumpUse.idState = adapter.config.triggerCisternPump || '';
838
+ currentPumpUse.control = { ...cisternPumpControl };
782
839
  currentPumpUse.pumpPower = parseInt(adapter.config.triggerCisternPumpPower);
783
840
 
784
841
  } else {
@@ -789,7 +846,7 @@ const setActualPump = async () => {
789
846
  currentPumpUse.pumpCistern = false;
790
847
  currentPumpUse.name = 'Main pump';
791
848
  currentPumpUse.id = 'info.mainPump';
792
- currentPumpUse.idState = adapter.config.triggerMainPump || '';
849
+ currentPumpUse.control = { ...mainPumpControl };
793
850
  currentPumpUse.pumpPower = parseInt(adapter.config.triggerMainPumpPower);
794
851
  }
795
852
  adapter.setState('control.restFlow', {
@@ -829,10 +886,10 @@ function addConsumedAndTime(entry) {
829
886
  ack: true
830
887
  });
831
888
  adapter.setState(`sprinkle.${entry.name}.history.lastRunningTime`, {
832
- val: addTime(entry.count, ''),
889
+ val: tools.addTime(entry.count, ''),
833
890
  ack: true
834
891
  });
835
- const _formatTime = formatTime(entry.startTime);
892
+ const _formatTime = tools.formatTime(entry.startTime);
836
893
  adapter.setState(`sprinkle.${entry.name}.history.lastOn`, {
837
894
  val: _formatTime.dayTime,
838
895
  ack: true
@@ -848,7 +905,7 @@ function addConsumedAndTime(entry) {
848
905
  adapter.getState(`sprinkle.${entry.name}.history.curCalWeekRunningTime`, (err, state) => {
849
906
  if (state && state.val) {
850
907
  adapter.setState(`sprinkle.${entry.name}.history.curCalWeekRunningTime`, {
851
- val: addTime(+state.val, entry.count),
908
+ val: tools.addTime(+state.val, entry.count),
852
909
  ack: true
853
910
  });
854
911
  }
@@ -883,9 +940,13 @@ const valveControl = {
883
940
  : (+adapter.config.switchingDistance)
884
941
  );
885
942
  adapter.log.info(`switchingDistanceMS: ${switchingDistanceMS}ms`);
943
+
886
944
  /* Object supplyVoltage (VersorgungsSpannung) anlegen */
887
945
  if (adapter.config.triggerControlVoltage.length > 5) {
888
- controlVoltage.idState = adapter.config.triggerControlVoltage; // Schaltaktor zuweisen
946
+
947
+ controlVoltage.control = await tools.idStateControl(adapter, adapter.config.triggerControlVoltage);
948
+ if (controlVoltage?.control?.idACK) adapter.subscribeForeignStates(controlVoltage.control.idACK);
949
+
889
950
  await adapter.setObjectNotExistsAsync(`info.supplyVoltage`, {
890
951
  type: 'state',
891
952
  common: {
@@ -977,11 +1038,12 @@ const valveControl = {
977
1038
  };
978
1039
 
979
1040
  switch (adapter.config.pumpSelection) {
1041
+
980
1042
  case 'noPump': {
981
1043
  currentPumpUse.pumpCistern = false;
982
1044
  currentPumpUse.name = 'no pump';
983
1045
  currentPumpUse.intBreak = false;
984
- currentPumpUse.idState = '';
1046
+ currentPumpUse.control.idState = '';
985
1047
  currentPumpUse.id = '';
986
1048
  currentPumpUse.leadTime = 0;
987
1049
  currentPumpUse.pumpPower = parseInt(adapter.config.triggerMainPumpPower) || 0;
@@ -992,11 +1054,14 @@ const valveControl = {
992
1054
  if (_cisternPump) await adapter.delObjectAsync(`info.cisternPump`);
993
1055
  break;
994
1056
  }
1057
+
995
1058
  case 'mainPump': {
1059
+ mainPumpControl = await tools.idStateControl(adapter, adapter.config.triggerMainPump);
1060
+ currentPumpUse.control = mainPumpControl;
1061
+ if (currentPumpUse?.control?.idACK) adapter.subscribeForeignStates(currentPumpUse.control.idACK);
996
1062
  currentPumpUse.pumpCistern = false;
997
1063
  currentPumpUse.name = 'Main pump';
998
1064
  currentPumpUse.intBreak = false;
999
- currentPumpUse.idState = adapter.config.triggerMainPump || '';
1000
1065
  currentPumpUse.id = 'info.mainPump';
1001
1066
  currentPumpUse.leadTime = (parseInt(adapter.config.mainPumpLeadTime) || 5) * 1000;
1002
1067
  currentPumpUse.pumpPower = parseInt(adapter.config.triggerMainPumpPower) || 0;
@@ -1007,6 +1072,7 @@ const valveControl = {
1007
1072
  if (_cisternPump) await adapter.delObjectAsync(`info.cisternPump`);
1008
1073
  break;
1009
1074
  }
1075
+
1010
1076
  case 'cistern': {
1011
1077
  /* Füllstand der Zisterne in % holen */
1012
1078
  const _actualValueLevel = await adapter.getForeignStateAsync(adapter.config.actualValueLevel);
@@ -1030,9 +1096,12 @@ const valveControl = {
1030
1096
  fillLevelCistern = 0;
1031
1097
  currentPumpUse.intBreak = true;
1032
1098
  }
1099
+
1100
+ cisternPumpControl = await tools.idStateControl(adapter, adapter.config.triggerCisternPump);
1101
+ currentPumpUse.control = cisternPumpControl;
1102
+ if (currentPumpUse?.control?.idACK) adapter.subscribeForeignStates(currentPumpUse.control.idACK);
1033
1103
  currentPumpUse.pumpCistern = false;
1034
1104
  currentPumpUse.name = 'Cistern pump';
1035
- currentPumpUse.idState = adapter.config.triggerCisternPump || '';
1036
1105
  currentPumpUse.id = 'info.cisternPump';
1037
1106
  currentPumpUse.leadTime = (parseInt(adapter.config.cisternPumpLeadTime) || 5) * 1000;
1038
1107
  currentPumpUse.pumpPower = parseInt(adapter.config.triggerCisternPumpPower) || 0;
@@ -1043,8 +1112,13 @@ const valveControl = {
1043
1112
  if (_mainPump) await adapter.delObjectAsync(`info.mainPump`);
1044
1113
  break;
1045
1114
  }
1115
+
1046
1116
  case 'pumpAndCistern': {
1047
1117
  currentPumpUse.enable = false;
1118
+ mainPumpControl = await tools.idStateControl(adapter, adapter.config.triggerMainPump);
1119
+ cisternPumpControl = await tools.idStateControl(adapter, adapter.config.triggerCisternPump);
1120
+ if (mainPumpControl?.idACK) adapter.subscribeForeignStates(mainPumpControl.idACK);
1121
+ if (cisternPumpControl?.idACK) adapter.subscribeForeignStates(cisternPumpControl.idACK);
1048
1122
  if (adapter.config.actualValueLevel) {
1049
1123
  /* Füllstand der Zisterne in % holen */
1050
1124
  const _actualValueLevel = await adapter.getForeignStateAsync(adapter.config.actualValueLevel);
@@ -1058,19 +1132,21 @@ const valveControl = {
1058
1132
  adapter.log.warn(`level sensor in the cistern => Wrong value: ${_actualValueLevel.val}, Type: ${typeof _actualValueLevel.val}`);
1059
1133
  }
1060
1134
  }
1061
-
1135
+
1062
1136
  if (fillLevelCistern > parseInt(adapter.config.triggerMinCisternLevel)) {
1137
+ /* Zisterne voll */
1063
1138
  currentPumpUse.pumpCistern = true;
1064
1139
  currentPumpUse.name = 'Cistern pump';
1065
- currentPumpUse.idState = adapter.config.triggerCisternPump || '';
1140
+ currentPumpUse.control = { ...cisternPumpControl };
1066
1141
  currentPumpUse.id = 'info.cisternPump';
1067
1142
  currentPumpUse.leadTime = (parseInt(adapter.config.cisternPumpLeadTime) || 5) * 1000;
1068
1143
  currentPumpUse.pumpPower = parseInt(adapter.config.triggerCisternPumpPower) || 0;
1069
1144
  currentPumpUse.restFlow = parseInt(adapter.config.triggerCisternPumpPower) || 0;
1070
1145
  } else {
1146
+ /* Zisterne leer */
1071
1147
  currentPumpUse.pumpCistern = false;
1072
1148
  currentPumpUse.name = 'Main pump';
1073
- currentPumpUse.idState = adapter.config.triggerMainPump || '';
1149
+ currentPumpUse.control = { ...mainPumpControl };
1074
1150
  currentPumpUse.id = 'info.mainPump';
1075
1151
  currentPumpUse.leadTime = (parseInt(adapter.config.mainPumpLeadTime) || 5) * 1000;
1076
1152
  currentPumpUse.pumpPower = parseInt(adapter.config.triggerMainPumpPower) || 0;
@@ -1106,29 +1182,31 @@ const valveControl = {
1106
1182
  /* Pumpe ausschalter, wenn vorhanden */
1107
1183
  if (adapter.config.triggerMainPump !== '') {
1108
1184
  const _triggerMainPump = await adapter.getStateAsync('adapter.config.triggerMainPump');
1109
- if (_triggerMainPump && _triggerMainPump.val && _triggerMainPump.val === true) {
1110
- adapter.setStateAsync(adapter.config.triggerMainPump, {
1185
+ if (_triggerMainPump?.val === true) {
1186
+ await adapter.setStateAsync(adapter.config.triggerMainPump, {
1111
1187
  val: false,
1112
1188
  ack: false
1113
1189
  });
1114
- adapter.setStateAsync('info.mainPump', {
1190
+ await adapter.setStateAsync('info.mainPump', {
1115
1191
  val:false,
1116
1192
  ack:false
1117
1193
  });
1194
+ await asyncTime.setTimeout(100, undefined, undefined); // kurze Wartezeit, damit die Hauptpumpe sicher ausschalten kann
1118
1195
  }
1119
1196
  }
1120
1197
  /* Pumpe (Zisterne) ausschalter, wenn vorhanden */
1121
1198
  if (adapter.config.triggerCisternPump !== '') {
1122
1199
  const _triggerCisternPump = await adapter.getStateAsync('adapter.config.triggerCisternPump');
1123
- if (_triggerCisternPump && _triggerCisternPump.val && _triggerCisternPump.val === true) {
1124
- adapter.setState(adapter.config.triggerCisternPump, {
1200
+ if (_triggerCisternPump?.val === true) {
1201
+ await adapter.setStateAsync(adapter.config.triggerCisternPump, {
1125
1202
  val: false,
1126
1203
  ack: false
1127
1204
  });
1128
- adapter.setStateAsync('info.cisternPump', {
1205
+ await adapter.setStateAsync('info.cisternPump', {
1129
1206
  val:false,
1130
1207
  ack:false
1131
1208
  });
1209
+ await asyncTime.setTimeout(100, undefined, undefined); // kurze Wartezeit, damit die Zisterne sicher ausschalten kann
1132
1210
  }
1133
1211
  }
1134
1212
  /* alle Ventile (.name = "hm-rpc.0.MEQ1234567.3.STATE") in einem definierten Zustand (false) versetzen*/
@@ -1148,6 +1226,7 @@ const valveControl = {
1148
1226
  val: false,
1149
1227
  ack: false
1150
1228
  });
1229
+ await asyncTime.setTimeout(100, undefined, undefined); // kurze Wartezeit, damit die Ventile sicher ausschalten können
1151
1230
  }
1152
1231
  /*Kontrolle Flow Pumpe > Flow Ventil*/
1153
1232
  if (testMaxFlow < parseInt(res.pipeFlow)) {
@@ -1172,6 +1251,7 @@ const valveControl = {
1172
1251
  */
1173
1252
  addList: async function (sprinkleList) {
1174
1253
  try {
1254
+ // kontrolle bei ausgeschalteter Pumpe, ob die Zisterne zur Bewässerung genutzt werden kann
1175
1255
  if (adapter.config.pumpSelection === 'pumpAndCistern'
1176
1256
  && currentPumpUse.enable === false
1177
1257
  && fillLevelCistern > parseFloat(adapter.config.triggerMinCisternLevel)
@@ -1193,14 +1273,24 @@ const valveControl = {
1193
1273
  if (entry.wateringTime === res.wateringTime) {
1194
1274
  return;
1195
1275
  }
1196
- entry.wateringTime = res.wateringTime;
1276
+ // wenn der Sprenger aktiv ist und die Bewässerungszeit verlängert werden muss
1277
+ if (entry.enable === true
1278
+ && adapter.config.switchingBehavior === 'homematic'
1279
+ && entry.wateringTime < res.wateringTime
1280
+ ) {
1281
+ entry.wateringTime = res.wateringTime;
1282
+ const _setValve2 = await setValve(entry, true);
1283
+ if (_setValve2 !== entry.control.idState) throw new Error(`update ID: ${entry.name} => An error occurred while updating the irrigation time for Homematic devices. Please check the connection to the device and the configuration of the switching behavior.`);
1284
+ } else {
1285
+ entry.wateringTime = res.wateringTime;
1286
+ adapter.log.debug(`update ID: ${entry.name} new time: ${tools.addTime(res.wateringTime, '')}`);
1287
+ }
1197
1288
  entry.autoOn = res.auto; // auto: = true autostart; = false Handbetrieb
1198
1289
  await adapter.setStateAsync(`sprinkle.${name}.runningTime`, {
1199
- val: addTime(res.wateringTime, ''),
1290
+ val: tools.addTime(res.wateringTime, ''),
1200
1291
  ack: true
1201
1292
  });
1202
1293
  addDone = true; // Sprinkle found
1203
- adapter.log.debug(`update ID: ${entry.name} new time: ${addTime(res.wateringTime, '')}`);
1204
1294
  break;
1205
1295
  }
1206
1296
  }
@@ -1214,10 +1304,11 @@ const valveControl = {
1214
1304
  });
1215
1305
  return;
1216
1306
  }
1307
+
1217
1308
  const newThread = {};
1218
1309
  newThread.sprinkleID = res.sprinkleID; // Array[0...]
1219
- newThread.name = name; // z.B "Blumenbeet"
1220
- newThread.idState = myConfig.config[res.sprinkleID].idState; // z.B. "hm-rpc.0.MEQ1810129.1.STATE"
1310
+ newThread.name = name; // z.B "Blumenbeet"
1311
+ newThread.control = { ...myConfig.config[res.sprinkleID].control }; // {idState:, idON_TIME:, idACK:, maker:}
1221
1312
  newThread.wateringTime = res.wateringTime; // Bewässerungszeit
1222
1313
  newThread.pipeFlow = myConfig.config[res.sprinkleID].pipeFlow; // Wasserverbrauch
1223
1314
  newThread.count = 0; // Zähler im Sekundentakt
@@ -1229,7 +1320,7 @@ const valveControl = {
1229
1320
  /** 0:"off", 1:"wait", 2:"on", 3:"break", 4:"Boost(on)", 5:"off(Boost)", 6:"Cistern empty", 7:"extBreak" */
1230
1321
  newThread.state = 'wait'; // zustand des Ventils Softwaremäßig
1231
1322
  newThread.enable = false; // zustand des Ventil softwaremäßig
1232
- newThread.myBreak = false; // meine Pause
1323
+ newThread.myBreak = false; // meine interne Pause
1233
1324
  newThread.extBreak = myConfig.config[res.sprinkleID].extBreak; // extBreak wird über .sprinkle.*.extBreak geschaltet
1234
1325
  newThread.killSprinkle = false; // Löschauftrag ausführen am Ende in threadList
1235
1326
  newThread.litersPerSecond = myConfig.config[res.sprinkleID].pipeFlow / 3600; // Wasserverbrauchsmenge pro Sekunde
@@ -1241,15 +1332,16 @@ const valveControl = {
1241
1332
  newThread.id = threadList.length || 0;
1242
1333
  threadList.push(newThread);
1243
1334
  /* Zustand des Ventils im Thread < 0 > off, <<< 1 >>> wait, < 2 > on, < 3 > break, < 4 > Boost(on), < 5 > off(Boost), < 6 > Cistern empty, < 7 > extBreak */
1244
- newThread.extBreak ? (newThread.state = 'extBreak') : (newThread.state = 'wait');
1335
+ newThread.extBreak ? (newThread.state = 'extBreak') : timeBasedRestrictionEn === true ? (newThread.state = 'Irrigation ban') : (newThread.state = 'wait');
1245
1336
  adapter.setState(`sprinkle.${name}.sprinklerState`, {
1246
1337
  val: newThread.state,
1247
1338
  ack: true
1248
1339
  });
1249
1340
  adapter.setState(`sprinkle.${name}.runningTime`, {
1250
- val: addTime(res.wateringTime, ''),
1341
+ val: tools.addTime(res.wateringTime, ''),
1251
1342
  ack: true
1252
1343
  });
1344
+
1253
1345
  adapter.log.debug(`ID: ${name} new order created: ${JSON.stringify(threadList[newThread.id])}`);
1254
1346
  }
1255
1347
  }
@@ -1260,6 +1352,57 @@ const valveControl = {
1260
1352
 
1261
1353
  }, // End addList
1262
1354
 
1355
+ timeBasedRestriction: async function (enable) { //Irrigation ban => zeitliche Bewässerungsbeschränkung noch zu bearbeiten ! ! !
1356
+ try {
1357
+ timeBasedRestrictionEn = enable;
1358
+ if (threadList) {
1359
+ if (timeBasedRestrictionEn === true) { // zeitliche Bewässerungsbeschränkung aktiv
1360
+ for await (const entry of threadList) {
1361
+ if (entry.enable === true) { // Ventil ist aktiv, dann Ventil ausschalten
1362
+ const _setValve = await setValve(entry, false);
1363
+ if (_setValve === entry.control.idState) {
1364
+ entry.state = 'Irrigation ban';
1365
+ clearInterval(entry.countdown); // Timer Countdown löschen
1366
+ entry.countdown = null;
1367
+ /* Booster zurücksetzen falls aktiv*/
1368
+ if (myConfig.config[entry.sprinkleID].booster) {
1369
+ entry.ac.acBoostOnTimer.abort(entry.extBreak);
1370
+ boostReady = true;
1371
+ boostOn = false;
1372
+ adapter.log.debug(`ID: ${entry.name} UpdateList Sprinkle Off: boostReady = ${boostReady}`);
1373
+ }
1374
+ } else {
1375
+ if(!sendMessageText.onlySendError()){
1376
+ sendMessageText.sendMessage(`The valve, ${entry.name}, could not be deactivated. Please turn it off manually.`);
1377
+ }
1378
+ adapter.log.warn(`The valve, ${entry.name}, could not be deactivated. Please turn it off manually.`);
1379
+ entry.state = 'undefined';
1380
+ }
1381
+ } else {
1382
+ entry.state = 'Irrigation ban';
1383
+ }
1384
+ adapter.setStateAsync(`sprinkle.${entry.name}.sprinklerState`, {
1385
+ val: entry.state,
1386
+ ack: true
1387
+ });
1388
+ }
1389
+ } else {
1390
+ for await (const entry of threadList) { // zeitliche Bewässerungsbeschränkung beendet
1391
+ entry.state = (entry.extBreak === true) ? 'extBreak' : 'wait';
1392
+ adapter.setStateAsync(`sprinkle.${entry.name}.sprinklerState`, {
1393
+ val: entry.state,
1394
+ ack: true
1395
+ });
1396
+ }
1397
+ }
1398
+ }
1399
+ updateList();
1400
+ return true;
1401
+ } catch (error) {
1402
+ adapter.log.warn(`TimeBasedRestriction: ${error}`);
1403
+ return false;
1404
+ }
1405
+ },
1263
1406
  /**
1264
1407
  * extBreak => Spränger unterbrechen bei extBreak
1265
1408
  *
@@ -1269,7 +1412,7 @@ const valveControl = {
1269
1412
  */
1270
1413
  extBreak: async function (sprinkleID, extBreak) {
1271
1414
  try {
1272
- const _found = await findAsync(threadList, async (d) => {
1415
+ const _found = await tools.findAsync(threadList, async (d) => {
1273
1416
  return Promise.resolve (d.sprinkleID === sprinkleID);
1274
1417
  });
1275
1418
 
@@ -1278,7 +1421,7 @@ const valveControl = {
1278
1421
  if (extBreak === true) { // extBreak aktiv
1279
1422
  if (_found.enable === true) { // Ventil ist eingeschaltet
1280
1423
  const _setValve = await setValve(_found, false);
1281
- if (_setValve === _found.idState) {
1424
+ if (_setValve === _found.control.idState) {
1282
1425
  _found.state = 'extBreak'; // <<< 7 >>> extBreak
1283
1426
  clearInterval(_found.countdown); // Timer Countdown löschen
1284
1427
  _found.countdown = null;
@@ -1304,13 +1447,22 @@ const valveControl = {
1304
1447
  });
1305
1448
  adapter.log.debug(`valveControl.extBreak: ${extBreak}, Name: ${_found.name}, ID: ${_found.sprinkleID}`); // löschen
1306
1449
  updateList();
1307
- return {val: true};
1450
+ return {
1451
+ name: _found.name,
1452
+ val: true
1453
+ };
1308
1454
  } else {
1309
- return {val: false};
1455
+ return {
1456
+ name: null,
1457
+ val: false
1458
+ };
1310
1459
  }
1311
1460
  } catch (error) {
1312
1461
  adapter.log.warn(`valveControl.extBreak(${sprinkleID}, ${extBreak}) => ${error}`);
1313
- return {val: false};
1462
+ return {
1463
+ name: null,
1464
+ val: false
1465
+ };
1314
1466
  }
1315
1467
  },
1316
1468
 
@@ -1352,12 +1504,12 @@ const valveControl = {
1352
1504
  clearInterval(entry.countdown);
1353
1505
  entry.countdown = null;
1354
1506
  /* Timer abbrechen */
1355
- if (entry.ac.acSetValveCancelTimeout.aborted === false) entry.ac.acSetValveCancelTimeout.abort(); // setValve
1356
- if (entry.ac.acBoostOnTimer.aborted === false) entry.ac.acBoostOnTimer.abort(); // BoostOnTimer
1357
- if (entry.ac.acUpdateListPuOff.aborted === false) entry.ac.acUpdateListPuOff.abort(); // UpdateList Pumpe aus (Leistung zu gering beim Pumpenwechsel)
1358
- if (entry.ac.acUpdateListOn.aborted === false) entry.ac.acUpdateListOn.abort(); // UpdateList Ventile ein
1359
- if (entry.ac.acUpdateListBoostOn.aborted === false) entry.ac.acUpdateListBoostOn.abort(); // UpdateList Ausschalten der Ventile bei BoostOn
1360
- if (entry.ac.acOnOffTimeoutOff.aborted === false) entry.ac.acOnOffTimeoutOff.abort(); // OnOffTimeoutOff Ausschaltdauer bei on-off-Betrieb
1507
+ if (entry?.ac?.acSetValveCancelTimeout?.aborted === false) entry.ac.acSetValveCancelTimeout.abort(); // setValve
1508
+ if (entry?.ac?.acBoostOnTimer?.aborted === false) entry.ac.acBoostOnTimer.abort(); // BoostOnTimer
1509
+ if (entry?.ac?.acUpdateListPuOff?.aborted === false) entry.ac.acUpdateListPuOff.abort(); // UpdateList Pumpe aus (Leistung zu gering beim Pumpenwechsel)
1510
+ if (entry?.ac?.acUpdateListOn?.aborted === false) entry.ac.acUpdateListOn.abort(); // UpdateList Ventile ein
1511
+ if (entry?.ac?.acUpdateListBoostOn?.aborted === false) entry.ac.acUpdateListBoostOn.abort(); // UpdateList Ausschalten der Ventile bei BoostOn
1512
+ if (entry?.ac?.acOnOffTimeoutOff?.aborted === false) entry.ac.acOnOffTimeoutOff.abort(); // OnOffTimeoutOff Ausschaltdauer bei on-off-Betrieb
1361
1513
 
1362
1514
  adapter.log.debug(`order deleted Stop all ID: ${entry.name} ( rest orders: ${threadList.length} )`);
1363
1515
  threadList.pop(); // del last array