iobroker.sprinklecontrol 0.2.13 → 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 (71) hide show
  1. package/{Lizenz → LICENSE} +21 -21
  2. package/README.md +60 -110
  3. package/admin/i18n/de/translations.json +134 -0
  4. package/admin/i18n/en/translations.json +134 -0
  5. package/admin/i18n/es/translations.json +134 -0
  6. package/admin/i18n/fr/translations.json +134 -0
  7. package/admin/i18n/it/translations.json +134 -0
  8. package/admin/i18n/nl/translations.json +134 -0
  9. package/admin/i18n/pl/translations.json +134 -0
  10. package/admin/i18n/pt/translations.json +134 -0
  11. package/admin/i18n/ru/translations.json +134 -0
  12. package/admin/i18n/uk/translations.json +134 -0
  13. package/admin/i18n/zh-cn/translations.json +134 -0
  14. package/admin/index_m.html +904 -907
  15. package/admin/index_m.js +779 -779
  16. package/admin/style.css +453 -158
  17. package/admin/words.js +143 -136
  18. package/io-package.json +256 -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 -125
  24. package/lib/valveControl.js +918 -887
  25. package/main.js +2041 -1828
  26. package/package.json +50 -32
  27. package/.gitattributes +0 -2
  28. package/admin/admin.d.ts +0 -1
  29. package/docs/de/img/E-Mail.jpg +0 -0
  30. package/docs/de/img/Extraeinstellungen.jpg +0 -0
  31. package/docs/de/img/Pumpeneinstellung.jpg +0 -0
  32. package/docs/de/img/Pushover.jpg +0 -0
  33. package/docs/de/img/Select_ID.jpg +0 -0
  34. package/docs/de/img/Telegram.jpg +0 -0
  35. package/docs/de/img/Ventil-Haupteinstellung.jpg +0 -0
  36. package/docs/de/img/Ventil-Pumpeneinstellung.jpg +0 -0
  37. package/docs/de/img/WhatsApp.jpg +0 -0
  38. package/docs/de/img/Zeiteinstellung.jpg +0 -0
  39. package/docs/de/img/addTime.jpg +0 -0
  40. package/docs/de/img/analog.jpg +0 -0
  41. package/docs/de/img/ber-verdunstung.jpg +0 -0
  42. package/docs/de/img/bew-einstellung.jpg +0 -0
  43. package/docs/de/img/bew-feste-tage.jpg +0 -0
  44. package/docs/de/img/bistabil.jpg +0 -0
  45. package/docs/de/img/bodenf-analog.jpg +0 -0
  46. package/docs/de/img/bodenf-bistabil.jpg +0 -0
  47. package/docs/de/img/calculation.jpg +0 -0
  48. package/docs/de/img/control.jpg +0 -0
  49. package/docs/de/img/einschaltpunkt-giessen.jpg +0 -0
  50. package/docs/de/img/festeTage.jpg +0 -0
  51. package/docs/de/img/main.jpg +0 -0
  52. package/docs/de/img/main_tab.jpg +0 -0
  53. package/docs/de/img/max-bodenfeuchtigkeit.jpg +0 -0
  54. package/docs/de/img/schaltverhalten.jpg +0 -0
  55. package/docs/de/img/sprinklecontrol.png +0 -0
  56. package/docs/de/img/verdunstDiagra.jpg +0 -0
  57. package/docs/de/img/verdunstung.jpg +0 -0
  58. package/docs/de/img/zus-bew-einstellung.jpg +0 -0
  59. package/docs/de/sprinklecontrol.md +0 -656
  60. package/docs/en/img/schaltverhalten.jpg +0 -0
  61. package/docs/en/img/screenshot1.jpg +0 -0
  62. package/docs/en/img/screenshot2.jpg +0 -0
  63. package/docs/en/img/screenshot3.jpg +0 -0
  64. package/docs/en/img/screenshot4.jpg +0 -0
  65. package/docs/en/img/screenshot5.jpg +0 -0
  66. package/docs/en/img/screenshot6.jpg +0 -0
  67. package/docs/en/img/screenshot7.jpg +0 -0
  68. package/docs/en/img/screenshot8.jpg +0 -0
  69. package/docs/en/img/sprinklecontrol.png +0 -0
  70. package/docs/en/sprinklecontrol.md +0 -230
  71. package/main.test.js +0 -30
package/main.js CHANGED
@@ -1,1828 +1,2041 @@
1
- 'use strict';
2
-
3
- // Load your modules here, e.g.: => // Laden Sie Ihre Module hier, z.B.
4
- // const fs = require("fs");
5
-
6
- const utils = require('@iobroker/adapter-core');
7
- const schedule = require('node-schedule');
8
- const SunCalc = require('suncalc2');
9
-
10
- const sendMessageText = require('./lib/sendMessageText.js'); // sendMessageText
11
- const valveControl = require('./lib/valveControl.js'); // Steuerung der einzelnen Ventile
12
- const myConfig = require('./lib/myConfig.js'); // myConfig → Speichern und abrufen von Konfigurationsdaten der Ventile
13
- const evaporation = require('./lib/evaporation.js');
14
- const addTime = require('./lib/tools.js').addTime;
15
- const formatTime = require('./lib/tools.js').formatTime;
16
- //const trend = require('./lib/tools').trend;
17
-
18
- /**
19
- * The adapter instance
20
- * @type {ioBroker.Adapter}
21
- */
22
- let adapter;
23
- const adapterName = require('./package.json').name.split('.').pop();
24
-
25
- /** ext. Adapter Deutsche Feiertage
26
- * @type {any} */
27
- let publicHolidayStr;
28
- /** @type {any} */
29
- let publicHolidayTomorrowStr;
30
- /* Regenvorhersage - DasWetter.com */
31
- /** @type {string}
32
- * - Pfad zur Regenvorhersage in mm */
33
- let weatherForecastTodayPfadStr;
34
- /** @type {number}
35
- * - heutige Regenvorhersage in mm */
36
- let weatherForecastTodayNum = 0;
37
- /** @type {number}
38
- * - morgige Regenvorhersage in mm */
39
- let weatherForecastTomorrowNum = 0;
40
- /** @type {boolean}
41
- * - Externer Schalter für Zusatzbewässerung */
42
- let addStartTimeSwitch = false;
43
-
44
- /** @type {string} */
45
- let startTimeStr;
46
- /** @type {string} */
47
- let sunriseStr;
48
- /** @type {string} */
49
- let sunsetStr;
50
- /** @type {string} */
51
- let goldenHourEnd;
52
- /** switch => sprinklecontrol.*.control.Holiday
53
- * - Wenn (Holiday == true) ist, soll das Wochenendprogramm gefahren werden.
54
- * @type {any} */
55
- let holidayStr;
56
- /** @type {any} */
57
- let autoOnOffStr;
58
- /** @type {string} */
59
- let kwStr; // akt. KW der Woche
60
- /** @type {number | undefined} */
61
- let timer, timerSleep;
62
- /** @type {number} */
63
- let today; // heutige Tag 0:So;1:Mo...6:Sa
64
-
65
- /* memo */
66
- /** @type {{}} */
67
- let ObjSprinkle = {};
68
-
69
- /**
70
- * +++++++++++++++++++++++++++ Starts the adapter instance ++++++++++++++++++++++++++++++++
71
- *
72
- * @param {Partial<ioBroker.AdapterOptions>} [options]
73
- */
74
- function startAdapter(options) {
75
-
76
- options = options || {};
77
- Object.assign(options, { name: adapterName });
78
-
79
- adapter = new utils.Adapter(options);
80
-
81
- // start here!
82
- adapter.on('ready', () => {
83
- // init createConfig
84
- myConfig.createConfig(adapter);
85
- // Hauptpumpe zur Bewässerung setzen
86
- valveControl.initValveControl(adapter);
87
- main(adapter)
88
- });
89
-
90
- /**
91
- * +++++++++++++++++++++++++ is called when adapter shuts down +++++++++++++++++++++++++
92
- *
93
- * @param {() => void}callback
94
- */
95
- adapter.on('unload', (callback) => {
96
- /* Wird beim Stoppen nicht ausgeführt - Warum?
97
- if(adapter.config.notificationEnabled){
98
- sendMessageText.sendMessage('sprinklerControl is shutting down');
99
- adapter.log.info('sprinklerControl is shutting down');
100
- }
101
- */
102
- try {
103
- adapter.log.info('cleaned everything up...');
104
- clearTimeout(timer);
105
- clearTimeout(timerSleep);
106
- /*Startzeiten der Timer löschen*/
107
- schedule.cancelJob('calcPosTimer');
108
- schedule.cancelJob('sprinkleStartTime');
109
- schedule.cancelJob('sprinkleAddStartTime');
110
- /* alle Ventile und Aktoren deaktivieren */
111
- valveControl.clearEntireList();
112
-
113
- callback();
114
- } catch (e) {
115
- callback();
116
- }
117
- });
118
-
119
- /**
120
- * ++++++++++++++++++ Answers when getTelegramUser calls from index_m ++++++++++++++++++
121
- * -------------- Antwortet bei Aufrufen von getTelegramUser von index_m ---------------
122
- * @param {object} obj
123
- */
124
- adapter.on ('message', (obj) => {
125
- if (obj) {
126
- switch (obj.command) {
127
- case 'getTelegramUser':
128
- adapter.getForeignState(adapter.config.telegramInstance + '.communicate.users', (err, state) => {
129
- err && adapter.log.error(err);
130
- if (state && state.val) {
131
- try {
132
- adapter.log.debug('getTelegramUser:' + state.val);
133
- adapter.sendTo(obj.from, obj.command, JSON.parse(state.val), obj.callback);
134
- } catch (err) {
135
- err && adapter.log.error(err);
136
- adapter.log.error('Cannot parse stored user IDs from Telegram!');
137
- }
138
- }
139
- });
140
- break;
141
- }
142
- }
143
- });
144
-
145
- /**
146
- * ++++++++++++++++++ is called if a subscribed object changes ++++++++++++++++++
147
- * ---------- wird aufgerufen, wenn sich ein abonniertes Objekt ändert ----------
148
- * @param {string} id
149
- * @param {Object | null | undefined} obj
150
- */
151
- adapter.on('objectChange', (id, obj) => {
152
- if (obj) {
153
- adapter.log.info(`object ${id} changed: ${JSON.stringify(obj)}`);
154
- } else {
155
- // The object was deleted
156
- adapter.log.info(`object ${id} deleted`);
157
- }
158
- });
159
-
160
- /**
161
- * ++++++++++++++++++ is called if a subscribed state changes ++++++++++++++++++
162
- * --------- wird aufgerufen, wenn sich ein abonnierter Status ändert ----------
163
- * @param {string} id
164
- * @param {State | null | undefined} state
165
- */
166
- adapter.on('stateChange', (id, state) => {
167
- if (state) {
168
- // The state was changed → Der Zustand wurde geändert
169
- adapter.log.debug(`state ${id} changed: ${state.val} (ack = ${state.ack})`);
170
-
171
- // wenn (Holiday == true) ist, soll das Wochenendprogramm gefahren werden.
172
- if (id === adapter.namespace + '.control.Holiday' && state.ack === false) {
173
- holidayStr = state.val;
174
- adapter.setState(id, {
175
- val: state.val,
176
- ack: true
177
- });
178
- startTimeSprinkle();
179
- }
180
- // wenn (addStartTimeSwitch == true) wird die zusätzliche Bewässerung aktiviert
181
- if (id === `${adapter.namespace}.control.addStartTimeSwitch` && typeof state.val === 'boolean' && state.ack === false) {
182
- addStartTimeSwitch = state.val;
183
- adapter.setState(id, {
184
- val: state.val,
185
- ack: true
186
- });
187
- }
188
- // wenn (autoOnOff == false) so werden alle Sprenger nicht mehr automatisch gestartet.
189
- if ((id === adapter.namespace + '.control.autoOnOff') && (state.ack === false)) {
190
- autoOnOffStr = state.val;
191
- adapter.log.info(`startAdapter: control.autoOnOff: ${state.val}`);
192
- adapter.setState(id, {
193
- val: state.val,
194
- ack: true
195
- });
196
- if (!state.val) {
197
- valveControl.clearEntireList();
198
- }
199
- startTimeSprinkle();
200
- }
201
- // wenn (...sprinkleName.runningTime sich ändert) so wird der aktuelle Sprenger [sprinkleName]
202
- // bei == 0 gestoppt, > 1 gestartet
203
- if (myConfig.config && !state.ack) {
204
- const found = myConfig.config.find(d => d.objectID === id);
205
- if (found) {
206
- if (id === myConfig.config[found.sprinkleID].objectID) {
207
- if (!isNaN(state.val)) {
208
- valveControl.addList(
209
- [{
210
- auto: false, // Handbetrieb
211
- sprinkleID: found.sprinkleID,
212
- wateringTime: (state.val <= 0) ? state.val : Math.round(60 * state.val)
213
- }]);
214
- adapter.setState(id, {
215
- val: state.val,
216
- ack: true
217
- });
218
- }
219
- }
220
- }
221
- }
222
- // wenn in der config unter methodControlSM!== 'analog' oder 'bistable' eingegeben wurde, dann Bodenfeuchte-Sensor auslesen
223
- if (myConfig.config) {
224
- function filterByID(obj){
225
- return (((obj.methodControlSM === 'analog') || (obj.methodControlSM === 'bistable')) && (obj.triggerSM === id));
226
- }
227
- const filter = myConfig.config.filter(filterByID);
228
- if (filter) {
229
- for (const fil of filter) {
230
- if (id === myConfig.config[fil.sprinkleID].triggerSM){
231
- // analog
232
- if (fil.methodControlSM === 'analog') {
233
- myConfig.setSoilMoistPct(fil.sprinkleID, state.val);
234
- } else if (fil.methodControlSM === 'bistable') { // bistable
235
- myConfig.setSoilMoistBool(fil.sprinkleID, state.val);
236
- }
237
- }
238
- }
239
- }
240
- }
241
- // wenn (...sprinkleName.autoOn == false[off]) so wird der aktuelle Sprenger [sprinkleName]
242
- // bei false nicht automatisch gestartet
243
- if (myConfig.config && (typeof state.val === 'boolean') && (state.ack === false)) {
244
- const found = myConfig.config.find(d => d.autoOnID === id);
245
- if (found && id === myConfig.config[found.sprinkleID].autoOnID) {
246
- myConfig.config[found.sprinkleID].autoOn = state.val;
247
- adapter.setState(id, { // Bestätigung
248
- val: state.val,
249
- ack: true
250
- });
251
- adapter.log.info(`set ${found.objectName}.autoOn = ${state.val}, id: ${id}`);
252
- if (state.val === false) {
253
- valveControl.addList(
254
- [{
255
- auto: false,
256
- sprinkleID: found.sprinkleID,
257
- wateringTime: 0
258
- }]
259
- );
260
- }
261
- }
262
- }
263
-
264
- // postponeByOneDay → um einen Tag verschieben bei fixDay (twoNd & threeRd)
265
- let idSplit = id.split('.', 5);
266
- if (idSplit[4] === `postponeByOneDay` && state.ack === false) {
267
- const found = myConfig.config.find(d => d.objectName === idSplit[3]);
268
- if (found) {
269
- myConfig.postponeByOneDay(found.sprinkleID).catch((e) => {adapter.log.warn(`postponeByOneDay: ${e}`)});
270
- adapter.setState(id, {
271
- val: false,
272
- ack: true
273
- });
274
- }
275
- }
276
-
277
- // Change in outside temperature → Änderung der Außentemperatur
278
- if (id === adapter.config.sensorOutsideTemperature) { /*Temperatur*/
279
- if (!Number.isNaN(Number.parseFloat(state.val))) {
280
- evaporation.setCurTemperature(parseFloat(state.val), state.ts);
281
- } else {
282
- adapter.log.warn(`sensorOutsideTemperature => Wrong value: ${state.val}, Type: ${typeof state.val}`);
283
- }
284
- }
285
- // LuftFeuchtigkeit
286
- if (id === adapter.config.sensorOutsideHumidity) {
287
- if (!Number.isNaN(Number.parseFloat(state.val))) {
288
- evaporation.setCurHumidity(parseFloat(state.val), state.lc);
289
- } else {
290
- adapter.log.warn(`sensorOutsideHumidity => Wrong value: ${state.val}, Type: ${typeof state.val}`);
291
- }
292
- }
293
- // Helligkeit
294
- if (id === adapter.config.sensorBrightness) {
295
- if (!Number.isNaN(Number.parseFloat(state.val))) {
296
- evaporation.setCurIllumination(parseFloat(state.val), state.lc);
297
- } else {
298
- adapter.log.warn(`sensorBrightness => Wrong value: ${state.val}, Type: ${typeof state.val}`);
299
- }
300
- }
301
- // Windgeschwindigkeit
302
- if (id === adapter.config.sensorWindSpeed) {
303
- if (!Number.isNaN(Number.parseFloat(state.val))) {
304
- evaporation.setCurWindSpeed(parseFloat(state.val), state.lc);
305
- } else {
306
- adapter.log.warn(`sensorWindSpeed => Wrong value: ${state.val}, Type: ${typeof state.val}`);
307
- }
308
- }
309
- // Regencontainer
310
- // If the amount of rain is over 20 mm, the 'lastRainCounter' is overwritten and no calculation is carried out. =>
311
- // * Wenn die Regenmenge mehr als 20 mm beträgt, wird der 'lastRainCounter' überschrieben und es wird keine Berechnung durchgeführt.
312
- if (id === adapter.config.sensorRainfall) {
313
- if (!Number.isNaN(Number.parseFloat(state.val))) {
314
- evaporation.setCurAmountOfRain(parseFloat(state.val));
315
- } else {
316
- adapter.log.warn(`sensorRainfall => Wrong value: ${state.val}, Type: ${typeof state.val}`);
317
- }
318
- }
319
- // Feiertagskalender
320
- if (adapter.config.publicHolidays === true) {
321
- if (id === adapter.config.publicHolInstance + '.heute.boolean') {
322
- publicHolidayStr = state.val;
323
- startTimeSprinkle();
324
- }
325
- if (id === adapter.config.publicHolInstance + '.morgen.boolean') {
326
- publicHolidayTomorrowStr = state.val;
327
- startTimeSprinkle();
328
- }
329
- }
330
- // Wettervorhersage
331
- if (adapter.config.weatherForecast === true) {
332
- if (id === weatherForecastTodayPfadStr) {
333
- if (typeof state.val == 'string') {
334
- weatherForecastTodayNum = parseFloat(state.val);
335
- } else if (typeof state.val == 'number') {
336
- weatherForecastTodayNum = state.val;
337
- } else {
338
- weatherForecastTodayNum = 0;
339
- console.log.info('StateChange => Wettervorhersage state.val ( ' + state.val + '; ' + typeof state.val + ' ) kann nicht als Number verarbeitet werden');
340
- }
341
- adapter.setState('info.rainToday', {
342
- val: weatherForecastTodayNum,
343
- ack: true
344
- });
345
- }
346
- if (id === adapter.config.weatherForInstance + '.NextDaysDetailed.Location_1.Day_2.rain_value') {
347
- weatherForecastTomorrowNum = parseFloat(state.val);
348
- adapter.setState('info.rainTomorrow', {
349
- val: weatherForecastTomorrowNum,
350
- ack: true
351
- });
352
- }
353
- }
354
- // Füllstand der Zisterne bei Statusänderung
355
- if (adapter.config.actualValueLevel && (id === adapter.config.actualValueLevel)) {
356
- valveControl.setFillLevelCistern(parseFloat(state.val) || 0);
357
- //fillLevelCistern = state.val || 0;
358
- }
359
- }
360
- });
361
- }
362
-
363
- // +++++++++++++++++ Get longitude an latitude from system config ++++++++++++++++++++
364
- /**get longitude/latitude from system if not set or not valid
365
- * do not change if we have already a valid value
366
- * so we could use different settings compared to system if necessary
367
- * >>>
368
- * Längen- / Breitengrad vom System abrufen, falls nicht festgelegt oder ungültig
369
- * wird nicht geändert, wenn bereits ein gültiger Wert vorhanden ist,
370
- * daher können wir bei Bedarf andere Einstellungen als das System verwenden
371
- */
372
- async function GetSystemData() {
373
- if (typeof adapter.config.longitude === undefined || adapter.config.longitude == null || adapter.config.longitude.length === 0 || isNaN(adapter.config.longitude)
374
- || typeof adapter.config.latitude === undefined || adapter.config.latitude == null || adapter.config.latitude.length === 0 || isNaN(adapter.config.latitude)) {
375
-
376
- try {
377
- const obj = await adapter.getForeignObjectAsync('system.config');
378
-
379
- if (obj && obj.common && obj.common.longitude && obj.common.latitude) {
380
- adapter.config.longitude = obj.common.longitude;
381
- adapter.config.latitude = obj.common.latitude;
382
-
383
- adapter.log.debug(`longitude: ${adapter.config.longitude} | latitude: ${adapter.config.latitude}`);
384
- } else {
385
- adapter.log.error('system settings cannot be called up. Please check the geo data');
386
- }
387
- } catch (err) {
388
- adapter.log.warn('system settings cannot be called up. Please check configuration!');
389
- }
390
- }
391
- }
392
-
393
-
394
- /**
395
- * Schreiben des nächsten Starts in '.actualSoilMoisture'
396
- * oder Rückgabe des DayName für den nächsten Start
397
- * @param {number} sprinkleID - Number of Array[0...]
398
- * @param {boolean} returnOn - true = Rückgabe des Wochentags; false = Schreiben in State /.actualSoilMoisture
399
- * @returns {string} nextStart ['Sun','Mon','Tue','Wed','Thur','Fri','Sat']
400
- */
401
- function curNextFixDay (sprinkleID, returnOn) {
402
- const weekDayArray = myConfig.config[sprinkleID].startFixDay;
403
- const objPfad = 'sprinkle.' + myConfig.config[sprinkleID].objectName;
404
- const weekday = ['Sun','Mon','Tue','Wed','Thur','Fri','Sat'];
405
- let found = false;
406
- let curDay = formatTime(adapter, '', 'day');
407
- for ( let i=0; i<7; i++ ) {
408
- if (curDay > 6) {curDay = curDay - 7;}
409
- if (weekDayArray[curDay] === true) {
410
- found = true;
411
- if (returnOn) {
412
- return weekday[curDay];
413
- } else {
414
- adapter.setState(objPfad + '.actualSoilMoisture', {
415
- val: curDay,
416
- ack: true
417
- });
418
- }
419
- break;
420
- }
421
- curDay++;
422
- }
423
- if (returnOn && found === false) {return 'off'}
424
- }
425
-
426
- //
427
- /**
428
- * Sets the status at start to a defined value
429
- * → Setzt den Status beim Start auf einen definierten Wert
430
- */
431
- function checkStates() {
432
- /**
433
- * control.Holiday
434
- * @param {string|null} err
435
- * @param {ioBroker.State|null|undefined} state
436
- */
437
- adapter.getState('control.Holiday', (err, state) => {
438
- if (state && (state.val == null)) {
439
- adapter.setState('control.Holiday', {val: false, ack: true});
440
- }
441
- });
442
- /**
443
- * control.autoOnOff
444
- * @param {string|null} err
445
- * @param {ioBroker.State|null|undefined} state
446
- */
447
- adapter.getState('control.autoOnOff', (err, state) => {
448
- if (state && (typeof state.val === "boolean")) {
449
- autoOnOffStr = state.val;
450
- } else {
451
- autoOnOffStr = true;
452
- adapter.setState('control.autoOnOff', {
453
- val: autoOnOffStr,
454
- ack: true
455
- });
456
- }
457
- });
458
- adapter.getState('evaporation.ETpToday', (err, state) => {
459
- if (state && (state.val == null)) {
460
- evaporation.setETpTodayNum(0);
461
- adapter.setState('evaporation.ETpToday', {
462
- val: 0,
463
- ack: true
464
- });
465
- } else if (state) {
466
- evaporation.setETpTodayNum(parseFloat(state.val));
467
- }
468
- });
469
- adapter.getState('evaporation.ETpYesterday', (err, state) => {
470
- if (state && (state.val == null || state.val === false)) {
471
- adapter.setState('evaporation.ETpYesterday', {
472
- val: 0,
473
- ack: true
474
- });
475
- }
476
- });
477
-
478
- // akt. kW ermitteln für history last week
479
- kwStr = formatTime(adapter, '','kW');
480
- today = formatTime(adapter,'', 'day');
481
- }
482
-
483
- /**
484
- * aktuelle States checken nach dem Start (2000 ms) wenn alle Sprenger-Kreise angelegt wurden
485
- */
486
- async function checkActualStates () {
487
-
488
- try {
489
- /**
490
- * switch Holiday
491
- * @type {ioBroker.State | null}
492
- * @private
493
- */
494
- const _holiday = await adapter.getStateAsync('control.Holiday');
495
- if (_holiday && _holiday.val && typeof _holiday.val === "boolean") {
496
- holidayStr = _holiday.val;
497
- }
498
-
499
- /**
500
- * switch autoOnOff
501
- * @type {ioBroker.State | null}
502
- * @private
503
- */
504
- const _autoOnOff = await adapter.getStateAsync('control.autoOnOff');
505
- if (_autoOnOff && _autoOnOff.val && typeof _autoOnOff.val === "boolean") {
506
- autoOnOffStr = _autoOnOff.val;
507
- }
508
-
509
- if (adapter.config.publicHolidays === true && (adapter.config.publicHolInstance !== 'none' || adapter.config.publicHolInstance !== '')) {
510
- /**
511
- * Feiertag Heute
512
- * @type {ioBroker.State | void}
513
- * @private
514
- */
515
- const _publicHolInstanceHeute = await adapter.getForeignStateAsync(
516
- adapter.config.publicHolInstance + '.heute.boolean'
517
- ).catch((e) => adapter.log.warn(e));
518
- if (_publicHolInstanceHeute && _publicHolInstanceHeute.val) {
519
- publicHolidayStr = _publicHolInstanceHeute.val;
520
- }
521
- /**
522
- * Feiertag MORGEN
523
- * @type {ioBroker.State | void}
524
- * @private
525
- */
526
- const _publicHolInstanceMorgen = await adapter.getForeignStateAsync(
527
- adapter.config.publicHolInstance + '.morgen.boolean'
528
- ).catch((e) => adapter.log.warn(e));
529
- if (_publicHolInstanceMorgen && _publicHolInstanceMorgen.val) {
530
- publicHolidayTomorrowStr = _publicHolInstanceMorgen.val;
531
- }
532
- }
533
-
534
- if (adapter.config.weatherForecast === true && (adapter.config.weatherForInstance !== 'none' || adapter.config.weatherForInstance !== '')) {
535
- /**
536
- * Niederschlagsmenge HEUTE in mm
537
- * @type {ioBroker.State | void}
538
- * @private
539
- */
540
- const _weatherForInstanceToday = await adapter.getForeignStateAsync(
541
- weatherForecastTodayPfadStr
542
- ).catch((e) => adapter.log.warn(e));
543
- if (_weatherForInstanceToday && _weatherForInstanceToday.val) {
544
- if (typeof _weatherForInstanceToday.val == 'string') {
545
- weatherForecastTodayNum = parseFloat(_weatherForInstanceToday.val);
546
- } else if (typeof _weatherForInstanceToday.val == 'number') {
547
- weatherForecastTodayNum = _weatherForInstanceToday.val;
548
- } else {
549
- weatherForecastTodayNum = 0;
550
- console.log.info('checkActualStates => Wettervorhersage state.val ( ' + _weatherForInstanceToday.val + '); ' + typeof _weatherForInstanceToday.val + ' kann nicht als Number verarbeitet werden');
551
- }
552
- await adapter.setStateAsync('info.rainToday',
553
- weatherForecastTodayNum,
554
- true
555
- );
556
- }
557
-
558
- /**
559
- * Niederschlagsmenge MORGEN in mm
560
- * @type {ioBroker.State | void}
561
- * @private
562
- */
563
- const _weatherForInstance = await adapter.getForeignStateAsync(
564
- adapter.config.weatherForInstance + '.NextDaysDetailed.Location_1.Day_2.rain_value'
565
- ).catch((e) => adapter.log.warn(e));
566
- if (_weatherForInstance && _weatherForInstance.val) {
567
- weatherForecastTomorrowNum = _weatherForInstance.val;
568
- await adapter.setStateAsync(
569
- 'info.rainTomorrow',
570
- weatherForecastTomorrowNum,
571
- true
572
- );
573
- }
574
- }
575
-
576
- // wenn (...sprinkleName.autoOn == false[off]) so wird der aktuelle Sprenger [sprinkleName]
577
- // bei false nicht automatisch gestartet
578
- /**
579
- * Abfrage von ...sprinkleName.autoOn
580
- * @type {[myConfig.config]}
581
- */
582
- const result = myConfig.config;
583
- if (result) {
584
- for (const res of result) {
585
- /**
586
- * Abfrage ... .autoOn beim Start
587
- * @type {ioBroker.State | void}
588
- * @private
589
- */
590
- const _autoOn = await adapter.getForeignStateAsync(
591
- res.autoOnID
592
- ).catch((e) => adapter.log.warn(e));
593
- if (_autoOn && typeof _autoOn.val === 'boolean') {
594
- res.autoOn = _autoOn.val;
595
- if (_autoOn.val === false) {adapter.log.info(`get ${res.objectName}.autoOn = ${res.autoOn}`)}
596
- }
597
- }
598
- }
599
-
600
- if (adapter.config.actualValueLevel){
601
- /**
602
- * Füllstand der Zisterne in % holen
603
- * @type {ioBroker.State | void}
604
- * @private
605
- */
606
- const _actualValueLevel = await adapter.getForeignStateAsync(
607
- adapter.config.actualValueLevel
608
- ).catch((e) => adapter.log.warn(e));
609
- if (_actualValueLevel && typeof parseFloat(_actualValueLevel.val) === "number") {
610
- valveControl.setFillLevelCistern(parseFloat(_actualValueLevel.val));
611
- }
612
- }
613
- /**
614
- * return the saved objects under sprinkle.*
615
- * rückgabe der gespeicherten Objekte unter sprinkle.*
616
- * @type {ioBroker.NonNullCallbackReturnTypeOf<(err?: (Error | null), objects?: Record<string, ioBroker.AnyObject & {type: "channel"}>) => void> | void}
617
- * @private
618
- */
619
- const _list = await adapter.getForeignObjectsAsync(adapter.namespace + '.sprinkle.*', 'channel').catch((e) => adapter.log.warn(e));
620
- if (_list) {
621
- ObjSprinkle = _list;
622
- }
623
-
624
- } catch (e) {
625
- adapter.log.warn(`sprinkleControl cannot check actual States ... Please check your sprinkleControl states: ${e}`);
626
- }
627
-
628
- }
629
-
630
-
631
- /**
632
- * at 0:05 start of StartTimeSprinkle
633
- * => um 0:05 start von StartTimeSprinkle
634
- * (..., '(s )m h d m wd')
635
- */
636
- const calcPos = schedule.scheduleJob('calcPosTimer', '5 0 * * *', function() {
637
- // Berechnungen mittels SunCalc
638
- sunPos();
639
- today = formatTime(adapter,'', 'day');
640
-
641
- // History Daten aktualisieren, wenn eine neue Woche beginnt
642
- adapter.log.debug(`calcPos 0:05 old-KW: ${kwStr} new-KW: ${formatTime(adapter, '','kW')} if: ${(kwStr !== formatTime(adapter, '','kW'))}`);
643
- if (kwStr !== formatTime(adapter, '','kW')) {
644
- const result = myConfig.config;
645
- if (result) {
646
- for(const i in result) {
647
- if (result.hasOwnProperty(i)) {
648
- const objectName = result[i].objectName;
649
- adapter.getState('sprinkle.' + objectName + '.history.curCalWeekConsumed', (err, state) => {
650
- if (state) {
651
- adapter.setState('sprinkle.' + objectName + '.history.lastCalWeekConsumed', { val: state.val, ack: true });
652
- adapter.setState('sprinkle.' + objectName + '.history.curCalWeekConsumed', { val: 0, ack: true });
653
- }
654
- });
655
- adapter.getState('sprinkle.' + objectName + '.history.curCalWeekRunningTime', (err, state) => {
656
- if (state) {
657
- adapter.setState('sprinkle.' + objectName + '.history.lastCalWeekRunningTime', { val: state.val, ack: true });
658
- adapter.setState('sprinkle.' + objectName + '.history.curCalWeekRunningTime', { val: '00:00', ack: true });
659
- }
660
- });
661
- }
662
-
663
- }
664
- }
665
- kwStr = formatTime(adapter, '','kW');
666
- }
667
-
668
- // ETpToday und ETpYesterday in evaporation aktualisieren da ein neuer Tag
669
- evaporation.setNewDay();
670
-
671
- // Startzeit Festlegenverzögert wegen Daten von SunCalc
672
- setTimeout(() => {
673
- startTimeSprinkle();
674
- addStartTimeSprinkle();
675
- },1000);
676
-
677
- });
678
-
679
- // Berechnung mittels sunCalc
680
- function sunPos() {
681
- // get today's sunlight times Holen Sie sich die heutige Sonnenlichtzeit
682
- const times = SunCalc.getTimes(new Date(), adapter.config.latitude, adapter.config.longitude);
683
-
684
- // format sunrise time from the Date object → Formatieren Sie die Sonnenaufgangszeit aus dem Date-Objekt
685
- sunriseStr = ('0' + times.sunrise.getHours()).slice(-2) + ':' + ('0' + times.sunrise.getMinutes()).slice(-2);
686
-
687
- // format golden hour end time from the Date object → Formatiere golden hour end time aus dem Date-Objekt
688
- goldenHourEnd = ('0' + times.goldenHourEnd.getHours()).slice(-2) + ':' + ('0' + times.goldenHourEnd.getMinutes()).slice(-2);
689
-
690
- // format sunset time from the Date object → formatieren Sie die Sonnenuntergangszeit aus dem Date-Objekt
691
- sunsetStr = sunsetStr = ('0' + times.sunset.getHours()).slice(-2) + ':' + ('0' + times.sunset.getMinutes()).slice(-2);
692
-
693
- }
694
-
695
- function addStartTimeSprinkle() {
696
- schedule.cancelJob('sprinkleAddStartTime');
697
- if (adapter.config.selectAddStartTime === 'greaterETpCurrent' || adapter.config.selectAddStartTime === 'withExternalSignal') {
698
- let addStartTimeSplit = adapter.config.addWateringStartTime.split(':');
699
- const scheduleAddStartTime = schedule.scheduleJob('sprinkleAddStartTime', addStartTimeSplit[1] + ' ' + addStartTimeSplit[0] + ' * * *', function() {
700
- // if (autoOnOff == false) => keine auto Start
701
- if (!autoOnOffStr) {
702
- schedule.cancelJob('sprinkleAddStartTime');
703
- return;
704
- }
705
- if (((adapter.config.selectAddStartTime === 'greaterETpCurrent') && (adapter.config.triggerAddStartTimeETpCur < evaporation.getETpTodayNum()))
706
- || (adapter.config.selectAddStartTime === 'withExternalSignal' && addStartTimeSwitch)) {
707
- let messageText = '';
708
-
709
- // Filter enabled
710
- const result = myConfig.config.filter(d => d.enabled === true);
711
- if (result) {
712
- /**
713
- * Array zum flüchtigen Sammeln von Bewässerungsaufgaben
714
- * @type {Array.<{auto: Boolean, sprinkleID: Number, wateringTime: Number}>}
715
- */
716
- const memAddList = [];
717
-
718
- /**
719
- * result Rain
720
- * - (aktuelle Wettervorhersage - Schwellwert der Regenberücksichtigung) wenn Sensor sich im Freien befindet
721
- * - (> 0) es regnet - Abbruch -
722
- * - (≤ 0) Start der Bewässerung
723
- * @param {boolean} inGreenhouse - Sensor befindet sich im Gewächshaus
724
- * @returns {number} - resultierende Regenmenge
725
- */
726
- function resRain (inGreenhouse) {
727
- return (adapter.config.weatherForecast && !inGreenhouse) ? (((+ weatherForecastTodayNum) - parseFloat(adapter.config.thresholdRain)).toFixed(1)) : 0;
728
- }
729
-
730
- for(const res of result) {
731
- if (res.autoOn // Ventil aktiv
732
- && (res.addWateringTime > 0) // zusätzliche Bewässerung aktiv time > 0
733
- && (resRain(res.inGreenhouse) <= 0)) { // keine Regenvorhersage
734
-
735
- switch (res.methodControlSM) {
736
- case 'bistable':
737
- if (res.soilMoisture.bool) {
738
- messageText += `<b>${res.objectName}</b> (${res.soilMoisture.bool})\n`
739
- + ` START => ${addTime(res.addWateringTime, '')}\n`;
740
- memAddList.push({
741
- auto: true,
742
- sprinkleID: res.sprinkleID,
743
- wateringTime: res.addWateringTime
744
- });
745
- }
746
- break;
747
- case 'fixDay':
748
- messageText += `<b>${res.objectName}</b>\n`
749
- + ` START => ${addTime(Math.round(60 * res.addWateringTime), '')}\n`;
750
- memAddList.push({
751
- auto: true,
752
- sprinkleID: res.sprinkleID,
753
- wateringTime: Math.round(60 * res.addWateringTime)
754
- });
755
- break;
756
- case 'calculation':
757
- let addCountdown = res.wateringTime * (res.soilMoisture.maxIrrigation - res.soilMoisture.val) / (res.soilMoisture.maxIrrigation - res.soilMoisture.triggersIrrigation) - res.wateringTime;
758
- adapter.log.debug(`addCountdown: ${addCountdown}, addWateringTime: ${res.addWateringTime}, if(${(addCountdown - res.addWateringTime) > 0})`);
759
- if ((addCountdown - res.addWateringTime) > 0) {
760
- messageText += `<b>${res.objectName}</b> ${res.soilMoisture.pct}% (${res.soilMoisture.pctTriggerIrrigation}%)\n`
761
- + ` START => ${addTime(Math.round(60 * addCountdown), '')}\n`;
762
- memAddList.push({
763
- auto: true,
764
- sprinkleID: res.sprinkleID,
765
- wateringTime: Math.round(60 * addCountdown)
766
- });
767
- }
768
- break;
769
- case 'analog':
770
- if (res.soilMoisture.pct < res.soilMoisture.pctAddTriggersIrrigation) {
771
- messageText += `<b>${res.objectName}</b> ${res.soilMoisture.pct} %(${res.soilMoisture.pctAddTriggersIrrigation}%)\n`
772
- + ` START => ${addTime(Math.round(60 * res.addWateringTime), '')}\n`;
773
- memAddList.push({
774
- auto: true,
775
- sprinkleID: res.sprinkleID,
776
- wateringTime: Math.round(60 * res.addWateringTime)
777
- });
778
- }
779
- break;
780
- }
781
- } else {
782
- adapter.log.debug(`${res.objectName}: autoOn (${res.autoOn}) && addWateringTime (${res.addWateringTime} > 0) && resRain (${resRain(res.inGreenhouse)}) <= 0, if(${res.autoOn && (res.addWateringTime > 0) && (resRain(res.inGreenhouse) <= 0)})`);
783
- }
784
- }
785
- valveControl.addList(memAddList);
786
- }
787
- if(!sendMessageText.onlySendError() && messageText.length > 0){
788
- sendMessageText.sendMessage(messageText);
789
- }
790
- } else {
791
- adapter.log.debug(`greaterETpCurrent: ${(adapter.config.selectAddStartTime === 'greaterETpCurrent')} & ${(adapter.config.triggerAddStartTimeETpCur < evaporation.getETpTodayNum())}, withExternalSignal; ${(adapter.config.selectAddStartTime === 'withExternalSignal')} & ${addStartTimeSwitch}`);
792
- }
793
- setTimeout(()=>{
794
- schedule.cancelJob('sprinkleAddStartTime');
795
- }, 200);
796
- });
797
- }
798
- }
799
-
800
- // Determination of the irrigation time => Bestimmung der Bewässerungszeit
801
- function startTimeSprinkle() {
802
- let startTimeSplit = [];
803
- let infoMessage;
804
- let messageText = '';
805
-
806
- schedule.cancelJob('sprinkleStartTime');
807
-
808
- // if (autoOnOff == false) => keine auto Start
809
- if (!autoOnOffStr) {
810
- adapter.log.info(`Sprinkle: autoOnOff == Aus ( ${autoOnOffStr} )`);
811
- adapter.setState('info.nextAutoStart', {
812
- val: 'autoOnOff = off(0)',
813
- ack: true
814
- });
815
- return;
816
- }
817
-
818
- /**
819
- * next start time (automatic)
820
- * Berechnung des nächsten Starts (Automatik)
821
- * @returns {string}
822
- */
823
- function nextStartTime () {
824
- let newStartTime;
825
- let run = 0;
826
- const curTime = new Date();
827
- const myHours = checkTime(curTime.getHours());
828
- const myMinutes = checkTime(curTime.getMinutes());
829
- let myWeekday = curTime.getDay();
830
- const myWeekdayStr = ['So','Mo','Di','Mi','Do','Fr','Sa'];
831
- const myTime = myHours + ':' + myMinutes;
832
-
833
- /**
834
- * aus 0...9 wird String 00...09
835
- * @param {string|number} i
836
- * @returns {string}
837
- */
838
- function checkTime(i) {
839
- return (i < 10) ? '0' + i : i;
840
- }
841
-
842
- do {
843
- myWeekday += run;
844
- run++;
845
- if (myWeekday>6){myWeekday=0;}
846
- // Start time variant according to configuration => Startzeitvariante gemäß Konfiguration
847
- switch(adapter.config.wateringStartTime) {
848
- case 'livingTime' : /*Startauswahl = festen Zeit*/
849
- infoMessage = 'Start zur festen Zeit ';
850
- newStartTime = adapter.config.weekLiving;
851
- break;
852
- case 'livingSunrise' : /*Startauswahl = Sonnenaufgang*/
853
- infoMessage = 'Start mit Sonnenaufgang ';
854
- // format sunrise time from the Date object
855
- newStartTime = addTime(sunriseStr, parseInt(adapter.config.timeShift));
856
- break;
857
- case 'livingGoldenHourEnd' : /*Startauswahl = Ende der Golden Hour*/
858
- infoMessage = 'Start zum Ende der Golden Hour ';
859
- // format goldenHourEnd time from the Date object
860
- newStartTime = goldenHourEnd;
861
- break;
862
- case 'livingSunset' : /*Startauswahl = Sonnenuntergang*/
863
- infoMessage = 'Start mit Sonnenuntergang ';
864
- // format sunset time from the Date object
865
- newStartTime = addTime(sunsetStr, parseInt(adapter.config.timeShift));
866
- break;
867
- }
868
- // Start am Wochenende →, wenn andere Zeiten verwendet werden soll
869
- if((adapter.config.publicWeekend) && ((myWeekday === 6) || (myWeekday === 0))){
870
- infoMessage = 'Start am Wochenende ';
871
- newStartTime = adapter.config.weekEndLiving;
872
- }
873
- // Start an Feiertagen →, wenn Zeiten des Wochenendes verwendet werden soll
874
- if((adapter.config.publicHolidays) && (adapter.config.publicWeekend)
875
- && (((publicHolidayStr === true) && (run === 1)) // heute Feiertag && erster Durchlauf
876
- || ((publicHolidayTomorrowStr === true) && (run === 2)) // morgen Feiertag && zweiter Durchlauf
877
- || (holidayStr === true))) { // Urlaub
878
- infoMessage = 'Start am Feiertag ';
879
- newStartTime = adapter.config.weekEndLiving;
880
- }
881
- } while ((newStartTime <= myTime) && (run === 1));
882
-
883
- const newStartTimeLong = myWeekdayStr[myWeekday] + ' ' + newStartTime;
884
- /**
885
- * next Auto-Start
886
- * @param {string|null} err
887
- * @param {State|null|undefined} state
888
- */
889
- adapter.getState('info.nextAutoStart', (err, state) =>{
890
- if (state) {
891
- if (state.val !== newStartTimeLong) {
892
- adapter.setState('info.nextAutoStart', {
893
- val: newStartTimeLong,
894
- ack: true
895
- });
896
- // next Start Message
897
- if(!sendMessageText.onlySendError){
898
- sendMessageText.sendMessage(infoMessage + '(' + myWeekdayStr[myWeekday] + ') um ' + newStartTime);
899
- }
900
- adapter.log.info(`${infoMessage} (${myWeekdayStr[myWeekday]}) um ${newStartTime}`);
901
- }
902
- }
903
- });
904
- return newStartTime;
905
- }
906
- //
907
- startTimeStr = nextStartTime();
908
- startTimeSplit = startTimeStr.split(':');
909
-
910
- const scheduleStartTime = schedule.scheduleJob('sprinkleStartTime', startTimeSplit[1] + ' ' + startTimeSplit[0] + ' * * *', function() {
911
- // Filter enabled
912
- const result = myConfig.config.filter(d => d.enabled === true);
913
- if (result) {
914
- /**
915
- * Array zum flüchtigen Sammeln von Bewässerungsaufgaben
916
- * @type {Array.<{auto: Boolean, sprinkleID: Number, wateringTime: Number}>}
917
- */
918
- const memAddList = [];
919
-
920
- /**
921
- * result Rain
922
- * - (aktuelle Wettervorhersage - Schwellwert der Regenberücksichtigung) wenn Sensor sich im Freien befindet
923
- * - (> 0) es regnet - Abbruch -
924
- * - (≤ 0) Start der Bewässerung
925
- * @param {boolean} inGreenhouse - Sensor befindet sich im Gewächshaus
926
- * @returns {number} - resultierende Regenmenge
927
- */
928
- function resRain (inGreenhouse) {
929
- return (adapter.config.weatherForecast && !inGreenhouse) ? (((+ weatherForecastTodayNum) - parseFloat(adapter.config.thresholdRain)).toFixed(1)) : 0;
930
- }
931
-
932
- for(const res of result) {
933
- messageText += '<b>' + res.objectName + '</b>';
934
- switch (res.methodControlSM) {
935
- case 'bistable':
936
- messageText += ' (' + res.soilMoisture.bool + ')\n';
937
- break;
938
- case 'analog':
939
- messageText += ' ' + res.soilMoisture.pct + '% (' + res.soilMoisture.pctTriggerIrrigation + '%)\n';
940
- break;
941
- case 'fixDay':
942
- messageText += ' (' + curNextFixDay(res.sprinkleID, true) + ')\n';
943
- break;
944
- case 'calculation':
945
- messageText += ' ' + res.soilMoisture.pct + '% (' + res.soilMoisture.pctTriggerIrrigation + '%)\n';
946
- break;
947
- }
948
-
949
- // Test Bodenfeuchte
950
- adapter.log.debug(`Bodenfeuchte: ${res.soilMoisture.val} <= ${res.soilMoisture.triggersIrrigation} AutoOn: ${res.autoOn}`);
951
- if (res.autoOn) {
952
- switch (res.methodControlSM) {
953
- // -- bistable -- Bodenfeuchte-Sensor mit 2-Punkt-Regler true und false -- //
954
- case 'bistable':
955
- if(res.soilMoisture.bool) {
956
- /* Wenn in der Config Regenvorhersage aktiviert: Startvorgang abbrechen, wenn der Regen den eingegebenen Schwellwert überschreitet. */
957
- if (resRain(res.inGreenhouse) <= 0) {
958
- const curWateringTime = Math.round(60 * res.wateringTime * evaporation.timeExtension(res.wateringAdd));
959
- memAddList.push({
960
- auto: true,
961
- sprinkleID: res.sprinkleID,
962
- wateringTime: curWateringTime
963
- });
964
- messageText += ' START => ' + addTime(curWateringTime, '') + '\n';
965
- } else if (adapter.config.weatherForecast) {
966
- /* Bewässerung unterdrückt da ausreichende Regenvorhersage */
967
- messageText += ' ' + '<i>' + 'Start verschoben, da heute ' + weatherForecastTodayNum + 'mm Niederschlag' + '</i> ' + '\n';
968
- adapter.log.info(`${res.objectName}: Start verschoben, da Regenvorhersage für Heute ${weatherForecastTodayNum} mm [ ${resRain(res.inGreenhouse)} > 0 ]`);
969
- }
970
- }
971
- break;
972
- // --- analog -- Bodenfeuchte-Sensor im Wertebereich von 0 bis 100% --- //
973
- // -- Prozentuale Bodenfeuchte zu gering -- //
974
- case 'analog':
975
- if(res.soilMoisture.pct <= res.soilMoisture.pctTriggerIrrigation) {
976
- /* Wenn in der Config Regenvorhersage aktiviert: Startvorgang abbrechen, wenn der Regen den eingegebenen Schwellwert überschreitet. */
977
- if (resRain(res.inGreenhouse) <= 0) {
978
- let countdown = res.wateringTime * (100 - res.soilMoisture.pct) / (100 - res.soilMoisture.pctTriggerIrrigation); // in min
979
- // Begrenzung der Bewässerungszeit auf dem in der Config eingestellten Überschreitung (in Prozent)
980
- if (countdown > (res.wateringTime * res.wateringAdd / 100)) {countdown = res.wateringTime * res.wateringAdd / 100;}
981
- memAddList.push({
982
- auto: true,
983
- sprinkleID: res.sprinkleID,
984
- wateringTime: Math.round(60* countdown)
985
- });
986
- messageText += ' START => ' + addTime(Math.round(60*countdown), '') + '\n';
987
- } else if (adapter.config.weatherForecast) {
988
- /* Bewässerung unterdrückt da ausreichende Regenvorhersage */
989
- messageText += ' ' + '<i>' + 'Start verschoben, da heute ' + weatherForecastTodayNum + 'mm Niederschlag' + '</i> ' + '\n';
990
- adapter.log.info(`${res.objectName}: Start verschoben, da Regenvorhersage für Heute ${weatherForecastTodayNum} mm [ ${resRain(res.inGreenhouse)} > 0 ]`);
991
- }
992
- }
993
- break;
994
- // --- fixDay -- Start an festen Tagen ohne Sensoren --- //
995
- // -- Bewässerungstag erreicht //
996
- case 'fixDay':
997
- /* Wenn in der Config Regenvorhersage aktiviert: Startvorgang abbrechen, wenn der Regen den eingegebenen Schwellwert überschreitet. */
998
- if (resRain(false) <= 0) {
999
- // Bewässerungstag erreicht
1000
- if (res.startFixDay[today]) {
1001
- const curWateringTime = Math.round(60 * res.wateringTime * evaporation.timeExtension(res.wateringAdd));
1002
- memAddList.push({
1003
- auto: true,
1004
- sprinkleID: res.sprinkleID,
1005
- wateringTime: curWateringTime
1006
- });
1007
- messageText += ' START => ' + addTime(curWateringTime, '') + '\n';
1008
- if (res.startDay === 'threeRd'){ // Next Start in 3 Tagen
1009
- res.startFixDay[today] = false;
1010
- res.startFixDay[(+ today + 3 > 6) ? (+ today-4) : (+ today+3)] = true;
1011
- }else if (res.startDay === 'twoNd') { // Next Start in 2 Tagen
1012
- res.startFixDay[today] = false;
1013
- res.startFixDay[(+ today + 2 > 6) ? (+ today-5) : (+ today+2)] = true;
1014
- }
1015
- }
1016
- } else if (adapter.config.weatherForecast){
1017
- messageText += ' ' + '<i>' + 'Start verschoben, da heute ' + weatherForecastTodayNum + 'mm Niederschlag' + '</i> ' + '\n';
1018
- adapter.log.info(`${res.objectName}: Start verschoben, da Regenvorhersage für Heute ${weatherForecastTodayNum} mm [ ${resRain(false)} > 0 ]`);
1019
- if ((res.startDay === 'threeRd') || (res.startDay === 'twoNd')) {
1020
- let startDay = -1;
1021
- res.startFixDay.forEach((item, index) => {if (item) {startDay = index}});
1022
- if (startDay !== -1) {
1023
- res.startFixDay[startDay] = false;
1024
- res.startFixDay[(+ startDay + 1 > 6) ? (+ startDay-6) : (+ startDay+1)] = true;
1025
- } else {
1026
- adapter.log.warn(`${res.objectName}: no start day found`)
1027
- }
1028
- }
1029
- }
1030
- curNextFixDay(res.sprinkleID, false);
1031
- break;
1032
- // --- calculation -- Berechnung der Bodenfeuchte --- //
1033
- // -- Bodenfeuchte zu gering -- //
1034
- case 'calculation':
1035
- if (res.soilMoisture.val <= res.soilMoisture.triggersIrrigation) {
1036
- /* Wenn in der Config Regenvorhersage aktiviert: Startvorgang abbrechen, wenn es heute ausreichend regnen sollte. */
1037
- const resMoisture = (adapter.config.weatherForecast)?((+ res.soilMoisture.val) + (+ weatherForecastTodayNum) - parseFloat(adapter.config.thresholdRain)):(res.soilMoisture.val); // aktualisierte Bodenfeuchte mit Regenvorhersage
1038
- if ((resMoisture <= res.soilMoisture.triggersIrrigation) || res.inGreenhouse) { // Kontrolle ob Regenvorhersage ausreicht || Bewässerung inGreenhouse
1039
- let countdown = res.wateringTime * (res.soilMoisture.maxIrrigation - res.soilMoisture.val) / (res.soilMoisture.maxIrrigation - res.soilMoisture.triggersIrrigation); // in min
1040
- // Begrenzung der Bewässerungszeit auf dem in der Config eingestellten Überschreitung (in Prozent)
1041
- if (countdown > (res.wateringTime * res.wateringAdd / 100)) {countdown = res.wateringTime * res.wateringAdd / 100;}
1042
- memAddList.push({
1043
- auto: true,
1044
- sprinkleID: res.sprinkleID,
1045
- wateringTime: Math.round(60*countdown)
1046
- });
1047
- messageText += ' START => ' + addTime(Math.round(60*countdown), '') + '\n';
1048
- } else if (adapter.config.weatherForecast) {
1049
- /* Bewässerung unterdrückt da ausreichende Regenvorhersage */
1050
- messageText += ' ' + '<i>' + 'Start verschoben, da heute ' + weatherForecastTodayNum + 'mm Niederschlag' + '</i> ' + '\n';
1051
- adapter.log.info(`${res.objectName}: Start verschoben, da Regenvorhersage für Heute ${weatherForecastTodayNum} mm [ ${res.soilMoisture.val.toFixed(1)} (${resMoisture.toFixed(1)}) <= ${res.soilMoisture.triggersIrrigation} ]`);
1052
- }
1053
- }
1054
- break;
1055
- }
1056
- } else {
1057
- messageText += ' ' + '<i>' + 'Ventil auf Handbetrieb' + '</i>' + '\n';
1058
- }
1059
- }
1060
- valveControl.addList(memAddList);
1061
- }
1062
- if(!sendMessageText.onlySendError()){
1063
- sendMessageText.sendMessage(messageText);
1064
- }
1065
- setTimeout (() => {
1066
- setTimeout(()=>{
1067
- nextStartTime();
1068
- }, 800);
1069
- schedule.cancelJob('sprinkleStartTime');
1070
- }, 200);
1071
- });
1072
- }
1073
-
1074
- //
1075
- async function createSprinklers() {
1076
- /**Creates an Object .control.addStartTimeSwitch, when additional watering has been activated via an external signal
1077
- * - Erzeugt ein Object .control.addStartTimeSwitch, wenn die Zusatzbewässerung über ein externes Signal aktiviert wurde
1078
- * @type {{id: string, name: string} | void}
1079
- * @private
1080
- */
1081
- const _addStartTimeSwitch = await adapter.findForeignObjectAsync(`${adapter.namespace}.control.addStartTimeSwitch`, 'boolean').catch((e) => adapter.log.warn(`.control.addStartTimeSwitch ${e}`));
1082
- if (_addStartTimeSwitch.id !== `${adapter.namespace}.control.addStartTimeSwitch` && adapter.config.selectAddStartTime === 'withExternalSignal') {
1083
- adapter.setObjectNotExistsAsync(`${adapter.namespace}.control.addStartTimeSwitch`, {
1084
- 'type': 'state',
1085
- 'common': {
1086
- "role": "switch",
1087
- 'name': 'additional irrigation enabled',
1088
- 'type': 'boolean',
1089
- 'read': true,
1090
- 'write': true,
1091
- 'def': false
1092
- },
1093
- 'native': {}
1094
- }).catch((e) => adapter.log.warn(`.control.addStartTimeSwitch ${e}`));
1095
- } else if (_addStartTimeSwitch.id === `${adapter.namespace}.control.addStartTimeSwitch`) {
1096
- if (adapter.config.selectAddStartTime !== 'withExternalSignal') {
1097
- adapter.delObjectAsync(`${adapter.namespace}.control.addStartTimeSwitch`).catch((e) => adapter.log.warn(`.control.addStartTimeSwitch ${e}`));
1098
- addStartTimeSwitch = false;
1099
- } else {
1100
- /** auslesen .control.addStartTimeSwitch.val
1101
- * @type {ioBroker.State | void}
1102
- * @private
1103
- */
1104
- const _state = await adapter.getStateAsync(`${adapter.namespace}.control.addStartTimeSwitch`).catch((e) => adapter.log.warn(`.control.addStartTimeSwitch ${e}`));
1105
- if (typeof _state.val === 'boolean') {
1106
- addStartTimeSwitch = _state.val;
1107
- }
1108
- }
1109
- }
1110
-
1111
-
1112
-
1113
- const result = adapter.config.events;
1114
- if (result) {
1115
- for(const res of result) {
1116
- let objectName;
1117
-
1118
- if(res.sprinkleName !== '') {
1119
- objectName = res.sprinkleName.replace(/[.;, ]/g, '_');
1120
- } else if (res.sprinkleName === '') {
1121
- objectName = res.name.replace(/[.;, ]/g, '_');
1122
- }
1123
-
1124
- const objPfad = 'sprinkle.' + objectName;
1125
- const j = myConfig.config.findIndex(d => d.objectName === objectName);
1126
-
1127
- // Create bzw. update .actualSoilMoisture
1128
-
1129
- let nameMetConSM, objMetConSM;
1130
- await fillMetConSM(res);
1131
- async function fillMetConSM(res) {
1132
- //adapter.log.debug(JSON.stringify(res));
1133
- switch (res.methodControlSM) {
1134
- case 'calculation':
1135
- nameMetConSM = objectName + ' => Calculated soil moisture in %';
1136
- objMetConSM = {
1137
- 'type': 'state',
1138
- 'common': {
1139
- 'role': 'state',
1140
- 'name': nameMetConSM,
1141
- 'type': 'number',
1142
- 'min': 0,
1143
- 'max': 150,
1144
- 'unit': '%',
1145
- 'read': true,
1146
- 'write': false,
1147
- 'def': 50
1148
- },
1149
- 'native': {},
1150
- };
1151
- break;
1152
- case 'bistable':
1153
- nameMetConSM = objectName + ' => bistable soil moisture sensor';
1154
- objMetConSM = {
1155
- 'type': 'state',
1156
- 'common': {
1157
- 'role': 'state',
1158
- 'name': nameMetConSM,
1159
- 'type': 'boolean',
1160
- 'read': true,
1161
- 'write': false,
1162
- 'def': false
1163
- },
1164
- 'native': {},
1165
- };
1166
- break;
1167
- case 'analog':
1168
- nameMetConSM = objectName + ' => analog soil moisture sensor in %';
1169
- objMetConSM = {
1170
- 'type': 'state',
1171
- 'common': {
1172
- 'role': 'state',
1173
- 'name': nameMetConSM,
1174
- 'type': 'number',
1175
- 'min': 0,
1176
- 'max': 150,
1177
- 'unit': '%',
1178
- 'read': true,
1179
- 'write': false
1180
- },
1181
- 'native': {},
1182
- };
1183
- break;
1184
- case 'fixDay':
1185
- nameMetConSM = objectName + ' => start on a fixed day';
1186
- objMetConSM = {
1187
- 'type': 'state',
1188
- 'common': {
1189
- 'role': 'state',
1190
- 'name': nameMetConSM,
1191
- 'type': 'number',
1192
- 'min': 0,
1193
- 'max': 7,
1194
- 'states': {"0":"Sun", "1":"Mon", "2":"Tue", "3":"Wed", "4":"Thur", "5":"Fri", "6":"Sat", "7":"off"},
1195
- 'read': true,
1196
- 'write': false,
1197
- 'def': 7
1198
- },
1199
- 'native': {},
1200
- };
1201
- break;
1202
- default:
1203
- adapter.log.warn(`sprinkleControl cannot created ... Please check your sprinkleControl config ${objectName} methodControl`);
1204
- nameMetConSM = objectName + ' => Emergency program! start on a fixed day';
1205
- objMetConSM = {
1206
- 'type': 'state',
1207
- 'common': {
1208
- 'role': 'state',
1209
- 'name': nameMetConSM,
1210
- 'type': 'number',
1211
- 'min': 0,
1212
- 'max': 7,
1213
- 'states': {"0":"Sun", "1":"Mon", "2":"Tue", "3":"Wed", "4":"Thur", "5":"Fri", "6":"Sat", "7":"off"},
1214
- 'read': true,
1215
- 'write': false,
1216
- 'def': 7
1217
- },
1218
- 'native': {},
1219
- };
1220
- }
1221
- }
1222
- // ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ //
1223
- // +++++ Objekte erstellen +++++ //
1224
- // ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ //
1225
- if (objectName && objectName !== '') {
1226
- try {
1227
- // Create Object for sprinkle. (ID)
1228
- const _sprinkleNotExist = await adapter.setObjectNotExistsAsync('sprinkle.' + objectName, {
1229
- 'type': 'channel',
1230
- 'common': {
1231
- 'name': res.sprinkleName
1232
- },
1233
- 'native': {},
1234
- });
1235
- // Create Object for .history
1236
- const _historyNotExist = await adapter.setObjectNotExistsAsync('sprinkle.' + objectName + '.history', {
1237
- 'type': 'channel',
1238
- 'common': {
1239
- 'name': res.sprinkleName + ' => History'
1240
- },
1241
- 'native': {},
1242
- });
1243
- // Create Object for .history.curCalWeekConsumed
1244
- // Sprinkler consumption of the current calendar week => History - Sprinkler-Verbrauch der aktuellen Kalenderwoche (783 Liter)
1245
- const _curCalWeekConsumedNotExist = adapter.setObjectNotExistsAsync(objPfad + '.history.curCalWeekConsumed', {
1246
- 'type': 'state',
1247
- 'common': {
1248
- 'role': 'state',
1249
- 'name': objectName + ' => History - Sprinkler consumption of the current calendar week',
1250
- 'type': 'number',
1251
- 'unit': 'Liter',
1252
- 'read': true,
1253
- 'write': false,
1254
- 'def': 0
1255
- },
1256
- 'native': {},
1257
- });
1258
- // Create Object for .history.curCalWeekRunningTime
1259
- // Sprinkler running time of the current calendar week => History - Sprinkler-Laufzeit der aktuellen Kalenderwoche (783 Liter)
1260
- const _curCalWeekRunningTimeNotExist = adapter.setObjectNotExistsAsync(objPfad + '.history.curCalWeekRunningTime', {
1261
- 'type': 'state',
1262
- 'common': {
1263
- 'role': 'state',
1264
- 'name': objectName + ' => History - Sprinkler running time of the current calendar week',
1265
- 'type': 'string',
1266
- 'read': true,
1267
- 'write': false,
1268
- 'def': '00:00'
1269
- },
1270
- 'native': {},
1271
- });
1272
- // Create Object for .history.lastCalWeekConsumed
1273
- // Sprinkler consumption of the last calendar week => History - Sprinkler-Verbrauch der letzten Kalenderwoche (783 Liter)
1274
- const _lastCalWeekConsumedNotExist = adapter.setObjectNotExistsAsync(objPfad + '.history.lastCalWeekConsumed', {
1275
- 'type': 'state',
1276
- 'common': {
1277
- 'role': 'state',
1278
- 'name': objectName + ' => History - Sprinkler consumption of the last calendar week',
1279
- 'type': 'number',
1280
- 'unit': 'Liter',
1281
- 'read': true,
1282
- 'write': false,
1283
- 'def': 0
1284
- },
1285
- 'native': {},
1286
- });
1287
- // Create Object for .history.lastCalWeekRunningTime
1288
- // Sprinkler running time of the last calendar week => History - Sprinkler-Laufzeit der letzten Kalenderwoche (783 Liter)
1289
- const _lastCalWeekRunningTimeNotExist = adapter.setObjectNotExistsAsync(objPfad + '.history.lastCalWeekRunningTime', {
1290
- 'type': 'state',
1291
- 'common': {
1292
- 'role': 'state',
1293
- 'name': objectName + ' => History - Sprinkler running time of the last calendar week',
1294
- 'type': 'string',
1295
- 'read': true,
1296
- 'write': false,
1297
- 'def': '00:00'
1298
- },
1299
- 'native': {},
1300
- });
1301
- // Create Object for .history.lastConsumed
1302
- // Last consumed of sprinkler => History - Letzte Verbrauchsmenge des Ventils (783 Liter)
1303
- const _lastConsumedNotExist = adapter.setObjectNotExistsAsync(objPfad + '.history.lastConsumed', {
1304
- 'type': 'state',
1305
- 'common': {
1306
- 'role': 'state',
1307
- 'name': objectName + ' => History - Last consumed of sprinkler',
1308
- 'type': 'number',
1309
- 'unit': 'Liter',
1310
- 'read': true,
1311
- 'write': false,
1312
- 'def': 0
1313
- },
1314
- 'native': {},
1315
- });
1316
- // Create Object for .history.lastOn
1317
- // Last On of sprinkler => History - Letzter Start des Ventils (30.03 06:30)
1318
- const _lastOnNotExist = adapter.setObjectNotExistsAsync(objPfad + '.history.lastOn', {
1319
- 'type': 'state',
1320
- 'common': {
1321
- 'role': 'state',
1322
- 'name': objectName + ' => History - Last On of sprinkler',
1323
- 'type': 'string',
1324
- 'read': true,
1325
- 'write': false,
1326
- 'def': '-'
1327
- },
1328
- 'native': {},
1329
- });
1330
- // Create Object for .history.lastRunningTime
1331
- // Last running time of sprinkler => History - Letzte Laufzeit des Ventils (0 sek, 47:00 min, 1:03:45 )
1332
- const _lastRunningTimeNotExist = adapter.setObjectNotExistsAsync(objPfad + '.history.lastRunningTime', {
1333
- 'type': 'state',
1334
- 'common': {
1335
- 'role': 'state',
1336
- 'name': objectName + ' => History - Last running time',
1337
- 'type': 'string',
1338
- 'read': true,
1339
- 'write': false,
1340
- 'def': '00:00'
1341
- },
1342
- 'native': {},
1343
- });
1344
- // Create Object for .autoOn
1345
- const _autoOnNotExist = adapter.setObjectNotExistsAsync(objPfad + '.autoOn', {
1346
- 'type': 'state',
1347
- 'common': {
1348
- 'role': 'Switch',
1349
- 'name': objectName + ' => Switch automatic mode on / off',
1350
- 'type': 'boolean',
1351
- "states": {
1352
- "false": "off",
1353
- "true": "on"
1354
- },
1355
- 'read': true,
1356
- 'write': true,
1357
- 'def': true
1358
- },
1359
- 'native': {},
1360
- }).catch((e) => adapter.log.warn(`setObjectNotExistsAsync ${objectName}.autoOn ${e}`));
1361
- // Create Object for .actualSoilMoisture
1362
- const _actualSoilMoistureFind = await adapter.findForeignObjectAsync(`${adapter.namespace}.${objPfad}.actualSoilMoisture`, `${objMetConSM.common.type}`);
1363
- if (_actualSoilMoistureFind.id !== `${adapter.namespace}.${objPfad}.actualSoilMoisture` || _actualSoilMoistureFind.name !== nameMetConSM) {
1364
- await adapter.setObjectAsync(
1365
- objPfad + '.actualSoilMoisture',
1366
- objMetConSM
1367
- ).catch((e) => adapter.log.warn(e));
1368
- adapter.log.info(`sprinkleControl [sprinkle.${objectName}.actualSoilMoisture] was updated`);
1369
- }
1370
-
1371
- // postponeByOneDay um einen Tag verschieben bei fixDay (twoNd & threeRd)
1372
- const _postponeByOneDay = await adapter.findForeignObjectAsync(`${adapter.namespace}.${objPfad}.postponeByOneDay`, `boolean`);
1373
- if (_postponeByOneDay.id !== `${adapter.namespace}.${objPfad}.postponeByOneDay`
1374
- && res.methodControlSM === "fixDay"
1375
- && (res.startDay === "twoNd"
1376
- || res.startDay === "threeRd")) {
1377
- await adapter.setObjectNotExistsAsync(objPfad + '.postponeByOneDay', {
1378
- 'type': 'state',
1379
- 'common': {
1380
- "role": 'button',
1381
- "name": objectName + 'Postpone start by one day',
1382
- "type": 'boolean',
1383
- "read": true,
1384
- "write": true,
1385
- "def": false
1386
- },
1387
- 'native': {
1388
- "UNIT": "",
1389
- "TAB_ORDER": 0,
1390
- "OPERATIONS": 6,
1391
- "FLAGS": 1,
1392
- "TYPE": "ACTION",
1393
- "MIN": false,
1394
- "MAX": true,
1395
- "DEFAULT": false
1396
- }
1397
- });
1398
- adapter.subscribeStates(`${adapter.namespace}.${objPfad}.postponeByOneDay`);
1399
- } else if (res.methodControlSM === "fixDay"
1400
- && (res.startDay === "twoNd"
1401
- || res.startDay === "threeRd")) {
1402
- adapter.subscribeStates(`${adapter.namespace}.${objPfad}.postponeByOneDay`);
1403
- } else {
1404
- await adapter.delObjectAsync(`${adapter.namespace}.${objPfad}.postponeByOneDay`); // "sprinklecontrol.0.sprinkle.???.actualSoilMoisture"
1405
- }
1406
-
1407
- // Create Object for .countdown => Countdown des Ventils
1408
- const _countdownNotExist = adapter.setObjectNotExistsAsync(objPfad + '.countdown', {
1409
- 'type': 'state',
1410
- 'common': {
1411
- 'role': 'state',
1412
- 'name': objectName + ' => countdown of sprinkler',
1413
- 'type': 'string',
1414
- 'read': true,
1415
- 'write': false,
1416
- 'def': '-'
1417
- },
1418
- 'native': {},
1419
- });
1420
- // Create Object for .runningTime => Laufzeit des Ventils
1421
- const _runningTimeNotExist = await adapter.setObjectNotExistsAsync(objPfad + '.runningTime', {
1422
- 'type': 'state',
1423
- 'common': {
1424
- 'role': 'state',
1425
- 'name': objectName + ' => running time of sprinkler',
1426
- 'type': 'string',
1427
- 'read': true,
1428
- 'write': true,
1429
- 'def': '-'
1430
- },
1431
- 'native': {},
1432
- });
1433
- // Create Object for .sprinklerState => Zustand des Ventils im Thread
1434
- // <<< 1 = warten >>> ( 0:off; 1:wait; 2:on; 3:break; 4:Boost(on); 5:off(Boost) )
1435
- // Create .sprinklerState
1436
- const _sprinklerStateNotExists = await adapter.setObjectNotExistsAsync(objPfad + '.sprinklerState', {
1437
- 'type': 'state',
1438
- 'common': {
1439
- 'role': 'state',
1440
- 'name': objectName + ' => actual state of sprinkler',
1441
- 'type': 'number',
1442
- 'min': 0,
1443
- 'max': 5,
1444
- 'states': {
1445
- "0": "off",
1446
- "1": "wait",
1447
- "2": "on",
1448
- "3": "break",
1449
- "4": "Boost(on)",
1450
- "5": "off(Boost)"
1451
- },
1452
- 'read': true,
1453
- 'write': false,
1454
- 'def': 0
1455
- },
1456
- 'native': {},
1457
- });
1458
- // Create Object for triggerPoint Schaltpunkt der Bodenfeuchte
1459
- const _triggerPointNotExist = await adapter.setObjectNotExistsAsync(objPfad + '.triggerPoint', {
1460
- 'type': 'state',
1461
- 'common': {
1462
- 'role': 'state',
1463
- 'name': objectName + ' => Trigger point of sprinkler',
1464
- 'type': 'string',
1465
- 'read': true,
1466
- 'write': false,
1467
- 'def': '-'
1468
- },
1469
- 'native': {},
1470
- });
1471
- // Object created
1472
- let value = true;
1473
- await (await Promise.all([
1474
- _sprinkleNotExist,
1475
- _historyNotExist,
1476
- _curCalWeekConsumedNotExist,
1477
- _curCalWeekRunningTimeNotExist,
1478
- _lastCalWeekConsumedNotExist,
1479
- _lastCalWeekRunningTimeNotExist,
1480
- _lastConsumedNotExist,
1481
- _lastOnNotExist,
1482
- _lastRunningTimeNotExist,
1483
- _autoOnNotExist,
1484
- _countdownNotExist,
1485
- _runningTimeNotExist,
1486
- _sprinklerStateNotExists,
1487
- _triggerPointNotExist
1488
- ])).forEach((val) => {
1489
- value &= val;
1490
- });
1491
- if(value) {
1492
- adapter.log.info(`sprinkleControl [sprinkle.${objectName}] was created`);
1493
- }
1494
-
1495
- // ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ //
1496
- // +++++ zustände der States aktualisieren +++++ //
1497
- // ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ //
1498
-
1499
- //
1500
- if(await _countdownNotExist){
1501
- const _countdown = await adapter.getStateAsync(objPfad + '.countdown').catch((e) => adapter.log.warn(`${objectName}.countdown ${e}`));
1502
- if (_countdown && _countdown.val !== '0') {
1503
- adapter.setStateAsync(
1504
- objPfad + '.countdown',
1505
- '0',
1506
- true
1507
- ).catch((e) => adapter.log.warn(e));
1508
- }
1509
- }
1510
- //
1511
- if (_runningTimeNotExist) {
1512
- const _runningTime = await adapter.getStateAsync(objPfad + '.runningTime').catch((e) => adapter.log.warn(`${objectName}.runningTime ${e}`));
1513
- if (_runningTime && _runningTime.val !== '00:00') {
1514
- adapter.setStateAsync(objPfad + '.runningTime',
1515
- '00:00',
1516
- true
1517
- ).catch((e) => adapter.log.warn(e));
1518
- }
1519
- }
1520
- //
1521
- if (_sprinklerStateNotExists){
1522
- const _sprinklerState = await adapter.getStateAsync(objPfad + '.sprinklerState').catch((e) => adapter.log.warn(`${objectName}.sprinklerState ${e}`));
1523
- if (_sprinklerState && _sprinklerState.val !== 0) {
1524
- await adapter.setStateAsync(objPfad + '.sprinklerState',
1525
- 0,
1526
- true
1527
- ).catch((e) => adapter.log.warn(`${objectName}.sprinklerState ${e}`));
1528
- }
1529
- }
1530
- // Festlegen des Schaltpunktes für den nächsten Start
1531
- switch (myConfig.config[j].methodControlSM) {
1532
- case 'bistable':
1533
- // Sensor soil moisture => Sensor Bodenfeuchte
1534
- const _triggerSMBistabile = await adapter.getForeignStateAsync(myConfig.config[j].triggerSM).catch((e) => adapter.log.warn(`${objectName}.triggerSMBistabile ${e}`));
1535
- if (_triggerSMBistabile && typeof _triggerSMBistabile.val === 'boolean') {
1536
- myConfig.setSoilMoistBool(myConfig.config[j].sprinkleID, _triggerSMBistabile.val);
1537
- } else {
1538
- myConfig.setSoilMoistBool(myConfig.config[j].sprinkleID, true);
1539
- adapter.log.warn(`The bistable sensor ${myConfig.config[j].triggerSM} in ${objectName} does not deliver correct values!`)
1540
- }
1541
-
1542
- adapter.setStateAsync(objPfad + '.triggerPoint',
1543
- '-',
1544
- true
1545
- ).catch((e) => adapter.log.warn(`${objectName}.triggerPoint ${e}`));
1546
- break;
1547
-
1548
- case 'analog':
1549
- // Sensor soil moisture => Sensor Bodenfeuchte
1550
- /**
1551
- * Schaltpunkt '.triggerSM' auslesen
1552
- * @type {ioBroker.State | void} _triggerSM - Schaltpunkt
1553
- * @private
1554
- */
1555
- const _triggerSMAnalog = await adapter.getForeignStateAsync(myConfig.config[j].triggerSM).catch((e) => adapter.log.warn(`${objectName}.triggerSMAnalog ${e}`));
1556
- if (_triggerSMAnalog && (typeof _triggerSMAnalog.val === 'number' || typeof _triggerSMAnalog.val === 'string')) {
1557
- myConfig.setSoilMoistPct(myConfig.config[j].sprinkleID, _triggerSMAnalog.val);
1558
- } else {
1559
- await adapter.setStateAsync(objPfad + '.actualSoilMoisture',
1560
- 50,
1561
- true
1562
- );
1563
- adapter.log.warn(`The analoge sensor ${myConfig.config[j].triggerSM} in ${objectName} does not deliver correct values!`)
1564
- }
1565
- adapter.setStateAsync(objPfad + '.triggerPoint',
1566
- (myConfig.config[j].soilMoisture.pctTriggerIrrigation).toString(),
1567
- true
1568
- ).catch((e) => adapter.log.warn(`${objectName}.triggerPoint setState ${e}`));
1569
- break;
1570
-
1571
- case 'fixDay':
1572
-
1573
- /** @type {number} */
1574
- const nextStartDay = ((today + 1) > 6 ? 0 : (today + 1));
1575
-
1576
- /**
1577
- * Neuen Start-Tag für Dreitage- und Zweitage-modus setzen
1578
- * @param {boolean} threeRd - Dreitage-modus Ja/Nein
1579
- * true Dreitage-modus (treeRD)
1580
- * false Zweitage-modus (twoNd)
1581
- */
1582
- async function setNewDay (threeRd) {
1583
- const today = await formatTime(adapter,'', 'day');
1584
- /**
1585
- *
1586
- * @type {ioBroker.GetStatePromise} _actualSoilMoisture
1587
- * @private {ioBroker.State|Void} _actualSoilMoisture
1588
- */
1589
- const _actualSoilMoisture = await adapter.getStateAsync(
1590
- objPfad + '.actualSoilMoisture'
1591
- ).catch((e) => adapter.log.warn(`${objectName}.actualSoilMoisture fixDay setState ${e}`));
1592
- if (_actualSoilMoisture && (typeof _actualSoilMoisture.val === 'number')) {
1593
- if ((_actualSoilMoisture.val >= 0) && (_actualSoilMoisture.val <= 6)) {
1594
- if ((threeRd)
1595
- && (_actualSoilMoisture.val === (((today + 3) > 6) ? 2 : (today + 3)))
1596
- || (_actualSoilMoisture.val === (((today + 2) > 6) ? 1 : (today + 2)))
1597
- || (_actualSoilMoisture.val === (((today + 1) > 6) ? 0 : (today + 1)))
1598
- || (_actualSoilMoisture.val === today)) {
1599
- myConfig.config[j].startFixDay[_actualSoilMoisture.val] = true;
1600
- } else {
1601
- myConfig.config[j].startFixDay[nextStartDay] = true;
1602
- }
1603
- } else {
1604
- myConfig.config[j].startFixDay[nextStartDay] = true;
1605
- }
1606
- curNextFixDay(myConfig.config[j].sprinkleID, false);
1607
- }
1608
- }
1609
-
1610
- if (myConfig.config[j].startDay === 'threeRd') {
1611
- await setNewDay(true);
1612
- } else if (myConfig.config[j].startDay === 'twoNd') {
1613
- await setNewDay(false);
1614
- } else if (myConfig.config[j].startDay === 'fixDay') {
1615
- curNextFixDay(myConfig.config[j].sprinkleID, false);
1616
- }
1617
-
1618
- adapter.setStateAsync(objPfad + '.triggerPoint',
1619
- '-',
1620
- true
1621
- ).catch((e) => adapter.log.warn(`${objectName}.triggerPoint fixDay setState ${e}`));
1622
- break;
1623
-
1624
- case 'calculation':
1625
-
1626
- const _actualSoilMoisture = await adapter.getStateAsync(objPfad + '.actualSoilMoisture').catch((e) => adapter.log.warn(e));
1627
- if (_actualSoilMoisture) {
1628
- if (await _actualSoilMoisture && typeof _actualSoilMoisture.val !== 'number' || _actualSoilMoisture.val === 0) {
1629
- adapter.setStateAsync(objPfad + '.actualSoilMoisture',
1630
- myConfig.config[j].soilMoisture.pct,
1631
- true
1632
- ).catch((e) => adapter.log.warn(e));
1633
- } else {
1634
- // num Wert der Bodenfeuchte berechnen und in der config speichern, wenn Wert zwischen 0 und max liegt
1635
- if ((0 < _actualSoilMoisture.val) && (_actualSoilMoisture.val <= (myConfig.config[j]).soilMoisture.maxRain*100/myConfig.config[j].soilMoisture.maxIrrigation)) {
1636
- myConfig.config[j].soilMoisture.val = _actualSoilMoisture.val * myConfig.config[j].soilMoisture.maxIrrigation / 100;
1637
- myConfig.config[j].soilMoisture.pct = _actualSoilMoisture.val;
1638
- } else {
1639
- // Wert aus config übernehmen
1640
- adapter.setStateAsync(objPfad + '.actualSoilMoisture',
1641
- myConfig.config[j].soilMoisture.pct,
1642
- true
1643
- ).catch((e) => adapter.log.warn(e));
1644
- }
1645
- }
1646
- }
1647
-
1648
- adapter.setStateAsync(objPfad + '.triggerPoint',
1649
- (myConfig.config[j].soilMoisture.pctTriggerIrrigation).toString(),
1650
- true
1651
- ).catch((e) => adapter.log.warn(e));
1652
- break;
1653
-
1654
- }
1655
-
1656
- } catch (e) {
1657
- adapter.log.warn(`sprinkleControl cannot created ... Please check your sprinkleControl config: ${e}`);
1658
- }
1659
- } else {
1660
- adapter.log.warn('sprinkleControl cannot created ... Please check in your config the sprinkle Name')
1661
- }
1662
- }
1663
-
1664
- // ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ //
1665
- // +++++ Objekte löschen +++++ //
1666
- // ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ //
1667
- for(const i in ObjSprinkle) {
1668
-
1669
- const resID = ObjSprinkle[i]._id;
1670
- const objectID = resID.split('.');
1671
- const resultID = objectID[3];
1672
-
1673
- const resultName = result.map(({ sprinkleName }) => ({ sprinkleName }));
1674
- const fullRes = [];
1675
-
1676
- for(const i in resultName) {
1677
- if (resultName.hasOwnProperty(i)) {
1678
- const res = resultName[i].sprinkleName.replace(/[.;, ]/g, '_');
1679
- fullRes.push(res);
1680
- }
1681
- }
1682
-
1683
- if (fullRes.indexOf(resultID) === -1) {
1684
- try {
1685
- // object deleted
1686
- /**
1687
- * del when exist Object Async
1688
- * @param id - the id of the object
1689
- * @param type - common.type of the state
1690
- * @returns {Promise<void>}
1691
- */
1692
- const delWhenExistObjectAsync = async (id, type) => {
1693
- const _find = await adapter.findForeignObjectAsync(`${id}`, `${type}`);
1694
- if (_find && _find.id === `${id}`) {
1695
- await adapter.delObjectAsync(`${id}`).catch((e) => adapter.log.warn(e)); // "sprinklecontrol.0.sprinkle.???.postponeByOneDay"
1696
- }
1697
- }
1698
-
1699
- Promise.all([
1700
- adapter.delObjectAsync(resID + '.actualSoilMoisture'), // "sprinklecontrol.0.sprinkle.???.actualSoilMoisture"
1701
- adapter.delObjectAsync(resID + '.triggerPoint'), // "sprinklecontrol.0.sprinkle.???.triggerPoint"
1702
- adapter.delObjectAsync(resID + '.sprinklerState'), // "sprinklecontrol.0.sprinkle.???.sprinklerState"
1703
- adapter.delObjectAsync(resID + '.runningTime'), // "sprinklecontrol.0.sprinkle.???.runningTime"
1704
- delWhenExistObjectAsync(`${resID}.postponeByOneDay`, `boolean`), // "sprinklecontrol.0.sprinkle.???.postponeByOneDay" wenn vorhanden löschen
1705
- adapter.delObjectAsync(resID + '.countdown'), // "sprinklecontrol.0.sprinkle.???.countdown"
1706
- adapter.delObjectAsync(resID + '.autoOn'), // "sprinklecontrol.0.sprinkle.???.autoOn"
1707
- adapter.delObjectAsync(resID + '.history.lastOn'), // "sprinklecontrol.0.sprinkle.???.history.lastOn"
1708
- adapter.delObjectAsync(resID + '.history.lastConsumed'), // "sprinklecontrol.0.sprinkle.???.history.lastConsumed"
1709
- adapter.delObjectAsync(resID + '.history.lastRunningTime'), // "sprinklecontrol.0.sprinkle.???.history.lastRunningTime"
1710
- adapter.delObjectAsync(resID + '.history.curCalWeekConsumed'), // "sprinklecontrol.0.sprinkle.???.history.curCalWeekConsumed"
1711
- adapter.delObjectAsync(resID + '.history.lastCalWeekConsumed'), // "sprinklecontrol.0.sprinkle.???.history.lastCalWeekConsumed"
1712
- adapter.delObjectAsync(resID + '.history.curCalWeekRunningTime'), // "sprinklecontrol.0.sprinkle.???.history.curCalWeekRunningTime"
1713
- adapter.delObjectAsync(resID + '.history.lastCalWeekRunningTime') // "sprinklecontrol.0.sprinkle.???.history.lastCalWeekRunningTime"
1714
- ]
1715
- ).then(async ()=>{
1716
- // History - Objekt(Ordner.history) löschen
1717
- await adapter.delObjectAsync(resID + '.history');
1718
- }).then(async ()=>{
1719
- // Objekt(Ordner) löschen
1720
- await adapter.delObjectAsync(resID);
1721
- }).then(()=>{
1722
- adapter.log.info(`sprinkleControl [${resID}] was deleted`);
1723
- });
1724
- } catch (e) {
1725
- adapter.log.warn(e);
1726
- }
1727
- }
1728
- }
1729
- }
1730
- }
1731
- ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
1732
-
1733
- /**
1734
- *
1735
- * @param {number} ms => Waiting time in ms
1736
- * @return {Promise<unknown>}
1737
- */
1738
- /*
1739
- async function sleep(ms) {
1740
- return new Promise(async (resolve) => {
1741
- // @ts-ignore
1742
- timerSleep = setTimeout(async () => resolve(), ms);
1743
- });
1744
- }
1745
- */
1746
- ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
1747
- /**
1748
- *
1749
- * @param {ioBroker.Adapter} adapter
1750
- */
1751
- function main(adapter) {
1752
-
1753
- /* The adapters' config (in the instance object everything under the attribute "native") is accessible via
1754
- * adapter.config:
1755
- * => Auf die Adapterkonfiguration (im Instanz objekt alles unter dem Attribut "native") kann zugegriffen werden über
1756
- adapter.config:
1757
- */
1758
- adapter.log.debug(`adapter.config.events: ${JSON.stringify(adapter.config.events)}`);
1759
- /**
1760
- * The adapters' config (in the instance object everything under the attribute "native") is accessible via adapter.config:
1761
- * => Auf die Adapterkonfiguration (im Instanz objekt alles unter dem Attribut "native") kann zugegriffen werden über adapter.config:
1762
- * @param {any} err
1763
- * @param {any} obj
1764
- */
1765
- adapter.getForeignObject('system.config', (err, obj) => {
1766
- if (obj) {
1767
- checkStates();
1768
- }
1769
- });
1770
- createSprinklers().catch((e) => adapter.log.warn(e));
1771
- GetSystemData().catch((e) => adapter.log.warn(e));
1772
- sendMessageText.initConfigMessage(adapter);
1773
- evaporation.initEvaporation(adapter); // init evaporation
1774
- checkActualStates().catch((e) => adapter.log.warn(e));
1775
- sunPos();
1776
- timer = setTimeout(() => {
1777
- startTimeSprinkle();
1778
- addStartTimeSprinkle()
1779
- }, 2000);
1780
-
1781
- /*
1782
- * in this template all states changes inside the adapters namespace are subscribed
1783
- * => In dieser Vorlage werden alle Statusänderungen im Namensraum des Adapters abonniert
1784
- * adapter.subscribeStates('*');
1785
- */
1786
-
1787
- //
1788
- adapter.subscribeStates('control.*');
1789
-
1790
- //adapter.subscribeStates('info.Elevation');
1791
- //adapter.subscribeStates('info.Azimut');
1792
-
1793
- // Request a notification from a third-party adapter => Fordern Sie eine Benachrichtigung von einem Drittanbieter-Adapter an
1794
- if (adapter.config.publicHolidays === true && (adapter.config.publicHolInstance + '.heute.*')) {
1795
- adapter.subscribeForeignStates(adapter.config.publicHolInstance + '.heute.*');
1796
- }
1797
- if (adapter.config.publicHolidays === true && (adapter.config.publicHolInstance + '.morgen.*')) {
1798
- adapter.subscribeForeignStates(adapter.config.publicHolInstance + '.morgen.*');
1799
- }
1800
- if (adapter.config.weatherForecast === true) {
1801
- if (adapter.config.weatherForecastService === 'ownDataPoint') {
1802
- weatherForecastTodayPfadStr = adapter.config.pathRainForecast;
1803
- adapter.subscribeForeignStates(weatherForecastTodayPfadStr);
1804
- } else if (adapter.config.weatherForecastService === 'dasWetter' && adapter.config.weatherForInstance) {
1805
- weatherForecastTodayPfadStr = adapter.config.weatherForInstance + '.NextDaysDetailed.Location_1.Day_1.rain_value';
1806
- adapter.subscribeForeignStates(weatherForecastTodayPfadStr);
1807
- adapter.subscribeForeignStates(adapter.config.weatherForInstance + '.NextDaysDetailed.Location_1.Day_2.rain_value');
1808
- } else {
1809
- adapter.log.warn('There is no valid data record stored in the weather forecast, please correct it!')
1810
- }
1811
- }
1812
- if (adapter.config.actualValueLevel !== '') {
1813
- adapter.subscribeForeignStates(adapter.config.actualValueLevel);
1814
- } else {
1815
- adapter.setState('info.cisternState', { val: 'The level sensor of the water cistern is not specified', ack: true });
1816
- }
1817
- }
1818
-
1819
- ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
1820
-
1821
- // ++++++++++++++++++ start option of Adapter ++++++++++++++++++++++
1822
- // If started as allInOne/compact mode => return function to create instance
1823
- if (module && module.parent) {
1824
- module.exports = startAdapter;
1825
- } else {
1826
- // or start the instance directly
1827
- startAdapter();
1828
- }
1
+ 'use strict';
2
+
3
+ // Load your modules here, e.g.: => // Laden Sie Ihre Module hier, z.B.
4
+ // const fs = require("fs");
5
+
6
+ const utils = require('@iobroker/adapter-core');
7
+ const schedule = require('node-schedule');
8
+ const SunCalc = require('suncalc');
9
+
10
+ const sendMessageText = require('./lib/sendMessageText.js'); // sendMessageText
11
+ const valveControl = require('./lib/valveControl.js'); // Steuerung der einzelnen Ventile
12
+ const myConfig = require('./lib/myConfig.js'); // myConfig → Speichern und abrufen von Konfigurationsdaten der Ventile
13
+ const evaporation = require('./lib/evaporation.js');
14
+ const addTime = require('./lib/tools.js').addTime;
15
+ const formatTime = require('./lib/tools.js').formatTime;
16
+ //const trend = require('./lib/tools').trend;
17
+
18
+ let adapter;
19
+ const adapterName = require('./package.json').name.split('.').pop();
20
+
21
+ let publicHolidayStr = false; // Feiertag heute?
22
+ let publicHolidayTomorrowStr = false; // Feiertag morgen?
23
+ let weatherForecastTodayPfadStr = ''; // Pfad zur Regenvorhersage in mm
24
+ let weatherForecastTodayNum = 0; // heutige Regenvorhersage in mm
25
+ let weatherForecastTomorrowNum = 0; // morgige Regenvorhersage in mm
26
+ let addStartTimeSwitch = false; // Externer Schalter für Zusatzbewässerung
27
+ let startTimeStr = ''; // 06:00
28
+ let sunriseStr = '';
29
+ let sunsetStr = '';
30
+ let goldenHourEnd = '';
31
+ let holidayStr = false; // switch => sprinklecontrol.*.control.Holiday | (Holiday == true) => Wochenendprogramm
32
+ let autoOnOffStr = true;
33
+ let kwStr = ''; // akt. KW der Woche
34
+ let timer, timerSleep;
35
+ let today = 0; // heutige Tag 0:So;1:Mo...6:Sa
36
+ /* memo */
37
+ let ObjSprinkle = {};
38
+
39
+
40
+ /* +++++++++++++++++++++++++++ Starts the adapter instance ++++++++++++++++++++++++++++++++ */
41
+
42
+ function startAdapter(options) {
43
+
44
+ options = options || {};
45
+ Object.assign(options, { name: adapterName });
46
+
47
+ adapter = new utils.Adapter(options);
48
+
49
+ // start here!
50
+ adapter.on('ready', () => {
51
+ // init createConfig
52
+ myConfig.createConfig(adapter);
53
+ // Hauptpumpe zur Bewässerung setzen
54
+ valveControl.initValveControl(adapter);
55
+ main(adapter);
56
+ });
57
+
58
+ /**
59
+ * +++++++++++++++++++++++++ is called when adapter shuts down +++++++++++++++++++++++++
60
+ *
61
+ * @param {() => void}callback
62
+ */
63
+ adapter.on('unload', (callback) => {
64
+ /* Wird beim Stoppen nicht ausgeführt - Warum?
65
+ if(adapter.config.notificationEnabled){
66
+ sendMessageText.sendMessage('sprinklerControl is shutting down');
67
+ adapter.log.info('sprinklerControl is shutting down');
68
+ }
69
+ */
70
+ try {
71
+ adapter.log.info('cleaned everything up...');
72
+ clearTimeout(timer);
73
+ clearTimeout(timerSleep);
74
+ /*Startzeiten der Timer löschen*/
75
+ schedule.cancelJob('calcPosTimer');
76
+ schedule.cancelJob('sprinkleStartTime');
77
+ schedule.cancelJob('sprinkleAddStartTime');
78
+ /* alle Ventile und Aktoren deaktivieren */
79
+ valveControl.clearEntireList();
80
+ callback();
81
+ } catch (err) {
82
+ callback(err);
83
+ }
84
+ });
85
+
86
+ /**
87
+ * ++++++++++++++++++ Answers when getTelegramUser calls from index_m ++++++++++++++++++
88
+ * -------------- Antwortet bei Aufrufen von getTelegramUser von index_m ---------------
89
+ */
90
+ adapter.on ('message', (obj) => {
91
+ if (obj) {
92
+ switch (obj.command) {
93
+ case 'getTelegramUser':
94
+ adapter.getForeignState(`${adapter.config.telegramInstance }.communicate.users`, (err, state) => {
95
+ err && adapter.log.error(err);
96
+ if (state && state.val) {
97
+ try {
98
+ adapter.log.debug(`getTelegramUser: ${state.val}`);
99
+ adapter.sendTo(obj.from, obj.command, JSON.parse(state.val), obj.callback);
100
+ } catch (err) {
101
+ err && adapter.log.error(err);
102
+ adapter.log.error('Cannot parse stored user IDs from Telegram!');
103
+ }
104
+ }
105
+ });
106
+ break;
107
+ }
108
+ }
109
+ });
110
+
111
+ /**
112
+ * ++++++++++++++++++ is called if a subscribed object changes ++++++++++++++++++
113
+ * ---------- wird aufgerufen, wenn sich ein abonniertes Objekt ändert ----------
114
+ */
115
+ adapter.on('objectChange', (id, obj) => {
116
+ if (obj) {
117
+ adapter.log.info(`object ${id} changed: ${JSON.stringify(obj)}`);
118
+ } else {
119
+ // The object was deleted
120
+ adapter.log.info(`object ${id} deleted`);
121
+ }
122
+ });
123
+
124
+ /**
125
+ * ++++++++++++++++++ is called if a subscribed state changes ++++++++++++++++++
126
+ * --------- wird aufgerufen, wenn sich ein abonnierter Status ändert ----------
127
+ */
128
+ adapter.on('stateChange', (id, state) => {
129
+ // The state was changed → Der Zustand wurde geändert
130
+ adapter.log.debug(`state ${id} changed: ${state.val} (ack = ${state.ack})`);
131
+
132
+ // Signale zum bestätigen (ack = true) - signals for confirmation
133
+ if (state?.ack === false) {
134
+ // wenn (Holiday == true) ist, soll das Wochenendprogramm gefahren werden.
135
+ if (id === `${adapter.namespace }.control.Holiday`) {
136
+ // @ts-ignore
137
+ holidayStr = state.val;
138
+ adapter.setState(id, {
139
+ val: state.val,
140
+ ack: true
141
+ });
142
+ startTimeSprinkle();
143
+ }
144
+ // wenn (addStartTimeSwitch == true) wird die zusätzliche Bewässerung aktiviert
145
+ if (id === `${adapter.namespace}.control.addStartTimeSwitch` && typeof state.val === 'boolean') {
146
+ addStartTimeSwitch = state.val;
147
+ adapter.setState(id, {
148
+ val: state.val,
149
+ ack: true
150
+ });
151
+ }
152
+ // wenn (autoOnOff == false) so werden alle Sprenger nicht mehr automatisch gestartet.
153
+ if ((id === `${adapter.namespace }.control.autoOnOff`)) {
154
+ autoOnOffStr = state.val;
155
+ adapter.log.info(`startAdapter: control.autoOnOff: ${state.val}`);
156
+ adapter.setState(id, {
157
+ val: state.val,
158
+ ack: true
159
+ });
160
+ if (!state.val) {
161
+ valveControl.clearEntireList();
162
+ }
163
+ startTimeSprinkle();
164
+ }
165
+ // wenn (autoStart == true) so die automatische Bewässerung von Hand gestartet
166
+ if (id === `${adapter.namespace}.control.autoStart`) {
167
+ adapter.setState(id, {
168
+ val: false,
169
+ ack: true
170
+ });
171
+ if (state.val === true) {
172
+ // auto Start;
173
+ startOfIrrigation();
174
+ }
175
+ }
176
+ // wenn (...sprinkleName.runningTime sich ändert) so wird der aktuelle Sprenger [sprinkleName]
177
+ // bei == 0 gestoppt, > 1 gestartet
178
+ if (myConfig.config) {
179
+ const found = myConfig.config.find(d => d.objectID === id);
180
+ if (found) {
181
+ if (id === myConfig.config[found.sprinkleID].objectID) {
182
+ if (!isNaN(state.val)) {
183
+ valveControl.addList(
184
+ [{
185
+ auto: false, // Handbetrieb
186
+ sprinkleID: found.sprinkleID,
187
+ wateringTime: (state.val <= 0) ? 0 : Math.round(60 * state.val)
188
+ }]);
189
+ adapter.setState(id, {
190
+ val: (state.val <= 0) ? 0 : addTime(Math.round(60 * state.val)),
191
+ ack: true
192
+ });
193
+ }
194
+ }
195
+ }
196
+ }
197
+ // wenn (...sprinkleName.autoOn == false[off]) so wird der aktuelle Sprenger [sprinkleName]
198
+ // bei false nicht automatisch gestartet
199
+ if (myConfig.config && (typeof state.val === 'boolean')) {
200
+ const found = myConfig.config.find(d => d.autoOnID === id);
201
+ if (found && id === myConfig.config[found.sprinkleID].autoOnID) {
202
+ myConfig.config[found.sprinkleID].autoOn = state.val;
203
+ adapter.setState(id, { // Bestätigung
204
+ val: state.val,
205
+ ack: true
206
+ });
207
+ adapter.log.info(`set ${found.objectName}.autoOn = ${state.val}, id: ${id}`);
208
+ if (state.val === false) {
209
+ valveControl.addList(
210
+ [{
211
+ auto: false,
212
+ sprinkleID: found.sprinkleID,
213
+ wateringTime: 0
214
+ }]
215
+ );
216
+ }
217
+ }
218
+ }
219
+ // postponeByOneDay → um einen Tag verschieben bei fixDay (twoNd & threeRd)
220
+ const idSplit = id.split('.', 5);
221
+ if (idSplit[4] === `postponeByOneDay`) {
222
+ const found = myConfig.config.find(d => d.objectName === idSplit[3]);
223
+ if (found) {
224
+ myConfig.postponeByOneDay(found.sprinkleID).catch((e) => {
225
+ adapter.log.warn(`postponeByOneDay: ${e}`);
226
+ });
227
+ adapter.setState(id, {
228
+ val: false,
229
+ ack: true
230
+ });
231
+ }
232
+ }
233
+ }
234
+ // Signale ohne Bestätigung - signals without confirmation
235
+
236
+ // wenn in der config unter methodControlSM!== 'analog' oder 'bistable' eingegeben wurde, dann Bodenfeuchte-Sensor auslesen
237
+ if (myConfig.config) {
238
+ function filterByID(obj){
239
+ return (((obj.methodControlSM === 'analog') || (obj.methodControlSM === 'bistable')) && (obj.triggerSM === id));
240
+ }
241
+ const filter = myConfig.config.filter(filterByID);
242
+ if (filter) {
243
+ for (const fil of filter) {
244
+ if (id === myConfig.config[fil.sprinkleID].triggerSM){
245
+ // analog
246
+ if (fil.methodControlSM === 'analog') {
247
+ myConfig.setSoilMoistPct(fil.sprinkleID, state.val);
248
+ } else if (fil.methodControlSM === 'bistable') { // bistable
249
+ myConfig.setSoilMoistBool(fil.sprinkleID, state.val);
250
+ }
251
+ }
252
+ }
253
+ }
254
+ }
255
+ // Change in outside temperature → Änderung der Außentemperatur
256
+ if (id === adapter.config.sensorOutsideTemperature) { /*Temperatur*/
257
+ if (!Number.isNaN(Number.parseFloat(state.val))) {
258
+ evaporation.setCurTemperature(parseFloat(state.val), state.ts);
259
+ } else {
260
+ adapter.log.warn(`sensorOutsideTemperature => Wrong value: ${state.val}, Type: ${typeof state.val}`);
261
+ }
262
+ }
263
+ // LuftFeuchtigkeit
264
+ if (id === adapter.config.sensorOutsideHumidity) {
265
+ if (!Number.isNaN(Number.parseFloat(state.val))) {
266
+ evaporation.setCurHumidity(parseFloat(state.val), state.lc);
267
+ } else {
268
+ adapter.log.warn(`sensorOutsideHumidity => Wrong value: ${state.val}, Type: ${typeof state.val}`);
269
+ }
270
+ }
271
+ // Helligkeit
272
+ if (id === adapter.config.sensorBrightness) {
273
+ if (!Number.isNaN(Number.parseFloat(state.val))) {
274
+ evaporation.setCurIllumination(parseFloat(state.val), state.lc);
275
+ } else {
276
+ adapter.log.warn(`sensorBrightness => Wrong value: ${state.val}, Type: ${typeof state.val}`);
277
+ }
278
+ }
279
+ // Windgeschwindigkeit
280
+ if (id === adapter.config.sensorWindSpeed) {
281
+ if (!Number.isNaN(Number.parseFloat(state.val))) {
282
+ evaporation.setCurWindSpeed(parseFloat(state.val), state.lc);
283
+ } else {
284
+ adapter.log.warn(`sensorWindSpeed => Wrong value: ${state.val}, Type: ${typeof state.val}`);
285
+ }
286
+ }
287
+ // Regencontainer
288
+ // If the amount of rain is over 20 mm, the 'lastRainCounter' is overwritten and no calculation is carried out. =>
289
+ // * Wenn die Regenmenge mehr als 20 mm beträgt, wird der 'lastRainCounter' überschrieben und es wird keine Berechnung durchgeführt.
290
+ if (id === adapter.config.sensorRainfall) {
291
+ if (!Number.isNaN(Number.parseFloat(state.val))) {
292
+ evaporation.setCurAmountOfRain(parseFloat(state.val));
293
+ } else {
294
+ adapter.log.warn(`sensorRainfall => Wrong value: ${state.val}, Type: ${typeof state.val}`);
295
+ }
296
+ }
297
+ // Feiertagskalender
298
+ if (adapter.config.publicHolidays === true) {
299
+ if (id === `${adapter.config.publicHolInstance }.heute.boolean`) {
300
+ publicHolidayStr = state.val;
301
+ startTimeSprinkle();
302
+ }
303
+ if (id === `${adapter.config.publicHolInstance }.morgen.boolean`) {
304
+ // @ts-ignore
305
+ publicHolidayTomorrowStr = state.val;
306
+ startTimeSprinkle();
307
+ }
308
+ }
309
+ // Wettervorhersage
310
+ if (adapter.config.weatherForecast === true) {
311
+ if (id === weatherForecastTodayPfadStr) {
312
+ if (typeof state.val == 'string') {
313
+ weatherForecastTodayNum = parseFloat(state.val);
314
+ } else if (typeof state.val == 'number') {
315
+ weatherForecastTodayNum = state.val;
316
+ } else {
317
+ weatherForecastTodayNum = 0;
318
+ adapter.log.info(`StateChange => Wettervorhersage state.val ( ${state.val}; ${typeof state.val} ) kann nicht als Number verarbeitet werden`);
319
+ }
320
+ adapter.setState('info.rainToday', {
321
+ val: weatherForecastTodayNum,
322
+ ack: true
323
+ });
324
+ }
325
+ if (id === `${adapter.config.weatherForInstance }.NextDaysDetailed.Location_1.Day_2.rain_value`) {
326
+ weatherForecastTomorrowNum = parseFloat(state.val);
327
+ adapter.setState('info.rainTomorrow', {
328
+ val: weatherForecastTomorrowNum,
329
+ ack: true
330
+ });
331
+ }
332
+ }
333
+ // Füllstand der Zisterne bei Statusänderung
334
+ if (adapter.config.actualValueLevel && (id === adapter.config.actualValueLevel)) {
335
+ valveControl.setFillLevelCistern(parseFloat(state.val) || 0);
336
+ //fillLevelCistern = state.val || 0;
337
+ }
338
+ });
339
+ }
340
+
341
+ // +++++++++++++++++ Get longitude an latitude from system config ++++++++++++++++++++
342
+ /**get longitude/latitude from system if not set or not valid
343
+ * do not change if we have already a valid value
344
+ * so we could use different settings compared to system if necessary
345
+ * >>>
346
+ * Längen- / Breitengrad vom System abrufen, falls nicht festgelegt oder ungültig
347
+ * wird nicht geändert, wenn bereits ein gültiger Wert vorhanden ist,
348
+ * daher können wir bei Bedarf andere Einstellungen als das System verwenden
349
+ */
350
+ async function GetSystemData() {
351
+ if (typeof adapter.config.longitude === undefined || adapter.config.longitude == null || adapter.config.longitude.length === 0 || isNaN(adapter.config.longitude)
352
+ || typeof adapter.config.latitude === undefined || adapter.config.latitude == null || adapter.config.latitude.length === 0 || isNaN(adapter.config.latitude)) {
353
+
354
+ try {
355
+ const obj = await adapter.getForeignObjectAsync('system.config');
356
+
357
+ if (obj && obj.common && obj.common.longitude && obj.common.latitude) {
358
+ adapter.config.longitude = obj.common.longitude;
359
+ adapter.config.latitude = obj.common.latitude;
360
+
361
+ adapter.log.debug(`longitude: ${adapter.config.longitude} | latitude: ${adapter.config.latitude}`);
362
+ } else {
363
+ adapter.log.error('system settings cannot be called up. Please check the geo data');
364
+ }
365
+ } catch (err) {
366
+ adapter.log.warn('system settings cannot be called up. Please check configuration!');
367
+ }
368
+ }
369
+ }
370
+
371
+
372
+ /**
373
+ * Schreiben des nächsten Starts in '.actualSoilMoisture'
374
+ * oder Rückgabe des DayName für den nächsten Start
375
+ *
376
+ * @param sprinkleID - Number of Array[0...]
377
+ * @param returnOn - true = Rückgabe des Wochentags; false = Schreiben in State /.actualSoilMoisture
378
+ * @returns nextStart ['Sun','Mon','Tue','Wed','Thur','Fri','Sat']
379
+ */
380
+ function curNextFixDay (sprinkleID, returnOn) {
381
+ const weekDayArray = myConfig.config[sprinkleID].startFixDay;
382
+ const objPfad = `sprinkle.${myConfig.config[sprinkleID].objectName}`;
383
+ const weekday = ['Sun','Mon','Tue','Wed','Thur','Fri','Sat'];
384
+ let found = false;
385
+ let curDay = formatTime(adapter, '', 'day');
386
+ if (startTimeStr <= formatTime(adapter, '', 'hh:mm')) {
387
+ curDay++
388
+ }
389
+ for ( let i=0; i<7; i++ ) {
390
+ if (curDay > 6) {
391
+ curDay = curDay - 7;
392
+ }
393
+ if (weekDayArray[curDay] === true) {
394
+ found = true;
395
+ if (returnOn) {
396
+ return weekday[curDay];
397
+ } else {
398
+ adapter.setState(`${objPfad}.actualSoilMoisture`, {
399
+ val: curDay,
400
+ ack: true
401
+ });
402
+ }
403
+ break;
404
+ }
405
+ curDay++;
406
+ }
407
+ if (returnOn && found === false) {
408
+ return 'off';
409
+ }
410
+ }
411
+
412
+ //
413
+ /**
414
+ * Sets the status at start to a defined value
415
+ * → Setzt den Status beim Start auf einen definierten Wert
416
+ */
417
+ function checkStates() {
418
+ /**
419
+ * control.Holiday
420
+ *
421
+ * @param {string|null} err
422
+ * @param {ioBroker.State|null|undefined} state
423
+ */
424
+ adapter.getState('control.Holiday', (err, state) => {
425
+ if (state && (state.val == null)) {
426
+ adapter.setState('control.Holiday', {val: false, ack: true});
427
+ }
428
+ });
429
+ /**
430
+ * control.autoOnOff
431
+ *
432
+ * @param {string|null} err
433
+ * @param {ioBroker.State|null|undefined} state
434
+ */
435
+ adapter.getState('control.autoOnOff', (err, state) => {
436
+ if (state && (typeof state.val === 'boolean')) {
437
+ autoOnOffStr = state.val;
438
+ } else {
439
+ autoOnOffStr = true;
440
+ adapter.setState('control.autoOnOff', {
441
+ val: autoOnOffStr,
442
+ ack: true
443
+ });
444
+ }
445
+ });
446
+ adapter.getState('evaporation.ETpToday', (err, state) => {
447
+ if (state && (state.val == null)) {
448
+ evaporation.setETpTodayNum(0);
449
+ adapter.setState('evaporation.ETpToday', {
450
+ val: 0,
451
+ ack: true
452
+ });
453
+ } else if (state) {
454
+ evaporation.setETpTodayNum(parseFloat(state.val));
455
+ }
456
+ });
457
+ adapter.getState('evaporation.ETpYesterday', (err, state) => {
458
+ if (state && (state.val == null || state.val === false)) {
459
+ adapter.setState('evaporation.ETpYesterday', {
460
+ val: 0,
461
+ ack: true
462
+ });
463
+ }
464
+ });
465
+
466
+ // akt. kW ermitteln für history last week
467
+ kwStr = formatTime(adapter, '','kW');
468
+ today = formatTime(adapter,'', 'day');
469
+ }
470
+
471
+ /**
472
+ * aktuelle States checken nach dem Start (2000 ms) wenn alle Sprenger-Kreise angelegt wurden
473
+ */
474
+ async function checkActualStates () {
475
+
476
+ try {
477
+ /**
478
+ * switch Holiday
479
+ *
480
+ */
481
+ const _holiday = await adapter.getStateAsync('control.Holiday');
482
+ if (_holiday && _holiday.val && typeof _holiday.val === 'boolean') {
483
+ holidayStr = _holiday.val;
484
+ }
485
+
486
+ /**
487
+ * switch autoOnOff
488
+ *
489
+ */
490
+ const _autoOnOff = await adapter.getStateAsync('control.autoOnOff');
491
+ if (_autoOnOff && _autoOnOff.val && typeof _autoOnOff.val === 'boolean') {
492
+ autoOnOffStr = _autoOnOff.val;
493
+ }
494
+
495
+ if (adapter.config.publicHolidays === true && (adapter.config.publicHolInstance !== 'none' || adapter.config.publicHolInstance !== '')) {
496
+ /**
497
+ * Feiertag Heute
498
+ *
499
+ */
500
+ const _publicHolInstanceHeute = await adapter.getForeignStateAsync(
501
+ `${adapter.config.publicHolInstance }.heute.boolean`
502
+ ).catch((e) => adapter.log.warn(e));
503
+ if (_publicHolInstanceHeute && _publicHolInstanceHeute.val) {
504
+ publicHolidayStr = _publicHolInstanceHeute.val;
505
+ }
506
+ /**
507
+ * Feiertag MORGEN
508
+ *
509
+ */
510
+ const _publicHolInstanceMorgen = await adapter.getForeignStateAsync(
511
+ `${adapter.config.publicHolInstance }.morgen.boolean`
512
+ ).catch((e) => adapter.log.warn(e));
513
+ if (_publicHolInstanceMorgen && _publicHolInstanceMorgen.val) {
514
+ publicHolidayTomorrowStr = _publicHolInstanceMorgen.val;
515
+ }
516
+ }
517
+
518
+ if (adapter.config.weatherForecast === true && (adapter.config.weatherForInstance !== 'none' || adapter.config.weatherForInstance !== '')) {
519
+ /**
520
+ * Niederschlagsmenge HEUTE in mm
521
+ *
522
+ */
523
+ const _weatherForInstanceToday = await adapter.getForeignStateAsync(
524
+ weatherForecastTodayPfadStr
525
+ ).catch((e) => adapter.log.warn(e));
526
+ if (_weatherForInstanceToday && _weatherForInstanceToday.val) {
527
+ if (typeof _weatherForInstanceToday.val == 'string') {
528
+ weatherForecastTodayNum = parseFloat(_weatherForInstanceToday.val);
529
+ } else if (typeof _weatherForInstanceToday.val == 'number') {
530
+ weatherForecastTodayNum = _weatherForInstanceToday.val;
531
+ } else {
532
+ weatherForecastTodayNum = 0;
533
+ adapter.log.info(`checkActualStates => Wettervorhersage state.val ( ${_weatherForInstanceToday.val}); ${typeof _weatherForInstanceToday.val} kann nicht als Number verarbeitet werden`);
534
+ }
535
+ await adapter.setStateAsync('info.rainToday',
536
+ weatherForecastTodayNum,
537
+ true
538
+ );
539
+ }
540
+
541
+ /**
542
+ * Niederschlagsmenge MORGEN in mm
543
+ *
544
+ */
545
+ const _weatherForInstance = await adapter.getForeignStateAsync(
546
+ `${adapter.config.weatherForInstance }.NextDaysDetailed.Location_1.Day_2.rain_value`
547
+ ).catch((e) => adapter.log.warn(e));
548
+ if (_weatherForInstance && _weatherForInstance.val) {
549
+ weatherForecastTomorrowNum = _weatherForInstance.val;
550
+ await adapter.setStateAsync(
551
+ 'info.rainTomorrow',
552
+ weatherForecastTomorrowNum,
553
+ true
554
+ );
555
+ }
556
+ }
557
+
558
+ // wenn (...sprinkleName.autoOn == false[off]) so wird der aktuelle Sprenger [sprinkleName]
559
+ // bei false nicht automatisch gestartet
560
+ /**
561
+ * Abfrage von ...sprinkleName.autoOn
562
+ *
563
+ */
564
+ const result = myConfig.config;
565
+ if (result) {
566
+ for (const res of result) {
567
+ /**
568
+ * Abfrage ... .autoOn beim Start
569
+ *
570
+ */
571
+ const _autoOn = await adapter.getForeignStateAsync(
572
+ res.autoOnID
573
+ ).catch((e) => adapter.log.warn(e));
574
+ if (_autoOn && typeof _autoOn.val === 'boolean') {
575
+ res.autoOn = _autoOn.val;
576
+ if (_autoOn.val === false) {
577
+ adapter.log.info(`get ${res.objectName}.autoOn = ${res.autoOn}`);
578
+ }
579
+ }
580
+ }
581
+ }
582
+
583
+ if (adapter.config.actualValueLevel){
584
+ /**
585
+ * Füllstand der Zisterne in % holen
586
+ *
587
+ */
588
+ const _actualValueLevel = await adapter.getForeignStateAsync(
589
+ adapter.config.actualValueLevel
590
+ ).catch((e) => adapter.log.warn(e));
591
+ if (_actualValueLevel && typeof parseFloat(_actualValueLevel.val) === 'number') {
592
+ valveControl.setFillLevelCistern(parseFloat(_actualValueLevel.val));
593
+ }
594
+ }
595
+ /**
596
+ * return the saved objects under sprinkle.*
597
+ * rückgabe der gespeicherten Objekte unter sprinkle.*
598
+ *
599
+ */
600
+ const _list = await adapter.getForeignObjectsAsync(`${adapter.namespace }.sprinkle.*`, 'channel').catch((e) => adapter.log.warn(e));
601
+ if (_list) {
602
+ ObjSprinkle = _list;
603
+ }
604
+
605
+ } catch (e) {
606
+ adapter.log.warn(`sprinkleControl cannot check actual States ... Please check your sprinkleControl states: ${e}`);
607
+ }
608
+
609
+ }
610
+
611
+
612
+ /**
613
+ * at 0:05 start of StartTimeSprinkle
614
+ * => um 0:05 start von StartTimeSprinkle
615
+ * (..., '(s )m h d m wd')
616
+ */
617
+ const calcPos = schedule.scheduleJob('calcPosTimer', '5 0 * * *', function() {
618
+ // Berechnungen mittels SunCalc
619
+ sunPos();
620
+ today = formatTime(adapter,'', 'day');
621
+
622
+ // History Daten aktualisieren, wenn eine neue Woche beginnt
623
+ adapter.log.debug(`calcPos 0:05 old-KW: ${kwStr} new-KW: ${formatTime(adapter, '','kW')} if: ${(kwStr !== formatTime(adapter, '','kW'))}`);
624
+ if (kwStr !== formatTime(adapter, '','kW')) {
625
+ const result = myConfig.config;
626
+ if (result) {
627
+ for(const i in result) {
628
+ if (Object.hasOwn(result, i)) {
629
+ const objectName = result[i].objectName;
630
+ adapter.getState(`sprinkle.${objectName}.history.curCalWeekConsumed`, (err, state) => {
631
+ if (state) {
632
+ adapter.setState(`sprinkle.${objectName}.history.lastCalWeekConsumed`, { val: state.val, ack: true });
633
+ adapter.setState(`sprinkle.${objectName}.history.curCalWeekConsumed`, { val: 0, ack: true });
634
+ }
635
+ });
636
+ adapter.getState(`sprinkle.${objectName}.history.curCalWeekRunningTime`, (err, state) => {
637
+ if (state) {
638
+ adapter.setState(`sprinkle.${objectName}.history.lastCalWeekRunningTime`, { val: state.val, ack: true });
639
+ adapter.setState(`sprinkle.${objectName}.history.curCalWeekRunningTime`, { val: '00:00', ack: true });
640
+ }
641
+ });
642
+ }
643
+
644
+ }
645
+ }
646
+ kwStr = formatTime(adapter, '','kW');
647
+ }
648
+
649
+ // ETpToday und ETpYesterday in evaporation aktualisieren da ein neuer Tag
650
+ evaporation.setNewDay();
651
+
652
+ // Startzeit Festlegen verzögert wegen Daten von SunCalc
653
+ setTimeout(() => {
654
+ startTimeSprinkle();
655
+ addStartTimeSprinkle();
656
+ },1000);
657
+
658
+ });
659
+
660
+ // Berechnung mittels sunCalc
661
+ function sunPos() {
662
+ // get today's sunlight times → Holen Sie sich die heutige Sonnenlichtzeit
663
+ const times = SunCalc.getTimes(new Date(), adapter.config.latitude, adapter.config.longitude);
664
+
665
+ // format sunrise time from the Date object → Formatieren Sie die Sonnenaufgangszeit aus dem Date-Objekt
666
+ sunriseStr = `${(`0${times.sunrise.getHours()}`).slice(-2)}:${(`0${times.sunrise.getMinutes()}`).slice(-2)}`;
667
+
668
+ // format golden hour end time from the Date object → Formatiere golden hour end time aus dem Date-Objekt
669
+ goldenHourEnd = `${(`0${times.goldenHourEnd.getHours()}`).slice(-2)}:${(`0${times.goldenHourEnd.getMinutes()}`).slice(-2)}`;
670
+
671
+ // format sunset time from the Date object formatieren Sie die Sonnenuntergangszeit aus dem Date-Objekt
672
+ sunsetStr = sunsetStr = `${(`0${times.sunset.getHours()}`).slice(-2)}:${(`0${times.sunset.getMinutes()}`).slice(-2)}`;
673
+
674
+ }
675
+
676
+ function addStartTimeSprinkle() {
677
+ schedule.cancelJob('sprinkleAddStartTime');
678
+ if (adapter.config.selectAddStartTime === 'greaterETpCurrent' || adapter.config.selectAddStartTime === 'withExternalSignal') {
679
+ const addStartTimeSplit = adapter.config.addWateringStartTime.split(':');
680
+ const scheduleAddStartTime = schedule.scheduleJob('sprinkleAddStartTime', `${addStartTimeSplit[1]} ${addStartTimeSplit[0]} * * *`, function() {
681
+ // if (autoOnOff == false) => keine auto Start
682
+ if (!autoOnOffStr) {
683
+ schedule.cancelJob('sprinkleAddStartTime');
684
+ return;
685
+ }
686
+ if (((adapter.config.selectAddStartTime === 'greaterETpCurrent') && (adapter.config.triggerAddStartTimeETpCur < evaporation.getETpTodayNum()))
687
+ || (adapter.config.selectAddStartTime === 'withExternalSignal' && addStartTimeSwitch)) {
688
+ let messageText = '';
689
+
690
+ // Filter enabled
691
+ const result = myConfig.config.filter(d => d.enabled === true);
692
+ if (result) {
693
+ /**
694
+ * Array zum flüchtigen Sammeln von Bewässerungsaufgaben
695
+ *
696
+ */
697
+ const memAddList = [];
698
+
699
+ /**
700
+ * result Rain
701
+ * - (aktuelle Wettervorhersage - Schwellwert der Regenberücksichtigung) wenn Sensor sich im Freien befindet
702
+ * - (> 0) es regnet - Abbruch -
703
+ * - (≤ 0) Start der Bewässerung
704
+ *
705
+ * @param inGreenhouse - Sensor befindet sich im Gewächshaus
706
+ * @returns - resultierende Regenmenge
707
+ */
708
+ function resRain (inGreenhouse) {
709
+ return (adapter.config.weatherForecast && !inGreenhouse) ? (((+ weatherForecastTodayNum) - parseFloat(adapter.config.thresholdRain)).toFixed(1)) : 0;
710
+ }
711
+
712
+ for(const res of result) {
713
+ if (res.autoOn // Ventil aktiv
714
+ && (res.addWateringTime > 0) // zusätzliche Bewässerung aktiv time > 0
715
+ && (resRain(res.inGreenhouse) <= 0)) { // keine Regenvorhersage
716
+
717
+ switch (res.methodControlSM) {
718
+ case 'bistable': {
719
+ if (res.soilMoisture.bool) {
720
+ messageText += `<b>${res.objectName}</b> (${res.soilMoisture.bool})\n`
721
+ + ` START => ${addTime(res.addWateringTime, '')}\n`;
722
+ memAddList.push({
723
+ auto: true,
724
+ sprinkleID: res.sprinkleID,
725
+ wateringTime: res.addWateringTime
726
+ });
727
+ }
728
+ break;
729
+ }
730
+
731
+ case 'fixDay': {
732
+ messageText += `<b>${res.objectName}</b>\n`
733
+ + ` START => ${addTime(Math.round(60 * res.addWateringTime), '')}\n`;
734
+ memAddList.push({
735
+ auto: true,
736
+ sprinkleID: res.sprinkleID,
737
+ wateringTime: Math.round(60 * res.addWateringTime)
738
+ });
739
+ break;
740
+ }
741
+
742
+ case 'calculation': {
743
+ const addCountdown = res.wateringTime * (res.soilMoisture.maxIrrigation - res.soilMoisture.val) / (res.soilMoisture.maxIrrigation - res.soilMoisture.triggersIrrigation) - res.wateringTime;
744
+ adapter.log.debug(`addCountdown: ${addCountdown}, addWateringTime: ${res.addWateringTime}, if(${(addCountdown - res.addWateringTime) > 0})`);
745
+ if ((addCountdown - res.addWateringTime) > 0) {
746
+ messageText += `<b>${res.objectName}</b> ${res.soilMoisture.pct}% (${res.soilMoisture.pctTriggerIrrigation}%)\n`
747
+ + ` START => ${addTime(Math.round(60 * addCountdown), '')}\n`;
748
+ memAddList.push({
749
+ auto: true,
750
+ sprinkleID: res.sprinkleID,
751
+ wateringTime: Math.round(60 * addCountdown)
752
+ });
753
+ }
754
+ break;
755
+ }
756
+
757
+ case 'analog': {
758
+ if (res.soilMoisture.pct < res.soilMoisture.pctAddTriggersIrrigation) {
759
+ messageText += `<b>${res.objectName}</b> ${res.soilMoisture.pct} %(${res.soilMoisture.pctAddTriggersIrrigation}%)\n`
760
+ + ` START => ${addTime(Math.round(60 * res.addWateringTime), '')}\n`;
761
+ memAddList.push({
762
+ auto: true,
763
+ sprinkleID: res.sprinkleID,
764
+ wateringTime: Math.round(60 * res.addWateringTime)
765
+ });
766
+ }
767
+ break;
768
+ }
769
+
770
+ }
771
+ } else {
772
+ adapter.log.debug(`${res.objectName}: autoOn (${res.autoOn}) && addWateringTime (${res.addWateringTime} > 0) && resRain (${resRain(res.inGreenhouse)}) <= 0, if(${res.autoOn && (res.addWateringTime > 0) && (resRain(res.inGreenhouse) <= 0)})`);
773
+ }
774
+ }
775
+ valveControl.addList(memAddList);
776
+ }
777
+ if(!sendMessageText.onlySendError() && messageText.length > 0){
778
+ sendMessageText.sendMessage(messageText);
779
+ }
780
+ } else {
781
+ adapter.log.debug(`greaterETpCurrent: ${(adapter.config.selectAddStartTime === 'greaterETpCurrent')} & ${(adapter.config.triggerAddStartTimeETpCur < evaporation.getETpTodayNum())}, withExternalSignal; ${(adapter.config.selectAddStartTime === 'withExternalSignal')} & ${addStartTimeSwitch}`);
782
+ }
783
+ setTimeout(()=>{
784
+ schedule.cancelJob('sprinkleAddStartTime');
785
+ }, 200);
786
+ });
787
+ }
788
+ }
789
+
790
+ // Determination of the irrigation time => Bestimmung der Bewässerungszeit
791
+ function startTimeSprinkle() {
792
+ let startTimeSplit = [];
793
+ let infoMessage;
794
+ let messageText = '';
795
+
796
+ schedule.cancelJob('sprinkleStartTime');
797
+
798
+ // if (autoOnOff == false) => keine auto Start
799
+ if (!autoOnOffStr) {
800
+ adapter.log.info(`Sprinkle: autoOnOff == Aus ( ${autoOnOffStr} )`);
801
+ adapter.setState('info.nextAutoStart', {
802
+ val: 'autoOnOff = off(0)',
803
+ ack: true
804
+ });
805
+ return;
806
+ }
807
+
808
+ /**
809
+ * next start time (automatic)
810
+ * Berechnung des nächsten Starts (Automatik)
811
+ *
812
+ * @returns
813
+ */
814
+ function nextStartTime () {
815
+ let newStartTime;
816
+ let run = 0;
817
+ const curTime = new Date();
818
+ const myHours = checkTime(curTime.getHours());
819
+ const myMinutes = checkTime(curTime.getMinutes());
820
+ let myWeekday = curTime.getDay();
821
+ const myWeekdayStr = ['So','Mo','Di','Mi','Do','Fr','Sa'];
822
+ const myTime = `${myHours}:${myMinutes}`;
823
+
824
+ /**
825
+ * aus 0...9 wird String 00...09
826
+ *
827
+ * @param i
828
+ * @returns
829
+ */
830
+ function checkTime(i) {
831
+ return (+i < 10) ? `0${i}` : i;
832
+ }
833
+
834
+ do {
835
+ myWeekday += run;
836
+ run++;
837
+ if (myWeekday>6){
838
+ myWeekday=0;
839
+ }
840
+ // Start time variant according to configuration => Startzeitvariante gemäß Konfiguration
841
+ switch(adapter.config.wateringStartTime) {
842
+ case 'livingTime' : /*Startauswahl = festen Zeit*/
843
+ infoMessage = 'Start zur festen Zeit ';
844
+ newStartTime = adapter.config.weekLiving;
845
+ break;
846
+ case 'livingSunrise' : /*Startauswahl = Sonnenaufgang*/
847
+ infoMessage = 'Start mit Sonnenaufgang ';
848
+ // format sunrise time from the Date object
849
+ newStartTime = addTime(sunriseStr, parseInt(adapter.config.timeShift));
850
+ break;
851
+ case 'livingGoldenHourEnd' : /*Startauswahl = Ende der Golden Hour*/
852
+ infoMessage = 'Start zum Ende der Golden Hour ';
853
+ // format goldenHourEnd time from the Date object
854
+ newStartTime = goldenHourEnd;
855
+ break;
856
+ case 'livingSunset' : /*Startauswahl = Sonnenuntergang*/
857
+ infoMessage = 'Start mit Sonnenuntergang ';
858
+ // format sunset time from the Date object
859
+ newStartTime = addTime(sunsetStr, parseInt(adapter.config.timeShift));
860
+ break;
861
+ }
862
+ // Start am Wochenende →, wenn andere Zeiten verwendet werden soll
863
+ if((adapter.config.publicWeekend) && ((myWeekday === 6) || (myWeekday === 0))){
864
+ infoMessage = 'Start am Wochenende ';
865
+ newStartTime = adapter.config.weekEndLiving;
866
+ }
867
+ // Start an Feiertagen →, wenn Zeiten des Wochenendes verwendet werden soll
868
+ if((adapter.config.publicHolidays) && (adapter.config.publicWeekend)
869
+ && (((publicHolidayStr === true) && (run === 1)) // heute Feiertag && erster Durchlauf
870
+ || ((publicHolidayTomorrowStr === true) && (run === 2)) // morgen Feiertag && zweiter Durchlauf
871
+ || (holidayStr === true))) { // Urlaub
872
+ infoMessage = 'Start am Feiertag ';
873
+ newStartTime = adapter.config.weekEndLiving;
874
+ }
875
+ } while ((newStartTime <= myTime) && (run === 1));
876
+
877
+ const newStartTimeLong = `${myWeekdayStr[myWeekday]} ${newStartTime}`;
878
+ /**
879
+ * next Auto-Start
880
+ *
881
+ * @param {string|null} err
882
+ * @param {ioBroker.State|null|undefined} state
883
+ */
884
+ adapter.getState('info.nextAutoStart', (err, state) =>{
885
+ if (state) {
886
+ if (state.val !== newStartTimeLong) {
887
+ adapter.setState('info.nextAutoStart', {
888
+ val: newStartTimeLong,
889
+ ack: true
890
+ });
891
+ // next Start Message
892
+ if(!sendMessageText.onlySendError){
893
+ sendMessageText.sendMessage(`${infoMessage}(${myWeekdayStr[myWeekday]}) um ${newStartTime}`);
894
+ }
895
+ adapter.log.info(`${infoMessage} (${myWeekdayStr[myWeekday]}) um ${newStartTime}`);
896
+ }
897
+ }
898
+ });
899
+ return newStartTime;
900
+ }
901
+ //
902
+ startTimeStr = nextStartTime();
903
+ startTimeSplit = startTimeStr.split(':');
904
+
905
+ const scheduleStartTime = schedule.scheduleJob('sprinkleStartTime', `${startTimeSplit[1]} ${startTimeSplit[0]} * * *`, function() {
906
+ startOfIrrigation();
907
+ setTimeout (() => {
908
+ setTimeout(()=>{
909
+ nextStartTime();
910
+ }, 800);
911
+ schedule.cancelJob('sprinkleStartTime');
912
+ }, 200);
913
+ });
914
+ }
915
+
916
+ const startOfIrrigation = () => {
917
+ let messageText = '';
918
+
919
+ // Filter enabled
920
+ const result = myConfig.config.filter(d => d.enabled === true);
921
+ if (result) {
922
+ /**
923
+ * Array zum flüchtigen Sammeln von Bewässerungsaufgaben
924
+ *
925
+ */
926
+ const memAddList = [];
927
+
928
+ /**
929
+ * result Rain
930
+ * - (aktuelle Wettervorhersage - Schwellwert der Regenberücksichtigung) wenn Sensor sich im Freien befindet
931
+ * - (> 0) es regnet - Abbruch -
932
+ * - (≤ 0) Start der Bewässerung
933
+ *
934
+ * @param inGreenhouse - Sensor befindet sich im Gewächshaus
935
+ * @returns - resultierende Regenmenge
936
+ */
937
+ function resRain (inGreenhouse) {
938
+ return (adapter.config.weatherForecast && !inGreenhouse) ? (((+ weatherForecastTodayNum) - parseFloat(adapter.config.thresholdRain)).toFixed(1)) : 0;
939
+ }
940
+ // Bewässerung mit Zisterne? Füllstand anzeigen
941
+ if (adapter.config.cisternSettings === true) messageText += `${valveControl.getStatusCistern()}\n`;
942
+
943
+ for(const res of result) {
944
+ let messageTextZeile1 = '';
945
+ let messageTextZeile2 = '';
946
+ messageTextZeile1 = `<b>${res.objectName}</b>`;
947
+ switch (res.methodControlSM) {
948
+ case 'bistable':
949
+ messageTextZeile1 += ` (${res.soilMoisture.bool})`;
950
+ break;
951
+ case 'analog':
952
+ messageTextZeile1 += ` ${res.soilMoisture.pct}% (${res.soilMoisture.pctTriggerIrrigation}%)`;
953
+ break;
954
+ case 'fixDay':
955
+ //messageTextZeile1 += ` (${curNextFixDay(res.sprinkleID, true)})`;
956
+ break;
957
+ case 'calculation':
958
+ messageTextZeile1 += ` ${res.soilMoisture.pct}% (${res.soilMoisture.pctTriggerIrrigation}%)`;
959
+ break;
960
+ }
961
+
962
+ // Test Bodenfeuchte
963
+ adapter.log.debug(`Bodenfeuchte: ${res.soilMoisture.val} <= ${res.soilMoisture.triggersIrrigation} AutoOn: ${res.autoOn}`);
964
+ if (res.autoOn) {
965
+ switch (res.methodControlSM) {
966
+ // -- bistable -- Bodenfeuchte-Sensor mit 2-Punkt-Regler true und false -- //
967
+ case 'bistable':
968
+ if(res.soilMoisture.bool) {
969
+ /* Wenn in der Config Regenvorhersage aktiviert: Startvorgang abbrechen, wenn der Regen den eingegebenen Schwellwert überschreitet. */
970
+ if (resRain(res.inGreenhouse) <= 0) {
971
+ const curWateringTime = Math.round(60 * res.wateringTime * evaporation.timeExtension(res.wateringAdd));
972
+ memAddList.push({
973
+ auto: true,
974
+ sprinkleID: res.sprinkleID,
975
+ wateringTime: curWateringTime
976
+ });
977
+ messageTextZeile2 += `START => ${addTime(curWateringTime, '')}`;
978
+ } else if (adapter.config.weatherForecast) {
979
+ /* Bewässerung unterdrückt da ausreichende Regenvorhersage */
980
+ messageTextZeile2 += `<i>Start verschoben, da heute ${weatherForecastTodayNum}mm Niederschlag</i>`;
981
+ adapter.log.info(`${res.objectName}: Start verschoben, da Regenvorhersage für Heute ${weatherForecastTodayNum} mm [ ${resRain(res.inGreenhouse)} > 0 ]`);
982
+ }
983
+ }
984
+ break;
985
+ // --- analog -- Bodenfeuchte-Sensor im Wertebereich von 0 bis 100% --- //
986
+ // -- Prozentuale Bodenfeuchte zu gering -- //
987
+ case 'analog':
988
+ if(res.soilMoisture.pct <= res.soilMoisture.pctTriggerIrrigation) {
989
+ /* Wenn in der Config Regenvorhersage aktiviert: Startvorgang abbrechen, wenn der Regen den eingegebenen Schwellwert überschreitet. */
990
+ if (resRain(res.inGreenhouse) <= 0) {
991
+ let countdown = res.wateringTime * (100 - res.soilMoisture.pct) / (100 - res.soilMoisture.pctTriggerIrrigation); // in min
992
+ // Begrenzung der Bewässerungszeit auf dem in der Config eingestellten Überschreitung (in Prozent)
993
+ if (countdown > (res.wateringTime * res.wateringAdd / 100)) {
994
+ countdown = res.wateringTime * res.wateringAdd / 100;
995
+ }
996
+ memAddList.push({
997
+ auto: true,
998
+ sprinkleID: res.sprinkleID,
999
+ wateringTime: Math.round(60* countdown)
1000
+ });
1001
+ messageTextZeile2 += `START => ${addTime(Math.round(60*countdown), '')}`;
1002
+ } else if (adapter.config.weatherForecast) {
1003
+ /* Bewässerung unterdrückt da ausreichende Regenvorhersage */
1004
+ messageTextZeile2 += `<i>Start verschoben, da heute ${weatherForecastTodayNum}mm Niederschlag</i> `;
1005
+ adapter.log.info(`${res.objectName}: Start verschoben, da Regenvorhersage für Heute ${weatherForecastTodayNum} mm [ ${resRain(res.inGreenhouse)} > 0 ]`);
1006
+ }
1007
+ }
1008
+ break;
1009
+ // --- fixDay -- Start an festen Tagen ohne Sensoren --- //
1010
+ // -- Bewässerungstag erreicht //
1011
+ case 'fixDay':
1012
+ /* Wenn in der Config Regenvorhersage aktiviert: Startvorgang abbrechen, wenn der Regen den eingegebenen Schwellwert überschreitet. */
1013
+ if (resRain(res.inGreenhouse) <= 0) {
1014
+ // Bewässerungstag erreicht
1015
+ if (res.startFixDay[today]) {
1016
+ const curWateringTime = Math.round(60 * res.wateringTime * evaporation.timeExtension(res.wateringAdd));
1017
+ memAddList.push({
1018
+ auto: true,
1019
+ sprinkleID: res.sprinkleID,
1020
+ wateringTime: curWateringTime
1021
+ });
1022
+ messageTextZeile2 += `START => ${addTime(curWateringTime, '')}`;
1023
+ if (res.startDay === 'threeRd'){ // Next Start in 3 Tagen
1024
+ res.startFixDay[today] = false;
1025
+ res.startFixDay[(+ today + 3 > 6) ? (+ today-4) : (+ today+3)] = true;
1026
+ }else if (res.startDay === 'twoNd') { // Next Start in 2 Tagen
1027
+ res.startFixDay[today] = false;
1028
+ res.startFixDay[(+ today + 2 > 6) ? (+ today-5) : (+ today+2)] = true;
1029
+ }
1030
+ }
1031
+ } else if (adapter.config.weatherForecast){
1032
+ messageTextZeile2 += `<i>Start verschoben, da heute ${weatherForecastTodayNum}mm Niederschlag</i>`;
1033
+ adapter.log.info(`${res.objectName}: Start verschoben, da Regenvorhersage für Heute ${weatherForecastTodayNum} mm [ ${resRain(false)} > 0 ]`);
1034
+ if ((res.startDay === 'threeRd') || (res.startDay === 'twoNd')) {
1035
+ let startDay = -1;
1036
+ res.startFixDay.forEach((item, index) => {
1037
+ if (item) {
1038
+ startDay = index;
1039
+ }
1040
+ });
1041
+ if (startDay !== -1) {
1042
+ res.startFixDay[startDay] = false;
1043
+ res.startFixDay[(+ startDay + 1 > 6) ? (+ startDay-6) : (+ startDay+1)] = true;
1044
+ } else {
1045
+ adapter.log.warn(`${res.objectName}: no start day found`);
1046
+ }
1047
+ }
1048
+ }
1049
+ curNextFixDay(res.sprinkleID, false);
1050
+ messageTextZeile1 += ` (${curNextFixDay(res.sprinkleID, true)})`;
1051
+ break;
1052
+ // --- calculation -- Berechnung der Bodenfeuchte --- //
1053
+ // -- Bodenfeuchte zu gering -- //
1054
+ case 'calculation':
1055
+ if (res.soilMoisture.val <= res.soilMoisture.triggersIrrigation) {
1056
+ /* Wenn in der Config Regenvorhersage aktiviert: Startvorgang abbrechen, wenn es heute ausreichend regnen sollte. */
1057
+ const resMoisture = (adapter.config.weatherForecast)?((+ res.soilMoisture.val) + (+ weatherForecastTodayNum) - parseFloat(adapter.config.thresholdRain)):(res.soilMoisture.val); // aktualisierte Bodenfeuchte mit Regenvorhersage
1058
+ if ((resMoisture <= res.soilMoisture.triggersIrrigation) || res.inGreenhouse) { // Kontrolle ob Regenvorhersage ausreicht || Bewässerung inGreenhouse
1059
+ let countdown = res.wateringTime * (res.soilMoisture.maxIrrigation - res.soilMoisture.val) / (res.soilMoisture.maxIrrigation - res.soilMoisture.triggersIrrigation); // in min
1060
+ // Begrenzung der Bewässerungszeit auf dem in der Config eingestellten Überschreitung (in Prozent)
1061
+ if (countdown > (res.wateringTime * res.wateringAdd / 100)) {
1062
+ countdown = res.wateringTime * res.wateringAdd / 100;
1063
+ }
1064
+ memAddList.push({
1065
+ auto: true,
1066
+ sprinkleID: res.sprinkleID,
1067
+ wateringTime: Math.round(60*countdown)
1068
+ });
1069
+ messageTextZeile2 += `START => ${addTime(Math.round(60*countdown), '')}`;
1070
+ } else if (adapter.config.weatherForecast) {
1071
+ /* Bewässerung unterdrückt da ausreichende Regenvorhersage */
1072
+ messageTextZeile2 += `<i>Start verschoben, da heute ${weatherForecastTodayNum}mm Niederschlag</i>`;
1073
+ adapter.log.info(`${res.objectName}: Start verschoben, da Regenvorhersage für Heute ${weatherForecastTodayNum} mm [ ${res.soilMoisture.val.toFixed(1)} (${resMoisture.toFixed(1)}) <= ${res.soilMoisture.triggersIrrigation} ]`);
1074
+ }
1075
+ }
1076
+ break;
1077
+ }
1078
+ } else {
1079
+ messageTextZeile2 += `<i>Ventil auf Handbetrieb</i> `;
1080
+ }
1081
+ messageText += (messageTextZeile2.length > 0) ? (`${messageTextZeile1}\n ${messageTextZeile2}\n`) : (`${messageTextZeile1}\n`);
1082
+ }
1083
+ valveControl.addList(memAddList);
1084
+ }
1085
+ if(!sendMessageText.onlySendError()){
1086
+ sendMessageText.sendMessage(messageText);
1087
+ }
1088
+ }
1089
+
1090
+ //
1091
+ async function createSprinklers() {
1092
+ /**Creates an Object .control.addStartTimeSwitch, when additional watering has been activated via an external signal
1093
+ * - Erzeugt ein Object .control.addStartTimeSwitch, wenn die Zusatzbewässerung über ein externes Signal aktiviert wurde
1094
+ * @type {{id: string, name: string} | void}
1095
+ * @private
1096
+ */
1097
+ const _addStartTimeSwitch = await adapter.findForeignObjectAsync(`${adapter.namespace}.control.addStartTimeSwitch`, 'boolean').catch((e) => adapter.log.warn(`.control.addStartTimeSwitch ${e}`));
1098
+ if (_addStartTimeSwitch.id !== `${adapter.namespace}.control.addStartTimeSwitch` && adapter.config.selectAddStartTime === 'withExternalSignal') {
1099
+ adapter.setObjectNotExistsAsync(`${adapter.namespace}.control.addStartTimeSwitch`, {
1100
+ type: 'state',
1101
+ common: {
1102
+ role: 'switch',
1103
+ name: {
1104
+ en: "additional irrigation enabled",
1105
+ de: "weitere Bewässerung aktiviert",
1106
+ ru: "включено дополнительное орошение",
1107
+ pt: "irrigação adicional ativada",
1108
+ nl: "extra irrigatie mogelijk",
1109
+ fr: "arrosage supplémentaire activé",
1110
+ it: "irrigazione extra abilitata",
1111
+ es: "riego adicional habilitado",
1112
+ pl: "włączone dodatkowe nawadnianie",
1113
+ uk: "додаткове зрошення включено",
1114
+ "zh-cn": "已启用额外灌溉"
1115
+ },
1116
+ type: 'boolean',
1117
+ read: true,
1118
+ write: true,
1119
+ def: false
1120
+ },
1121
+ native: {}
1122
+ }).catch((e) => adapter.log.warn(`.control.addStartTimeSwitch ${e}`));
1123
+ } else if (_addStartTimeSwitch.id === `${adapter.namespace}.control.addStartTimeSwitch`) {
1124
+ if (adapter.config.selectAddStartTime !== 'withExternalSignal') {
1125
+ adapter.delObjectAsync(`${adapter.namespace}.control.addStartTimeSwitch`).catch((e) => adapter.log.warn(`.control.addStartTimeSwitch ${e}`));
1126
+ addStartTimeSwitch = false;
1127
+ } else {
1128
+ /**
1129
+ * auslesen .control.addStartTimeSwitch.val
1130
+ */
1131
+ const _state = await adapter.getStateAsync(`${adapter.namespace}.control.addStartTimeSwitch`).catch((e) => adapter.log.warn(`.control.addStartTimeSwitch ${e}`));
1132
+ if (typeof _state.val === 'boolean') {
1133
+ addStartTimeSwitch = _state.val;
1134
+ }
1135
+ }
1136
+ }
1137
+
1138
+
1139
+
1140
+ const result = adapter.config.events;
1141
+ if (result) {
1142
+ for(const res of result) {
1143
+ let objectName;
1144
+
1145
+ if(res.sprinkleName !== '') {
1146
+ objectName = res.sprinkleName.replace(/[.;, ]/g, '_');
1147
+ } else if (res.sprinkleName === '') {
1148
+ objectName = res.name.replace(/[.;, ]/g, '_');
1149
+ }
1150
+
1151
+ const objPfad = `sprinkle.${objectName}`;
1152
+ const j = myConfig.config.findIndex(d => d.objectName === objectName);
1153
+
1154
+ // Create bzw. update .actualSoilMoisture
1155
+
1156
+ let nameMetConSM, objMetConSM;
1157
+ await fillMetConSM(res);
1158
+ async function fillMetConSM(res) {
1159
+ //adapter.log.debug(JSON.stringify(res));
1160
+ switch (res.methodControlSM) {
1161
+ case 'calculation':
1162
+ nameMetConSM = {
1163
+ en: `${objectName} => Calculated soil moisture in %`,
1164
+ de: `${objectName} => Berechnete Bodenfeuchte in%`,
1165
+ ru: `${objectName} => Расчетная влажность почвы в%`,
1166
+ pt: `${objectName} => Umidade do solo calculada em%`,
1167
+ nl: `${objectName} => Berekend bodemvocht in%`,
1168
+ fr: `${objectName} => Humidité du sol calculée en %`,
1169
+ it: `${objectName} => Umidità del suolo in%`,
1170
+ es: `${objectName} => Humedad del suelo calculada en%`,
1171
+ pl: `${objectName} => Obliczona wilgotność gleby w%`,
1172
+ uk: `${objectName} => Розрахована вологість ґрунту в%`,
1173
+ "zh-cn": `${objectName} => 计算出的土壤湿度百分比`
1174
+ };
1175
+ objMetConSM = {
1176
+ type: 'state',
1177
+ common: {
1178
+ role: 'state',
1179
+ name: nameMetConSM,
1180
+ type: 'number',
1181
+ min: 0,
1182
+ max: 150,
1183
+ unit: '%',
1184
+ read: true,
1185
+ write: false,
1186
+ def: 50
1187
+ },
1188
+ native: {},
1189
+ };
1190
+ break;
1191
+ case 'bistable':
1192
+ nameMetConSM = {
1193
+ en: `${objectName} => bistable soil moisture sensor`,
1194
+ de: `${objectName} => Bistabiler Bodenfeuchtesensor`,
1195
+ ru: `${objectName} => бистабильный датчик влажности почвы`,
1196
+ pt: `${objectName} => sensor biestável de umidade`,
1197
+ nl: `${objectName} => bistabiele bodemvochtsensor`,
1198
+ fr: `${objectName} => capteur d'humidité du sol bistable`,
1199
+ it: `${objectName} => sensore di umidità bistabile`,
1200
+ es: `${objectName} => sensor biestable de humedad`,
1201
+ pl: `${objectName} => bistabilny czujnik wilgotności gleby`,
1202
+ uk: `${objectName} => бістабільний датчик вологості ґрунту`,
1203
+ "zh-cn": `${objectName} => 双稳态土壤湿度传感器`
1204
+ };
1205
+ objMetConSM = {
1206
+ type: 'state',
1207
+ common: {
1208
+ role: 'state',
1209
+ name: nameMetConSM,
1210
+ type: 'boolean',
1211
+ read: true,
1212
+ write: false,
1213
+ def: false
1214
+ },
1215
+ native: {},
1216
+ };
1217
+ break;
1218
+ case 'analog':
1219
+ nameMetConSM = {
1220
+ en: `${objectName} => analog soil moisture sensor in %`,
1221
+ de: `${objectName} => analoger Bodenfeuchtesensor in%`,
1222
+ ru: `${objectName} => аналоговый датчик влажности почвы в%`,
1223
+ pt: `${objectName} => sensor analógico de umidade em%`,
1224
+ nl: `${objectName} => analoge bodemvochtsensor in%`,
1225
+ fr: `${objectName} => capteur d'humidité du sol analogique en %`,
1226
+ it: `${objectName} => sensore umidità analogico in%`,
1227
+ es: `${objectName} => sensor analógico de humedad en%`,
1228
+ pl: `${objectName} => analogowy czujnik wilgotności gleby w%`,
1229
+ uk: `${objectName} => аналоговий датчик вологості ґрунту в%`,
1230
+ "zh-cn": `${objectName} => 模拟土壤湿度传感器,单位为%`
1231
+ };
1232
+ objMetConSM = {
1233
+ type: 'state',
1234
+ common: {
1235
+ role: 'state',
1236
+ name: nameMetConSM,
1237
+ type: 'number',
1238
+ min: 0,
1239
+ max: 150,
1240
+ unit: '%',
1241
+ read: true,
1242
+ write: false
1243
+ },
1244
+ native: {},
1245
+ };
1246
+ break;
1247
+ case 'fixDay':
1248
+ nameMetConSM = {
1249
+ en: `${objectName} => start on a fixed day`,
1250
+ de: `${objectName} => start an einem festen Tag`,
1251
+ ru: `${objectName} => начните в определенный день`,
1252
+ pt: `${objectName} => começa em dia fixo`,
1253
+ nl: `${objectName} => start op een vaste dag`,
1254
+ fr: `${objectName} => commence un jour fixe`,
1255
+ it: `${objectName} => inizio a un giorno`,
1256
+ es: `${objectName} => empezar un día fijo`,
1257
+ pl: `${objectName} => zacznij w ustalonym dniu`,
1258
+ uk: `${objectName} => початок у фіксований день`,
1259
+ "zh-cn": `${objectName} => 在固定的日期开始`
1260
+ };
1261
+ objMetConSM = {
1262
+ type: 'state',
1263
+ common: {
1264
+ role: 'state',
1265
+ name: nameMetConSM,
1266
+ type: 'number',
1267
+ min: 0,
1268
+ max: 7,
1269
+ states: {0:'Sun', 1:'Mon', 2:'Tue', 3:'Wed', 4:'Thur', 5:'Fri', 6:'Sat', 7:'off'},
1270
+ read: true,
1271
+ write: false,
1272
+ def: 7
1273
+ },
1274
+ native: {},
1275
+ };
1276
+ break;
1277
+ default:
1278
+ adapter.log.warn(`sprinkleControl cannot created ... Please check your sprinkleControl config ${objectName} methodControl`);
1279
+ nameMetConSM = {
1280
+ en: `${objectName} => Emergency program! start on a fixed day`,
1281
+ de: `${objectName} => Notfallprogramm! start an einem festen Tag`,
1282
+ ru: `${objectName} => Экстренная программа! начните в определенный день`,
1283
+ pt: `${objectName} => Programa urgente! comece em dia fixo`,
1284
+ nl: `${objectName} => Noodprogramma! op een vaste dag beginnen`,
1285
+ fr: `${objectName} => Programme d'urgence ! commencer un jour fixe`,
1286
+ it: `${objectName} => Emergenze! inizio a data fissa`,
1287
+ es: `${objectName} => ¡Programa de emergencia! empezar un día fijo`,
1288
+ pl: `${objectName} => Program awaryjny! zacznij w ustalonym dniu`,
1289
+ uk: `${objectName} => Екстрена програма! початок у визначений день`,
1290
+ "zh-cn": `${objectName} => 紧急计划!从固定的日子开始`
1291
+ };
1292
+ objMetConSM = {
1293
+ type: 'state',
1294
+ common: {
1295
+ role: 'state',
1296
+ name: nameMetConSM,
1297
+ type: 'number',
1298
+ min: 0,
1299
+ max: 7,
1300
+ states: {0:'Sun', 1:'Mon', 2:'Tue', 3:'Wed', 4:'Thur', 5:'Fri', 6:'Sat', 7:'off'},
1301
+ read: true,
1302
+ write: false,
1303
+ def: 7
1304
+ },
1305
+ native: {},
1306
+ };
1307
+ }
1308
+ }
1309
+ // ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ //
1310
+ // +++++ Objekte erstellen +++++ //
1311
+ // ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ //
1312
+ if (objectName && objectName !== '') {
1313
+ try {
1314
+ // Create Object for sprinkle. (ID)
1315
+ const _sprinkleNotExist = await adapter.setObjectNotExistsAsync(`sprinkle.${objectName}`, {
1316
+ type: 'channel',
1317
+ common: {
1318
+ name: res.sprinkleName
1319
+ },
1320
+ native: {},
1321
+ });
1322
+ // Create Object for .history
1323
+ const _historyNotExist = await adapter.setObjectNotExistsAsync(`sprinkle.${objectName}.history`, {
1324
+ type: 'channel',
1325
+ common: {
1326
+ name: {
1327
+ en: `${res.sprinkleName} => History`,
1328
+ de: `${res.sprinkleName} => Verlauf`,
1329
+ ru: `${res.sprinkleName} => История`,
1330
+ pt: `${res.sprinkleName} => História`,
1331
+ nl: `${res.sprinkleName} => Geschiedenis`,
1332
+ fr: `${res.sprinkleName} => Histoire`,
1333
+ it: `${res.sprinkleName} => Storia`,
1334
+ es: `${res.sprinkleName} => Historia`,
1335
+ pl: `${res.sprinkleName} => Historia`,
1336
+ uk: `${res.sprinkleName} => Історія`,
1337
+ "zh-cn": `${res.sprinkleName} => 历史`
1338
+ }
1339
+ },
1340
+ native: {},
1341
+ });
1342
+ // Create Object for .history.curCalWeekConsumed
1343
+ // Sprinkler consumption of the current calendar week => History - Sprinkler-Verbrauch der aktuellen Kalenderwoche (783 Liter)
1344
+ const _curCalWeekConsumedNotExist = adapter.setObjectNotExistsAsync(`${objPfad}.history.curCalWeekConsumed`, {
1345
+ type: 'state',
1346
+ common: {
1347
+ role: 'state',
1348
+ name: {
1349
+ en: `${objectName} => History - Sprinkler consumption of the current calendar week`,
1350
+ de: `${objectName} => Verlauf — Sprinklerverbrauch der aktuellen Woche`,
1351
+ ru: `${objectName} => История - потребление спринклеров за текущую календарную неделю`,
1352
+ pt: `${objectName} => Histórico - Consumo de sprinklers na semana atual`,
1353
+ nl: `${objectName} => Geschiedenis - Sprinklerverbruik van de huidige kalenderweek`,
1354
+ fr: `${objectName} => Historique - Consommation de gicleurs de la semaine en cours`,
1355
+ it: `${objectName} => Cronologia - Consumo di irrigatori nella settimana corrente`,
1356
+ es: `${objectName} => Historial: consumo de rociadores en la semana natural actual`,
1357
+ pl: `${objectName} => Historia - Zużycie tryskaczy w bieżącym tygodniu kalendarzowym`,
1358
+ uk: `${objectName} => Історія - Споживання спринклерів поточного календарного тижня`,
1359
+ "zh-cn": `${objectName} => 历史-当前日历周的洒水消耗量`
1360
+ },
1361
+ type: 'number',
1362
+ unit: 'Liter',
1363
+ read: true,
1364
+ write: false,
1365
+ def: 0
1366
+ },
1367
+ native: {},
1368
+ });
1369
+ // Create Object for .history.curCalWeekRunningTime
1370
+ // Sprinkler running time of the current calendar week => History - Sprinkler-Laufzeit der aktuellen Kalenderwoche (783 Liter)
1371
+ const _curCalWeekRunningTimeNotExist = adapter.setObjectNotExistsAsync(`${objPfad}.history.curCalWeekRunningTime`, {
1372
+ type: 'state',
1373
+ common: {
1374
+ role: 'state',
1375
+ name: {
1376
+ en: `${objectName} => History - Sprinkler running time of the current calendar week`,
1377
+ de: `${objectName} => Verlauf — Sprinklerlaufzeit der aktuellen Woche`,
1378
+ ru: `${objectName} => История - время работы спринклеров на текущей календарной неделе`,
1379
+ pt: `${objectName} => Histórico - Tempo de operação do sprinkler na semana atual`,
1380
+ nl: `${objectName} => Geschiedenis - De looptijd van de sprinkler in de huidige kalenderweek`,
1381
+ fr: `${objectName} => Historique - Durée du gicleur pour la semaine civile en cours`,
1382
+ it: `${objectName} => Cronologia - Durata dell'irrigatore nella settimana corrente`,
1383
+ es: `${objectName} => Historia: duración de los rociadores de la semana actual`,
1384
+ pl: `${objectName} => Historia - Czas pracy tryskacza bieżącego tygodnia kalendarzowego`,
1385
+ uk: `${objectName} => Історія - Час роботи спринклера поточного календарного тижня`,
1386
+ "zh-cn": `${objectName} => 历史-当前日历周的洒水器运行时间`
1387
+ },
1388
+ type: 'string',
1389
+ read: true,
1390
+ write: false,
1391
+ def: '00:00'
1392
+ },
1393
+ native: {},
1394
+ });
1395
+ // Create Object for .history.lastCalWeekConsumed
1396
+ // Sprinkler consumption of the last calendar week => History - Sprinkler-Verbrauch der letzten Kalenderwoche (783 Liter)
1397
+ const _lastCalWeekConsumedNotExist = adapter.setObjectNotExistsAsync(`${objPfad}.history.lastCalWeekConsumed`, {
1398
+ type: 'state',
1399
+ common: {
1400
+ role: 'state',
1401
+ name: {
1402
+ en: `${objectName} => History - Sprinkler consumption of the last calendar week`,
1403
+ de: `${objectName} => Verlauf — Sprinklerverbrauch der letzten Woche`,
1404
+ ru: `${objectName} => История - потребление спринклеров за последнюю календарную неделю`,
1405
+ pt: `${objectName} => Histórico - Consumo de sprinklers na última semana`,
1406
+ nl: `${objectName} => Geschiedenis - Sprinklerverbruik van de laatste kalenderweek`,
1407
+ fr: `${objectName} => Historique - Consommation de gicleurs la semaine dernière`,
1408
+ it: `${objectName} => Cronologia - Consumo di irrigatori nell'ultima settimana`,
1409
+ es: `${objectName} => Historia: consumo de rociadores de la última semana natural`,
1410
+ pl: `${objectName} => Historia - Zużycie tryskaczy w ostatnim tygodniu kalendarzowym`,
1411
+ uk: `${objectName} => Історія - Споживання спринклерів за останній календарний тиждень`,
1412
+ "zh-cn": `${objectName} => 历史-上个日历周的洒水消耗量`
1413
+ },
1414
+ type: 'number',
1415
+ unit: 'Liter',
1416
+ read: true,
1417
+ write: false,
1418
+ def: 0
1419
+ },
1420
+ native: {},
1421
+ });
1422
+ // Create Object for .history.lastCalWeekRunningTime
1423
+ // Sprinkler running time of the last calendar week => History - Sprinkler-Laufzeit der letzten Kalenderwoche (783 Liter)
1424
+ const _lastCalWeekRunningTimeNotExist = adapter.setObjectNotExistsAsync(`${objPfad}.history.lastCalWeekRunningTime`, {
1425
+ type: 'state',
1426
+ common: {
1427
+ role: 'state',
1428
+ name: {
1429
+ en: `${objectName} => History - Sprinkler running time of the last calendar week`,
1430
+ de: `${objectName} => Verlauf — Sprinklerlaufzeit der letzten Woche`,
1431
+ ru: `${objectName} => История - Время работы спринклеров за последнюю календарную неделю`,
1432
+ pt: `${objectName} => Histórico - Tempo de operação do sprinkler na última semana`,
1433
+ nl: `${objectName} => Geschiedenis - De looptijd van de sprinkler van de laatste kalenderweek`,
1434
+ fr: `${objectName} => Historique - Durée de fonctionnement du gicleur la semaine dernière`,
1435
+ it: `${objectName} => Storia - Durata dell'irrigatore nell'ultima settimana`,
1436
+ es: `${objectName} => Historia: funcionamiento de los rociadores de la última semana`,
1437
+ pl: `${objectName} => Historia - Czas pracy tryskacza w ostatnim tygodniu kalendarzowym`,
1438
+ uk: `${objectName} => Історія - Час роботи спринклерів за останній календарний тиждень`,
1439
+ "zh-cn": `${objectName} => 历史-上个日历周的洒水器运行时间`
1440
+ },
1441
+ type: 'string',
1442
+ read: true,
1443
+ write: false,
1444
+ def: '00:00'
1445
+ },
1446
+ native: {},
1447
+ });
1448
+ // Create Object for .history.lastConsumed
1449
+ // Last consumed of sprinkler => History - Letzte Verbrauchsmenge des Ventils (783 Liter)
1450
+ const _lastConsumedNotExist = adapter.setObjectNotExistsAsync(`${objPfad}.history.lastConsumed`, {
1451
+ type: 'state',
1452
+ common: {
1453
+ role: 'state',
1454
+ name: {
1455
+ en: `${objectName} => History - Last consumed of sprinkler`,
1456
+ de: `${objectName} => Historie — Letzter Sprinklerverbrauch`,
1457
+ ru: `${objectName} => История - Последнее потребление спринклера`,
1458
+ pt: `${objectName} => Histórico - Último consumo do aspersor`,
1459
+ nl: `${objectName} => Geschiedenis - Sprinkler voor het laatst verbruikt`,
1460
+ fr: `${objectName} => Historique - Dernier gicleur consommé`,
1461
+ it: `${objectName} => Storia - Ultimo uso di irrigatore`,
1462
+ es: `${objectName} => Historia: rociador consumido por última vez`,
1463
+ pl: `${objectName} => Historia - Ostatnie zużycie tryskacza`,
1464
+ uk: `${objectName} => Історія - Останнє споживання спринклера`,
1465
+ "zh-cn": `${objectName} => 历史-上次消耗的洒水器`
1466
+ },
1467
+ type: 'number',
1468
+ unit: 'Liter',
1469
+ read: true,
1470
+ write: false,
1471
+ def: 0
1472
+ },
1473
+ native: {},
1474
+ });
1475
+ // Create Object for .history.lastOn
1476
+ // Last On of sprinkler => History - Letzter Start des Ventils (30.03 06:30)
1477
+ const _lastOnNotExist = adapter.setObjectNotExistsAsync(`${objPfad}.history.lastOn`, {
1478
+ type: 'state',
1479
+ common: {
1480
+ role: 'state',
1481
+ name: {
1482
+ en: `${objectName} => History - Last On of sprinkler`,
1483
+ de: `${objectName} => Historie — Letzter Start des Sprinkler`,
1484
+ ru: `${objectName} => История - Последний включитель спринклера`,
1485
+ pt: `${objectName} => História - Último aspersor`,
1486
+ nl: `${objectName} => Geschiedenis - De laatste sprinkler`,
1487
+ fr: `${objectName} => Histoire - Dernier gicleur`,
1488
+ it: `${objectName} => Storia - Ultimo irrigatore`,
1489
+ es: `${objectName} => Historia: el último rociador`,
1490
+ pl: `${objectName} => Historia - Ostatnie włączenie tryskacza`,
1491
+ uk: `${objectName} => Історія - Остання увімкнення спринклера`,
1492
+ "zh-cn": `${objectName} => 历史-洒水器的最后一次`
1493
+ },
1494
+ type: 'string',
1495
+ read: true,
1496
+ write: false,
1497
+ def: '-'
1498
+ },
1499
+ native: {},
1500
+ });
1501
+ // Create Object for .history.lastRunningTime
1502
+ // Last running time of sprinkler => History - Letzte Laufzeit des Ventils (0 sek, 47:00 min, 1:03:45 )
1503
+ const _lastRunningTimeNotExist = adapter.setObjectNotExistsAsync(`${objPfad}.history.lastRunningTime`, {
1504
+ type: 'state',
1505
+ common: {
1506
+ role: 'state',
1507
+ name: {
1508
+ en: `${objectName} => History - Last running time`,
1509
+ de: `${objectName} => Verlauf — Letzte Laufzeit`,
1510
+ ru: `${objectName} => История - время последнего запуска`,
1511
+ pt: `${objectName} => Histórico - Última execução`,
1512
+ nl: `${objectName} => Geschiedenis - Laatste speeltijd`,
1513
+ fr: `${objectName} => Historique - Dernière diffusion`,
1514
+ it: `${objectName} => Storia - Ultima durata`,
1515
+ es: `${objectName} => Historia: última edición`,
1516
+ pl: `${objectName} => Historia - Ostatni czas trwania`,
1517
+ uk: `${objectName} => Історія - Останній час роботи`,
1518
+ "zh-cn": `${objectName} => 历史-上次运行时间`
1519
+ },
1520
+ type: 'string',
1521
+ read: true,
1522
+ write: false,
1523
+ def: '00:00'
1524
+ },
1525
+ native: {},
1526
+ });
1527
+ // Create Object for .autoOn
1528
+ const _autoOnNotExist = adapter.setObjectNotExistsAsync(`${objPfad}.autoOn`, {
1529
+ type: 'state',
1530
+ common: {
1531
+ role: 'Switch',
1532
+ name: {
1533
+ en: `${objectName} => Switch automatic mode on / off`,
1534
+ de: `${objectName} => Automatik ein-/ausschalten`,
1535
+ ru: `${objectName} => Включить/выключить автоматический режим`,
1536
+ pt: `${objectName} => Liga/desliga o modo automático`,
1537
+ nl: `${objectName} => Automatische modus in- en uitschakelen`,
1538
+ fr: `${objectName} => Activer/désactiver le mode automatique`,
1539
+ it: `${objectName} => Attiva/spegne automaticamente`,
1540
+ es: `${objectName} => Activar/desactivar el modo automático`,
1541
+ pl: `${objectName} => Włączanie/wyłączanie trybu automatycznego`,
1542
+ uk: `${objectName} => Увімкнути/вимкнути автоматичний режим`,
1543
+ "zh-cn": `${objectName} => 开启/关闭自动模式`
1544
+ },
1545
+ type: 'boolean',
1546
+ states: {
1547
+ false: 'off',
1548
+ true: 'on'
1549
+ },
1550
+ read: true,
1551
+ write: true,
1552
+ def: true
1553
+ },
1554
+ native: {},
1555
+ }).catch((e) => adapter.log.warn(`setObjectNotExistsAsync ${objectName}.autoOn ${e}`));
1556
+ // Create Object for .actualSoilMoisture
1557
+ const _actualSoilMoistureFind = await adapter.findForeignObjectAsync(`${adapter.namespace}.${objPfad}.actualSoilMoisture`, `${objMetConSM.common.type}`);
1558
+ if (_actualSoilMoistureFind.id !== `${adapter.namespace}.${objPfad}.actualSoilMoisture` || _actualSoilMoistureFind.name !== nameMetConSM) {
1559
+ await adapter.setObjectAsync(
1560
+ `${objPfad}.actualSoilMoisture`,
1561
+ objMetConSM
1562
+ ).catch((e) => adapter.log.warn(e));
1563
+ adapter.log.info(`sprinkleControl [sprinkle.${objectName}.actualSoilMoisture] was updated`);
1564
+ }
1565
+
1566
+ // postponeByOneDay → um einen Tag verschieben bei fixDay (twoNd & threeRd)
1567
+ const _postponeByOneDay = await adapter.findForeignObjectAsync(`${adapter.namespace}.${objPfad}.postponeByOneDay`, `boolean`);
1568
+ if (_postponeByOneDay.id !== `${adapter.namespace}.${objPfad}.postponeByOneDay`
1569
+ && res.methodControlSM === 'fixDay'
1570
+ && (res.startDay === 'twoNd'
1571
+ || res.startDay === 'threeRd')) {
1572
+ await adapter.setObjectNotExistsAsync(`${objPfad}.postponeByOneDay`, {
1573
+ type: 'state',
1574
+ common: {
1575
+ role: 'button',
1576
+ name: {
1577
+ en: `${objectName} Postpone start by one day`,
1578
+ de: `${objectName} Start um einen Tag verschieben`,
1579
+ ru: `${objectName} Отложите начало на один день`,
1580
+ pt: `${objectName} Adie o início em um dia`,
1581
+ nl: `${objectName} Start met een dag uitstellen`,
1582
+ fr: `${objectName} Reporter le début d'un jour`,
1583
+ it: `${objectName} Rinviare l'inizio di un giorno`,
1584
+ es: `${objectName} Posponer un día el inicio`,
1585
+ pl: `${objectName} Odłóż początek o jeden dzień`,
1586
+ uk: `${objectName} Відкладіть початок на один день`,
1587
+ "zh-cn": `${objectName} 推迟一天开始`
1588
+ },
1589
+ type: 'boolean',
1590
+ read: true,
1591
+ write: true,
1592
+ def: false
1593
+ },
1594
+ native: {
1595
+ UNIT: '',
1596
+ TAB_ORDER: 0,
1597
+ OPERATIONS: 6,
1598
+ FLAGS: 1,
1599
+ TYPE: 'ACTION',
1600
+ MIN: false,
1601
+ MAX: true,
1602
+ DEFAULT: false
1603
+ }
1604
+ });
1605
+ adapter.subscribeStates(`${adapter.namespace}.${objPfad}.postponeByOneDay`);
1606
+ } else if (res.methodControlSM === 'fixDay'
1607
+ && (res.startDay === 'twoNd'
1608
+ || res.startDay === 'threeRd')) {
1609
+ adapter.subscribeStates(`${adapter.namespace}.${objPfad}.postponeByOneDay`);
1610
+ } else {
1611
+ await adapter.delObjectAsync(`${adapter.namespace}.${objPfad}.postponeByOneDay`); // "sprinklecontrol.0.sprinkle.???.actualSoilMoisture"
1612
+ }
1613
+
1614
+ // Create Object for .countdown => Countdown des Ventils
1615
+ const _countdownNotExist = adapter.setObjectNotExistsAsync(`${objPfad}.countdown`, {
1616
+ type: 'state',
1617
+ common: {
1618
+ role: 'state',
1619
+ name: {
1620
+ en: `${objectName} => countdown of sprinkler`,
1621
+ de: `${objectName} => Sprinkler-Countdown`,
1622
+ ru: `${objectName} => обратный отсчет времени спринклера`,
1623
+ pt: `${objectName} => contagem do aspersor`,
1624
+ nl: `${objectName} => aftellen van de sprinkler`,
1625
+ fr: `${objectName} => compte à rebours`,
1626
+ it: `${objectName} => conto alla rovescia`,
1627
+ es: `${objectName} => cuenta atrás del aspersor`,
1628
+ pl: `${objectName} => odliczanie tryskacza`,
1629
+ uk: `${objectName} => зворотний відлік спринклера`,
1630
+ "zh-cn": `${objectName} => 洒水器倒计时`
1631
+ },
1632
+ type: 'string',
1633
+ read: true,
1634
+ write: false,
1635
+ def: '-'
1636
+ },
1637
+ native: {},
1638
+ });
1639
+ // Create Object for .runningTime => Laufzeit des Ventils
1640
+ const _runningTimeNotExist = await adapter.setObjectNotExistsAsync(`${objPfad}.runningTime`, {
1641
+ type: 'state',
1642
+ common: {
1643
+ role: 'state',
1644
+ name: {
1645
+ en: `${objectName} => running time of sprinkler`,
1646
+ de: `${objectName} => Laufzeit des Sprinklers`,
1647
+ ru: `${objectName} => время работы спринклера`,
1648
+ pt: `${objectName} => duração do aspersor`,
1649
+ nl: `${objectName} => looptijd van de sprinkler`,
1650
+ fr: `${objectName} => durée de fonctionnement du gicleur`,
1651
+ it: `${objectName} => durata dell'irrigatore`,
1652
+ es: `${objectName} => duración del rociador`,
1653
+ pl: `${objectName} => czas pracy zraszacza`,
1654
+ uk: `${objectName} => час роботи спринклера`,
1655
+ "zh-cn": `${objectName} => 洒水器的运行时间`
1656
+ },
1657
+ type: 'string',
1658
+ read: true,
1659
+ write: true,
1660
+ def: '-'
1661
+ },
1662
+ native: {},
1663
+ });
1664
+ // Create Object for .sprinklerState => Zustand des Ventils im Thread
1665
+ // <<< 1 = warten >>> ( 0:off; 1:wait; 2:on; 3:break; 4:Boost(on); 5:off(Boost) )
1666
+ // Create .sprinklerState
1667
+ const _sprinklerStateNotExists = await adapter.setObjectNotExistsAsync(`${objPfad}.sprinklerState`, {
1668
+ type: 'state',
1669
+ common: {
1670
+ role: 'state',
1671
+ name: {
1672
+ en: `${objectName} => actual state of sprinkler`,
1673
+ de: `${objectName} => Stand der Sprinkleranlage`,
1674
+ ru: `${objectName} => фактическое состояние спринклера`,
1675
+ pt: `${objectName} => estado real do aspersor`,
1676
+ nl: `${objectName} => werkelijke toestand van de sprinkler`,
1677
+ fr: `${objectName} => état actuel du gicleur`,
1678
+ it: `${objectName} => stato attuale dell'irrigatore`,
1679
+ es: `${objectName} => estado actual del rociador`,
1680
+ pl: `${objectName} => rzeczywisty stan tryskacza`,
1681
+ uk: `${objectName} => фактичний стан спринклера`,
1682
+ "zh-cn": `${objectName} => 洒水器的实际状态`
1683
+ },
1684
+ type: 'number',
1685
+ min: 0,
1686
+ max: 5,
1687
+ states: {
1688
+ 0: 'off',
1689
+ 1: 'wait',
1690
+ 2: 'on',
1691
+ 3: 'break',
1692
+ 4: 'Boost(on)',
1693
+ 5: 'off(Boost)'
1694
+ },
1695
+ read: true,
1696
+ write: false,
1697
+ def: 0
1698
+ },
1699
+ native: {},
1700
+ });
1701
+ // Create Object for triggerPoint → Schaltpunkt der Bodenfeuchte
1702
+ const _triggerPointNotExist = await adapter.setObjectNotExistsAsync(`${objPfad}.triggerPoint`, {
1703
+ type: 'state',
1704
+ common: {
1705
+ role: 'state',
1706
+ name: {
1707
+ en: `${objectName} => Trigger point of sprinkler`,
1708
+ de: `${objectName} => Schaltpunkt des Sprinklers`,
1709
+ ru: `${objectName} => Точка срабатывания спринклера`,
1710
+ pt: `${objectName} => Ponto de gatilho do aspersor`,
1711
+ nl: `${objectName} => Triggerpunt van de sprinkler`,
1712
+ fr: `${objectName} => Point de déclenchement du gicleur`,
1713
+ it: `${objectName} => Punto di innesco`,
1714
+ es: `${objectName} => Punto de activación del aspersor`,
1715
+ pl: `${objectName} => Punkt spustowy zraszacza`,
1716
+ uk: `${objectName} => Тригерна точка спринклера`,
1717
+ "zh-cn": `${objectName} => 洒水器的触发点`
1718
+ },
1719
+ type: 'string',
1720
+ read: true,
1721
+ write: false,
1722
+ def: '-'
1723
+ },
1724
+ native: {},
1725
+ });
1726
+ // Object created
1727
+ let value = true;
1728
+ (await Promise.all([
1729
+ _sprinkleNotExist,
1730
+ _historyNotExist,
1731
+ _curCalWeekConsumedNotExist,
1732
+ _curCalWeekRunningTimeNotExist,
1733
+ _lastCalWeekConsumedNotExist,
1734
+ _lastCalWeekRunningTimeNotExist,
1735
+ _lastConsumedNotExist,
1736
+ _lastOnNotExist,
1737
+ _lastRunningTimeNotExist,
1738
+ _autoOnNotExist,
1739
+ _countdownNotExist,
1740
+ _runningTimeNotExist,
1741
+ _sprinklerStateNotExists,
1742
+ _triggerPointNotExist
1743
+ ])).forEach((val) => {
1744
+ value &= val;
1745
+ });
1746
+ if(value) {
1747
+ adapter.log.info(`sprinkleControl [sprinkle.${objectName}] was created`);
1748
+ }
1749
+
1750
+ // ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ //
1751
+ // +++++ zustände der States aktualisieren +++++ //
1752
+ // ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ //
1753
+
1754
+ //
1755
+ if(await _countdownNotExist){
1756
+ const _countdown = await adapter.getStateAsync(`${objPfad}.countdown`).catch((e) => adapter.log.warn(`${objectName}.countdown ${e}`));
1757
+ if (_countdown && _countdown.val !== '0') {
1758
+ adapter.setStateAsync(
1759
+ `${objPfad}.countdown`,
1760
+ '0',
1761
+ true
1762
+ ).catch((e) => adapter.log.warn(e));
1763
+ }
1764
+ }
1765
+ //
1766
+ if (_runningTimeNotExist) {
1767
+ const _runningTime = await adapter.getStateAsync(`${objPfad}.runningTime`).catch((e) => adapter.log.warn(`${objectName}.runningTime ${e}`));
1768
+ if (_runningTime && _runningTime.val !== '00:00') {
1769
+ adapter.setStateAsync(`${objPfad}.runningTime`,
1770
+ '00:00',
1771
+ true
1772
+ ).catch((e) => adapter.log.warn(e));
1773
+ }
1774
+ }
1775
+ //
1776
+ if (_sprinklerStateNotExists){
1777
+ const _sprinklerState = await adapter.getStateAsync(`${objPfad}.sprinklerState`).catch((e) => adapter.log.warn(`${objectName}.sprinklerState ${e}`));
1778
+ if (_sprinklerState && _sprinklerState.val !== 0) {
1779
+ await adapter.setStateAsync(`${objPfad}.sprinklerState`,
1780
+ 0,
1781
+ true
1782
+ ).catch((e) => adapter.log.warn(`${objectName}.sprinklerState ${e}`));
1783
+ }
1784
+ }
1785
+ // Festlegen des Schaltpunktes für den nächsten Start
1786
+ switch (myConfig.config[j].methodControlSM) {
1787
+ case 'bistable': {
1788
+ // Sensor soil moisture => Sensor Bodenfeuchte
1789
+ const _triggerSMBistabile = await adapter.getForeignStateAsync(myConfig.config[j].triggerSM).catch((e) => adapter.log.warn(`${objectName}.triggerSMBistabile ${e}`));
1790
+ if (_triggerSMBistabile && typeof _triggerSMBistabile.val === 'boolean') {
1791
+ myConfig.setSoilMoistBool(myConfig.config[j].sprinkleID, _triggerSMBistabile.val);
1792
+ } else {
1793
+ myConfig.setSoilMoistBool(myConfig.config[j].sprinkleID, true);
1794
+ adapter.log.warn(`The bistable sensor ${myConfig.config[j].triggerSM} in ${objectName} does not deliver correct values!`);
1795
+ }
1796
+
1797
+ adapter.setStateAsync(`${objPfad}.triggerPoint`,
1798
+ '-',
1799
+ true
1800
+ ).catch((e) => adapter.log.warn(`${objectName}.triggerPoint ${e}`));
1801
+ break;
1802
+ }
1803
+
1804
+ case 'analog': {
1805
+ // Sensor soil moisture => Sensor Bodenfeuchte
1806
+ const _triggerSMAnalog = await adapter.getForeignStateAsync(myConfig.config[j].triggerSM).catch((e) => adapter.log.warn(`${objectName}.triggerSMAnalog ${e}`));
1807
+ if (_triggerSMAnalog && (typeof _triggerSMAnalog.val === 'number' || typeof _triggerSMAnalog.val === 'string')) {
1808
+ myConfig.setSoilMoistPct(myConfig.config[j].sprinkleID, _triggerSMAnalog.val);
1809
+ } else {
1810
+ await adapter.setStateAsync(`${objPfad}.actualSoilMoisture`,
1811
+ 50,
1812
+ true
1813
+ );
1814
+ adapter.log.warn(`The analoge sensor ${myConfig.config[j].triggerSM} in ${objectName} does not deliver correct values!`);
1815
+ }
1816
+ adapter.setStateAsync(`${objPfad}.triggerPoint`,
1817
+ (myConfig.config[j].soilMoisture.pctTriggerIrrigation).toString(),
1818
+ true
1819
+ ).catch((e) => adapter.log.warn(`${objectName}.triggerPoint setState ${e}`));
1820
+ break;
1821
+ }
1822
+
1823
+ case 'fixDay': {
1824
+ const nextStartDay = ((today + 1) > 6 ? 0 : (today + 1));
1825
+
1826
+ /**
1827
+ * Neuen Start-Tag für Dreitage- und Zweitage-modus setzen
1828
+ *
1829
+ * @param threeRd - Dreitage-modus Ja/Nein
1830
+ * true → Dreitage-modus (treeRD)
1831
+ * false → Zweitage-modus (twoNd)
1832
+ */
1833
+ async function setNewDay (threeRd) {
1834
+ const today = await formatTime(adapter,'', 'day');
1835
+ const _actualSoilMoisture = await adapter.getStateAsync(
1836
+ `${objPfad}.actualSoilMoisture`
1837
+ ).catch((e) => adapter.log.warn(`${objectName}.actualSoilMoisture fixDay setState ${e}`));
1838
+ if (_actualSoilMoisture && (typeof _actualSoilMoisture.val === 'number')) {
1839
+ if ((_actualSoilMoisture.val >= 0) && (_actualSoilMoisture.val <= 6)) {
1840
+ if ((threeRd)
1841
+ && (_actualSoilMoisture.val === (((today + 3) > 6) ? 2 : (today + 3)))
1842
+ || (_actualSoilMoisture.val === (((today + 2) > 6) ? 1 : (today + 2)))
1843
+ || (_actualSoilMoisture.val === (((today + 1) > 6) ? 0 : (today + 1)))
1844
+ || (_actualSoilMoisture.val === today)) {
1845
+ myConfig.config[j].startFixDay[_actualSoilMoisture.val] = true;
1846
+ } else {
1847
+ myConfig.config[j].startFixDay[nextStartDay] = true;
1848
+ }
1849
+ } else {
1850
+ myConfig.config[j].startFixDay[nextStartDay] = true;
1851
+ }
1852
+ curNextFixDay(myConfig.config[j].sprinkleID, false);
1853
+ }
1854
+ }
1855
+
1856
+ if (myConfig.config[j].startDay === 'threeRd') {
1857
+ await setNewDay(true);
1858
+ } else if (myConfig.config[j].startDay === 'twoNd') {
1859
+ await setNewDay(false);
1860
+ } else if (myConfig.config[j].startDay === 'fixDay') {
1861
+ curNextFixDay(myConfig.config[j].sprinkleID, false);
1862
+ }
1863
+
1864
+ adapter.setStateAsync(`${objPfad}.triggerPoint`,
1865
+ '-',
1866
+ true
1867
+ ).catch((e) => adapter.log.warn(`${objectName}.triggerPoint fixDay setState ${e}`));
1868
+ break;
1869
+ }
1870
+
1871
+ case 'calculation': {
1872
+ const _actualSoilMoisture = await adapter.getStateAsync(`${objPfad}.actualSoilMoisture`).catch((e) => adapter.log.warn(e));
1873
+ if (_actualSoilMoisture) {
1874
+ if (await _actualSoilMoisture && typeof _actualSoilMoisture.val !== 'number' || _actualSoilMoisture.val === 0) {
1875
+ adapter.setStateAsync(`${objPfad}.actualSoilMoisture`,
1876
+ myConfig.config[j].soilMoisture.pct,
1877
+ true
1878
+ ).catch((e) => adapter.log.warn(e));
1879
+ } else {
1880
+ // num Wert der Bodenfeuchte berechnen und in der config speichern, wenn Wert zwischen 0 und max liegt
1881
+ if ((0 < _actualSoilMoisture.val) && (_actualSoilMoisture.val <= (myConfig.config[j]).soilMoisture.maxRain*100/myConfig.config[j].soilMoisture.maxIrrigation)) {
1882
+ myConfig.config[j].soilMoisture.val = _actualSoilMoisture.val * myConfig.config[j].soilMoisture.maxIrrigation / 100;
1883
+ myConfig.config[j].soilMoisture.pct = _actualSoilMoisture.val;
1884
+ } else {
1885
+ // Wert aus config übernehmen
1886
+ adapter.setStateAsync(`${objPfad}.actualSoilMoisture`,
1887
+ myConfig.config[j].soilMoisture.pct,
1888
+ true
1889
+ ).catch((e) => adapter.log.warn(e));
1890
+ }
1891
+ }
1892
+ }
1893
+
1894
+ adapter.setStateAsync(`${objPfad}.triggerPoint`,
1895
+ (myConfig.config[j].soilMoisture.pctTriggerIrrigation).toString(),
1896
+ true
1897
+ ).catch((e) => adapter.log.warn(e));
1898
+ break;
1899
+ }
1900
+ }
1901
+
1902
+ } catch (e) {
1903
+ adapter.log.warn(`sprinkleControl cannot created ... Please check your sprinkleControl config: ${e}`);
1904
+ }
1905
+ } else {
1906
+ adapter.log.warn('sprinkleControl cannot created ... Please check in your config the sprinkle Name');
1907
+ }
1908
+ }
1909
+
1910
+ // ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ //
1911
+ // +++++ Objekte löschen +++++ //
1912
+ // ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ //
1913
+ for(const i in ObjSprinkle) {
1914
+
1915
+ const resID = ObjSprinkle[i]._id;
1916
+ const objectID = resID.split('.');
1917
+ const resultID = objectID[3];
1918
+
1919
+ const resultName = result.map(({ sprinkleName }) => ({ sprinkleName }));
1920
+ const fullRes = [];
1921
+
1922
+ for(const i in resultName) {
1923
+ // @ts-ignore
1924
+ if (Object.hasOwn(resultName,i)) {
1925
+ const res = resultName[i].sprinkleName.replace(/[.;, ]/g, '_');
1926
+ fullRes.push(res);
1927
+ }
1928
+ }
1929
+
1930
+ if (fullRes.indexOf(resultID) === -1) {
1931
+ try {
1932
+ // object deleted
1933
+ /* del when exist Object Async */
1934
+ const delWhenExistObjectAsync = async (id, type) => {
1935
+ const _find = await adapter.findForeignObjectAsync(`${id}`, `${type}`);
1936
+ if (_find && _find.id === `${id}`) {
1937
+ await adapter.delObjectAsync(`${id}`).catch((e) => adapter.log.warn(e)); // "sprinklecontrol.0.sprinkle.???.postponeByOneDay"
1938
+ }
1939
+ };
1940
+
1941
+ Promise.all([
1942
+ adapter.delObjectAsync(`${resID}.actualSoilMoisture`), // "sprinklecontrol.0.sprinkle.???.actualSoilMoisture"
1943
+ adapter.delObjectAsync(`${resID}.triggerPoint`), // "sprinklecontrol.0.sprinkle.???.triggerPoint"
1944
+ adapter.delObjectAsync(`${resID}.sprinklerState`), // "sprinklecontrol.0.sprinkle.???.sprinklerState"
1945
+ adapter.delObjectAsync(`${resID}.runningTime`), // "sprinklecontrol.0.sprinkle.???.runningTime"
1946
+ delWhenExistObjectAsync(`${resID}.postponeByOneDay`, `boolean`), // "sprinklecontrol.0.sprinkle.???.postponeByOneDay" wenn vorhanden löschen
1947
+ adapter.delObjectAsync(`${resID}.countdown`), // "sprinklecontrol.0.sprinkle.???.countdown"
1948
+ adapter.delObjectAsync(`${resID}.autoOn`), // "sprinklecontrol.0.sprinkle.???.autoOn"
1949
+ adapter.delObjectAsync(`${resID}.history.lastOn`), // "sprinklecontrol.0.sprinkle.???.history.lastOn"
1950
+ adapter.delObjectAsync(`${resID}.history.lastConsumed`), // "sprinklecontrol.0.sprinkle.???.history.lastConsumed"
1951
+ adapter.delObjectAsync(`${resID}.history.lastRunningTime`), // "sprinklecontrol.0.sprinkle.???.history.lastRunningTime"
1952
+ adapter.delObjectAsync(`${resID}.history.curCalWeekConsumed`), // "sprinklecontrol.0.sprinkle.???.history.curCalWeekConsumed"
1953
+ adapter.delObjectAsync(`${resID}.history.lastCalWeekConsumed`), // "sprinklecontrol.0.sprinkle.???.history.lastCalWeekConsumed"
1954
+ adapter.delObjectAsync(`${resID}.history.curCalWeekRunningTime`), // "sprinklecontrol.0.sprinkle.???.history.curCalWeekRunningTime"
1955
+ adapter.delObjectAsync(`${resID}.history.lastCalWeekRunningTime`) // "sprinklecontrol.0.sprinkle.???.history.lastCalWeekRunningTime"
1956
+ ]
1957
+ ).then(async ()=>{
1958
+ // History - Objekt(Ordner.history) löschen
1959
+ await adapter.delObjectAsync(`${resID}.history`);
1960
+ }).then(async ()=>{
1961
+ // Objekt(Ordner) löschen
1962
+ await adapter.delObjectAsync(resID);
1963
+ }).then(()=>{
1964
+ adapter.log.info(`sprinkleControl [${resID}] was deleted`);
1965
+ });
1966
+ } catch (e) {
1967
+ adapter.log.warn(e);
1968
+ }
1969
+ }
1970
+ }
1971
+ }
1972
+ }
1973
+ ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
1974
+
1975
+ function main(adapter) {
1976
+
1977
+ /* Auf die Adapterkonfiguration (im Instanz objekt alles unter dem Attribut "native") kann zugegriffen werden über adapter.config: */
1978
+ adapter.log.debug(`adapter.config.events: ${JSON.stringify(adapter.config.events)}`);
1979
+ /* Auf die Adapterkonfiguration (im Instanz objekt alles unter dem Attribut "native") kann zugegriffen werden über adapter.config */
1980
+ adapter.getForeignObject('system.config', (err, obj) => {
1981
+ if (obj) {
1982
+ checkStates();
1983
+ }
1984
+ });
1985
+ createSprinklers().catch((e) => adapter.log.warn(e));
1986
+ GetSystemData().catch((e) => adapter.log.warn(e));
1987
+ sendMessageText.initConfigMessage(adapter);
1988
+ evaporation.initEvaporation(adapter); // init evaporation
1989
+ checkActualStates().catch((e) => adapter.log.warn(e));
1990
+ sunPos();
1991
+ timer = setTimeout(() => {
1992
+ startTimeSprinkle();
1993
+ addStartTimeSprinkle();
1994
+ }, 2000);
1995
+
1996
+ /*
1997
+ * in this template all states changes inside the adapters namespace are subscribed
1998
+ * => In dieser Vorlage werden alle Statusänderungen im Namensraum des Adapters abonniert
1999
+ * adapter.subscribeStates('*');
2000
+ */
2001
+
2002
+ adapter.subscribeStates('control.autoOnOff');
2003
+ adapter.subscribeStates('control.autoStart');
2004
+ adapter.subscribeStates('control.Holiday');
2005
+
2006
+ // Request a notification from a third-party adapter => Fordern Sie eine Benachrichtigung von einem Drittanbieter-Adapter an
2007
+ if (adapter.config.publicHolidays === true && (`${adapter.config.publicHolInstance}.heute.*`)) {
2008
+ adapter.subscribeForeignStates(`${adapter.config.publicHolInstance}.heute.*`);
2009
+ }
2010
+ if (adapter.config.publicHolidays === true && (`${adapter.config.publicHolInstance}.morgen.*`)) {
2011
+ adapter.subscribeForeignStates(`${adapter.config.publicHolInstance}.morgen.*`);
2012
+ }
2013
+ if (adapter.config.weatherForecast === true) {
2014
+ if (adapter.config.weatherForecastService === 'ownDataPoint') {
2015
+ weatherForecastTodayPfadStr = adapter.config.pathRainForecast;
2016
+ adapter.subscribeForeignStates(weatherForecastTodayPfadStr);
2017
+ } else if (adapter.config.weatherForecastService === 'dasWetter' && adapter.config.weatherForInstance) {
2018
+ weatherForecastTodayPfadStr = `${adapter.config.weatherForInstance}.NextDaysDetailed.Location_1.Day_1.rain_value`;
2019
+ adapter.subscribeForeignStates(weatherForecastTodayPfadStr);
2020
+ adapter.subscribeForeignStates(`${adapter.config.weatherForInstance}.NextDaysDetailed.Location_1.Day_2.rain_value`);
2021
+ } else {
2022
+ adapter.log.warn('There is no valid data record stored in the weather forecast, please correct it!');
2023
+ }
2024
+ }
2025
+ if (adapter.config.actualValueLevel !== '') {
2026
+ adapter.subscribeForeignStates(adapter.config.actualValueLevel);
2027
+ } else {
2028
+ adapter.setState('info.cisternState', { val: 'The level sensor of the water cistern is not specified', ack: true });
2029
+ }
2030
+ }
2031
+
2032
+ ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
2033
+
2034
+ // ++++++++++++++++++ start option of Adapter ++++++++++++++++++++++
2035
+ // If started as allInOne/compact mode => return function to create instance
2036
+ if (module && module.parent) {
2037
+ module.exports = startAdapter;
2038
+ } else {
2039
+ // or start the instance directly
2040
+ startAdapter();
2041
+ }