iobroker.sprinklecontrol 0.2.12 → 0.2.14

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (62) hide show
  1. package/{Lizenz → LICENSE} +21 -21
  2. package/README.md +80 -129
  3. package/admin/i18n/de/translations.json +134 -0
  4. package/admin/i18n/en/translations.json +134 -0
  5. package/admin/i18n/es/translations.json +134 -0
  6. package/admin/i18n/fr/translations.json +134 -0
  7. package/admin/i18n/it/translations.json +134 -0
  8. package/admin/i18n/nl/translations.json +134 -0
  9. package/admin/i18n/pl/translations.json +134 -0
  10. package/admin/i18n/pt/translations.json +134 -0
  11. package/admin/i18n/ru/translations.json +134 -0
  12. package/admin/i18n/uk/translations.json +134 -0
  13. package/admin/i18n/zh-cn/translations.json +134 -0
  14. package/admin/index_m.html +904 -907
  15. package/admin/index_m.js +779 -780
  16. package/admin/style.css +453 -158
  17. package/admin/words.js +143 -136
  18. package/io-package.json +268 -189
  19. package/lib/adapter-config.d.ts +19 -16
  20. package/lib/evaporation.js +443 -412
  21. package/lib/myConfig.js +373 -359
  22. package/lib/sendMessageText.js +162 -199
  23. package/lib/tools.js +148 -118
  24. package/lib/valveControl.js +918 -888
  25. package/main.js +2041 -1764
  26. package/package.json +51 -30
  27. package/.commitmessage +0 -4
  28. package/.gitattributes +0 -2
  29. package/admin/admin.d.ts +0 -1
  30. package/docs/de/img/E-Mail.jpg +0 -0
  31. package/docs/de/img/Extraeinstellungen.jpg +0 -0
  32. package/docs/de/img/Pumpeneinstellung.jpg +0 -0
  33. package/docs/de/img/Pushover.jpg +0 -0
  34. package/docs/de/img/Select_ID.jpg +0 -0
  35. package/docs/de/img/Telegram.jpg +0 -0
  36. package/docs/de/img/Ventil-Haupteinstellung.jpg +0 -0
  37. package/docs/de/img/Ventil-Pumpeneinstellung.jpg +0 -0
  38. package/docs/de/img/WhatsApp.jpg +0 -0
  39. package/docs/de/img/Zeiteinstellung.jpg +0 -0
  40. package/docs/de/img/analog.jpg +0 -0
  41. package/docs/de/img/bistabil.jpg +0 -0
  42. package/docs/de/img/control.jpg +0 -0
  43. package/docs/de/img/festeTage.jpg +0 -0
  44. package/docs/de/img/main.jpg +0 -0
  45. package/docs/de/img/main_tab.jpg +0 -0
  46. package/docs/de/img/schaltverhalten.jpg +0 -0
  47. package/docs/de/img/sprinklecontrol.png +0 -0
  48. package/docs/de/img/verdunstDiagra.jpg +0 -0
  49. package/docs/de/img/verdunstung.jpg +0 -0
  50. package/docs/de/sprinklecontrol.md +0 -588
  51. package/docs/en/img/schaltverhalten.jpg +0 -0
  52. package/docs/en/img/screenshot1.jpg +0 -0
  53. package/docs/en/img/screenshot2.jpg +0 -0
  54. package/docs/en/img/screenshot3.jpg +0 -0
  55. package/docs/en/img/screenshot4.jpg +0 -0
  56. package/docs/en/img/screenshot5.jpg +0 -0
  57. package/docs/en/img/screenshot6.jpg +0 -0
  58. package/docs/en/img/screenshot7.jpg +0 -0
  59. package/docs/en/img/screenshot8.jpg +0 -0
  60. package/docs/en/img/sprinklecontrol.png +0 -0
  61. package/docs/en/sprinklecontrol.md +0 -230
  62. package/main.test.js +0 -30
package/main.js CHANGED
@@ -1,1764 +1,2041 @@
1
- 'use strict';
2
- /*
3
- info: log aufbau main.js: #0.*
4
- */
5
- // Load your modules here, e.g.: => // Laden Sie Ihre Module hier, z.B.
6
- // const fs = require("fs");
7
-
8
- const utils = require('@iobroker/adapter-core');
9
- const schedule = require('node-schedule');
10
- const SunCalc = require('suncalc2');
11
-
12
- const sendMessageText = require('./lib/sendMessageText.js'); // sendMessageText
13
- const valveControl = require('./lib/valveControl.js'); // Steuerung der einzelnen Ventile
14
- const myConfig = require('./lib/myConfig.js'); // myConfig → Speichern und abrufen von Konfigurationsdaten der Ventile
15
- const evaporation = require('./lib/evaporation.js');
16
- const addTime = require('./lib/tools.js').addTime;
17
- const formatTime = require('./lib/tools.js').formatTime;
18
- //const trend = require('./lib/tools').trend;
19
-
20
- /**
21
- * The adapter instance
22
- * @type {ioBroker.Adapter}
23
- */
24
- let adapter;
25
- const adapterName = require('./package.json').name.split('.').pop();
26
-
27
- /** ext. Adapter → Deutsche Feiertage
28
- * @type {any} */
29
- let publicHolidayStr;
30
- /** @type {any} */
31
- let publicHolidayTomorrowStr;
32
- /* Regenvorhersage - DasWetter.com */
33
- /** @type {string}
34
- * - Pfad zur Regenvorhersage in mm */
35
- let weatherForecastTodayPfadStr;
36
- /** @type {number}
37
- * - heutige Regenvorhersage in mm */
38
- let weatherForecastTodayNum = 0;
39
- /** @type {number}
40
- * - morgige Regenvorhersage in mm */
41
- let weatherForecastTomorrowNum = 0;
42
- /** @type {boolean}
43
- * - Externer Schalter für Zusatzbewässerung */
44
- let addStartTimeSwitch = false;
45
-
46
- /** @type {string} */
47
- let startTimeStr;
48
- /** @type {string} */
49
- let sunriseStr;
50
- /** @type {string} */
51
- let sunsetStr;
52
- /** @type {string} */
53
- let goldenHourEnd;
54
- /** switch => sprinklecontrol.*.control.Holiday
55
- * - Wenn (Holiday == true) ist, soll das Wochenendprogramm gefahren werden.
56
- * @type {any} */
57
- let holidayStr;
58
- /** @type {any} */
59
- let autoOnOffStr;
60
- /** @type {string} */
61
- let kwStr; // akt. KW der Woche
62
- /** @type {number | undefined} */
63
- let timer, timerSleep;
64
- /** @type {number} */
65
- let today; // heutige Tag 0:So;1:Mo...6:Sa
66
-
67
- /* memo */
68
- /** @type {{}} */
69
- let ObjSprinkle = {};
70
-
71
- /**
72
- * +++++++++++++++++++++++++++ Starts the adapter instance ++++++++++++++++++++++++++++++++
73
- *
74
- * @param {Partial<ioBroker.AdapterOptions>} [options]
75
- */
76
- function startAdapter(options) {
77
-
78
- options = options || {};
79
- Object.assign(options, { name: adapterName });
80
-
81
- adapter = new utils.Adapter(options);
82
-
83
- // start here!
84
- adapter.on('ready', () => {
85
- // init createConfig
86
- myConfig.createConfig(adapter);
87
- // Hauptpumpe zur Bewässerung setzen
88
- valveControl.initValveControl(adapter);
89
- main(adapter)
90
- });
91
-
92
- /**
93
- * +++++++++++++++++++++++++ is called when adapter shuts down +++++++++++++++++++++++++
94
- *
95
- * @param {() => void}callback
96
- */
97
- adapter.on('unload', (callback) => {
98
- /* Wird beim Stoppen nicht ausgeführt - Warum?
99
- if(adapter.config.notificationEnabled){
100
- sendMessageText.sendMessage('sprinklerControl is shutting down');
101
- adapter.log.info('sprinklerControl is shutting down');
102
- }
103
- */
104
- try {
105
- adapter.log.info('cleaned everything up...');
106
- clearTimeout(timer);
107
- clearTimeout(timerSleep);
108
- /*Startzeiten der Timer löschen*/
109
- schedule.cancelJob('calcPosTimer');
110
- schedule.cancelJob('sprinkleStartTime');
111
- schedule.cancelJob('sprinkleAddStartTime');
112
- /* alle Ventile und Aktoren deaktivieren */
113
- valveControl.clearEntireList();
114
-
115
- callback();
116
- } catch (e) {
117
- callback();
118
- }
119
- });
120
-
121
- /**
122
- * ++++++++++++++++++ Answers when getTelegramUser calls from index_m ++++++++++++++++++
123
- * -------------- Antwortet bei Aufrufen von getTelegramUser von index_m ---------------
124
- * @param {object} obj
125
- */
126
- adapter.on ('message', (obj) => {
127
- if (obj) {
128
- switch (obj.command) {
129
- case 'getTelegramUser':
130
- adapter.getForeignState(adapter.config.telegramInstance + '.communicate.users', (err, state) => {
131
- err && adapter.log.error(err);
132
- if (state && state.val) {
133
- try {
134
- adapter.log.debug('getTelegramUser:' + state.val);
135
- adapter.sendTo(obj.from, obj.command, JSON.parse(state.val), obj.callback);
136
- } catch (err) {
137
- err && adapter.log.error(err);
138
- adapter.log.error('Cannot parse stored user IDs from Telegram!');
139
- }
140
- }
141
- });
142
- break;
143
- }
144
- }
145
- });
146
-
147
- /**
148
- * ++++++++++++++++++ is called if a subscribed object changes ++++++++++++++++++
149
- * ---------- wird aufgerufen, wenn sich ein abonniertes Objekt ändert ----------
150
- * @param {string} id
151
- * @param {Object | null | undefined} obj
152
- */
153
- adapter.on('objectChange', (id, obj) => {
154
- if (obj) {
155
- adapter.log.info(`object ${id} changed: ${JSON.stringify(obj)}`);
156
- } else {
157
- // The object was deleted
158
- adapter.log.info(`object ${id} deleted`);
159
- }
160
- });
161
-
162
- /**
163
- * ++++++++++++++++++ is called if a subscribed state changes ++++++++++++++++++
164
- * --------- wird aufgerufen, wenn sich ein abonnierter Status ändert ----------
165
- * @param {string} id
166
- * @param {State | null | undefined} state
167
- */
168
- adapter.on('stateChange', (id, state) => {
169
- if (state) {
170
- // The state was changed → Der Zustand wurde geändert
171
- adapter.log.debug(`state ${id} changed: ${state.val} (ack = ${state.ack})`);
172
-
173
- // wenn (Holiday == true) ist, soll das Wochenendprogramm gefahren werden.
174
- if (id === adapter.namespace + '.control.Holiday') {
175
- holidayStr = state.val;
176
- startTimeSprinkle();
177
- }
178
- // wenn (addStartTimeSwitch == true) wird die zusätzliche Bewässerung aktiviert
179
- if (id === `${adapter.namespace}.control.addStartTimeSwitch`
180
- && typeof state.val === 'boolean') {
181
- addStartTimeSwitch = state.val;
182
- }
183
- // wenn (autoOnOff == false) so werden alle Sprenger nicht mehr automatisch gestartet.
184
- if (id === adapter.namespace + '.control.autoOnOff') {
185
- autoOnOffStr = state.val;
186
- adapter.log.info(`startAdapter: control.autoOnOff: ${state.val}`);
187
- if (!state.val) {
188
- valveControl.clearEntireList();
189
- }
190
- startTimeSprinkle();
191
- }
192
- // wenn (...sprinkleName.runningTime sich ändert) so wird der aktuelle Sprenger [sprinkleName]
193
- // bei == 0 gestoppt, > 1 gestartet
194
- if (myConfig.config && !state.ack) {
195
- const found = myConfig.config.find(d => d.objectID === id);
196
- if (found) {
197
- if (id === myConfig.config[found.sprinkleID].objectID) {
198
- if (!isNaN(state.val)) {
199
- valveControl.addList(
200
- [{
201
- auto: false, // Handbetrieb
202
- sprinkleID: found.sprinkleID,
203
- wateringTime: (state.val <= 0) ? state.val : Math.round(60 * state.val)
204
- }]);
205
- }
206
- }
207
- }
208
- }
209
- // wenn in der config unter methodControlSM !== 'analog' oder 'bistable' eingegeben wurde, dann Bodenfeuchte-Sensor auslesen
210
- if (myConfig.config) {
211
- function filterByID(obj){
212
- return (((obj.methodControlSM === 'analog') || (obj.methodControlSM === 'bistable')) && (obj.triggerSM === id));
213
- }
214
- const filter = myConfig.config.filter(filterByID);
215
- if (filter) {
216
- for (const fil of filter) {
217
- if (id === myConfig.config[fil.sprinkleID].triggerSM){
218
- // analog
219
- if (fil.methodControlSM === 'analog') {
220
- myConfig.setSoilMoistPct(fil.sprinkleID, state.val);
221
- } else if (fil.methodControlSM === 'bistable') { // bistable
222
- myConfig.setSoilMoistBool(fil.sprinkleID, state.val);
223
- }
224
- }
225
- }
226
- }
227
- }
228
- // wenn (...sprinkleName.autoOn == false[off]) so wird der aktuelle Sprenger [sprinkleName]
229
- // bei false nicht automatisch gestartet
230
- if (myConfig.config && (typeof state.val === 'boolean')) {
231
- const found = myConfig.config.find(d => d.autoOnID === id);
232
- if (found && id === myConfig.config[found.sprinkleID].autoOnID) {
233
- myConfig.config[found.sprinkleID].autoOn = state.val;
234
- valveControl.addList(
235
- [{
236
- auto: false,
237
- sprinkleID: found.sprinkleID,
238
- wateringTime: 0
239
- }]);
240
- }
241
- }
242
-
243
- // postponeByOneDay um einen Tag verschieben bei fixDay (twoNd & threeRd)
244
- let idSplit = id.split('.', 5);
245
- if (idSplit[4] === `postponeByOneDay`) {
246
- const found = myConfig.config.find(d => d.objectName === idSplit[3]);
247
- if (found) {
248
- myConfig.postponeByOneDay(found.sprinkleID).catch((e) => {adapter.log.warn(`postponeByOneDay: ${e}`)});
249
- }
250
- }
251
-
252
- // Change in outside temperature → Änderung der Außentemperatur
253
- if (id === adapter.config.sensorOutsideTemperature) { /*Temperatur*/
254
- if (!Number.isNaN(Number.parseFloat(state.val))) {
255
- evaporation.setCurTemperature(parseFloat(state.val), state.ts);
256
- } else {
257
- adapter.log.warn(`sensorOutsideTemperature => Wrong value: ${state.val}, Type: ${typeof state.val}`);
258
- }
259
- }
260
- // LuftFeuchtigkeit
261
- if (id === adapter.config.sensorOutsideHumidity) {
262
- if (!Number.isNaN(Number.parseFloat(state.val))) {
263
- evaporation.setCurHumidity(parseFloat(state.val), state.lc);
264
- } else {
265
- adapter.log.warn(`sensorOutsideHumidity => Wrong value: ${state.val}, Type: ${typeof state.val}`);
266
- }
267
- }
268
- // Helligkeit
269
- if (id === adapter.config.sensorBrightness) {
270
- if (!Number.isNaN(Number.parseFloat(state.val))) {
271
- evaporation.setCurIllumination(parseFloat(state.val), state.lc);
272
- } else {
273
- adapter.log.warn(`sensorBrightness => Wrong value: ${state.val}, Type: ${typeof state.val}`);
274
- }
275
- }
276
- // Windgeschwindigkeit
277
- if (id === adapter.config.sensorWindSpeed) {
278
- if (!Number.isNaN(Number.parseFloat(state.val))) {
279
- evaporation.setCurWindSpeed(parseFloat(state.val), state.lc);
280
- } else {
281
- adapter.log.warn(`sensorWindSpeed => Wrong value: ${state.val}, Type: ${typeof state.val}`);
282
- }
283
- }
284
- // Regencontainer
285
- // If the amount of rain is over 20 mm, the 'lastRainCounter' is overwritten and no calculation is carried out. =>
286
- // * Wenn die Regenmenge mehr als 20 mm beträgt, wird der 'lastRainCounter' überschrieben und es wird keine Berechnung durchgeführt.
287
- if (id === adapter.config.sensorRainfall) {
288
- if (!Number.isNaN(Number.parseFloat(state.val))) {
289
- evaporation.setCurAmountOfRain(parseFloat(state.val));
290
- } else {
291
- adapter.log.warn(`sensorRainfall => Wrong value: ${state.val}, Type: ${typeof state.val}`);
292
- }
293
- }
294
- // Feiertagskalender
295
- if (adapter.config.publicHolidays === true) {
296
- if (id === adapter.config.publicHolInstance + '.heute.boolean') {
297
- publicHolidayStr = state.val;
298
- startTimeSprinkle();
299
- }
300
- if (id === adapter.config.publicHolInstance + '.morgen.boolean') {
301
- publicHolidayTomorrowStr = state.val;
302
- startTimeSprinkle();
303
- }
304
- }
305
- // Wettervorhersage
306
- if (adapter.config.weatherForecast === true) {
307
- if (id === weatherForecastTodayPfadStr) {
308
- if (typeof state.val == 'string') {
309
- weatherForecastTodayNum = parseFloat(state.val);
310
- } else if (typeof state.val == 'number') {
311
- weatherForecastTodayNum = state.val;
312
- } else {
313
- weatherForecastTodayNum = 0;
314
- console.log.info('StateChange => Wettervorhersage state.val ( ' + state.val + '; ' + typeof state.val + ' ) kann nicht als Number verarbeitet werden');
315
- }
316
- adapter.setState('info.rainToday', {
317
- val: weatherForecastTodayNum,
318
- ack: true
319
- });
320
- }
321
- if (id === adapter.config.weatherForInstance + '.NextDaysDetailed.Location_1.Day_2.rain_value') {
322
- weatherForecastTomorrowNum = parseFloat(state.val);
323
- adapter.setState('info.rainTomorrow', {
324
- val: weatherForecastTomorrowNum,
325
- ack: true
326
- });
327
- }
328
- }
329
- // Füllstand der Zisterne bei Statusänderung
330
- if (adapter.config.actualValueLevel && (id === adapter.config.actualValueLevel)) {
331
- valveControl.setFillLevelCistern(parseFloat(state.val) || 0);
332
- //fillLevelCistern = state.val || 0;
333
- }
334
- }
335
- });
336
- }
337
-
338
- // +++++++++++++++++ Get longitude an latitude from system config ++++++++++++++++++++
339
- /**get longitude/latitude from system if not set or not valid
340
- * do not change if we have already a valid value
341
- * so we could use different settings compared to system if necessary
342
- * >>>
343
- * Längen- / Breitengrad vom System abrufen, falls nicht festgelegt oder ungültig
344
- * wird nicht geändert, wenn bereits ein gültiger Wert vorhanden ist,
345
- * daher können wir bei Bedarf andere Einstellungen als das System verwenden
346
- */
347
- async function GetSystemData() {
348
- if (typeof adapter.config.longitude === undefined || adapter.config.longitude == null || adapter.config.longitude.length === 0 || isNaN(adapter.config.longitude)
349
- || typeof adapter.config.latitude === undefined || adapter.config.latitude == null || adapter.config.latitude.length === 0 || isNaN(adapter.config.latitude)) {
350
-
351
- try {
352
- const obj = await adapter.getForeignObjectAsync('system.config');
353
-
354
- if (obj && obj.common && obj.common.longitude && obj.common.latitude) {
355
- adapter.config.longitude = obj.common.longitude;
356
- adapter.config.latitude = obj.common.latitude;
357
-
358
- adapter.log.debug(`longitude: ${adapter.config.longitude} | latitude: ${adapter.config.latitude}`);
359
- } else {
360
- adapter.log.error('system settings cannot be called up. Please check the geo data');
361
- }
362
- } catch (err) {
363
- adapter.log.warn('system settings cannot be called up. Please check configuration!');
364
- }
365
- }
366
- }
367
-
368
-
369
- /**
370
- * Schreiben des nächsten Starts in '.actualSoilMoisture'
371
- * oder Rückgabe des DayName für den nächsten Start
372
- * @param {number} sprinkleID - Number of Array[0...]
373
- * @param {boolean} returnOn - true = Rückgabe des Wochentags; false = Schreiben in State /.actualSoilMoisture
374
- * @returns {string} nextStart ['Sun','Mon','Tue','Wed','Thur','Fri','Sat']
375
- */
376
- function curNextFixDay (sprinkleID, returnOn) {
377
- const weekDayArray = myConfig.config[sprinkleID].startFixDay;
378
- const objPfad = 'sprinkle.' + myConfig.config[sprinkleID].objectName;
379
- const weekday = ['Sun','Mon','Tue','Wed','Thur','Fri','Sat'];
380
- let curDay = formatTime(adapter, '', 'day');
381
- for ( let i=0; i<7; i++ ) {
382
- curDay++;
383
- if (curDay > 6) {curDay = curDay - 7;}
384
- if (weekDayArray[curDay] === true) {
385
- if (returnOn) {
386
- return weekday[curDay];
387
- } else {
388
- adapter.setState(objPfad + '.actualSoilMoisture', {
389
- val: curDay,
390
- ack: true
391
- });
392
- }
393
- break;
394
- }
395
- }
396
- }
397
-
398
- //
399
- /**
400
- * Sets the status at start to a defined value
401
- * → Setzt den Status beim Start auf einen definierten Wert
402
- */
403
- function checkStates() {
404
- /**
405
- * control.Holiday
406
- * @param {string|null} err
407
- * @param {ioBroker.State|null|undefined} state
408
- */
409
- adapter.getState('control.Holiday', (err, state) => {
410
- if (state && (state.val == null)) {
411
- adapter.setState('control.Holiday', {val: false, ack: true});
412
- }
413
- });
414
- /**
415
- * control.autoOnOff
416
- * @param {string|null} err
417
- * @param {ioBroker.State|null|undefined} state
418
- */
419
- adapter.getState('control.autoOnOff', (err, state) => {
420
- if (state && (state.val == null) && (state.val === undefined)) {
421
- autoOnOffStr = true;
422
- adapter.setState('control.autoOnOff', {
423
- val: autoOnOffStr,
424
- ack: true
425
- });
426
- }
427
- });
428
- adapter.getState('evaporation.ETpToday', (err, state) => {
429
- if (state && (state.val == null)) {
430
- evaporation.setETpTodayNum(0);
431
- adapter.setState('evaporation.ETpToday', {
432
- val: 0,
433
- ack: true
434
- });
435
- } else if (state) {
436
- evaporation.setETpTodayNum(parseFloat(state.val));
437
- }
438
- });
439
- adapter.getState('evaporation.ETpYesterday', (err, state) => {
440
- if (state && (state.val == null || state.val === false)) {
441
- adapter.setState('evaporation.ETpYesterday', {
442
- val: 0,
443
- ack: true
444
- });
445
- }
446
- });
447
-
448
- // akt. kW ermitteln für history last week
449
- kwStr = formatTime(adapter, '','kW');
450
- today = formatTime(adapter,'', 'day');
451
- }
452
-
453
- /**
454
- * aktuelle States checken nach dem Start (2000 ms) wenn alle Sprenger-Kreise angelegt wurden
455
- */
456
- async function checkActualStates () {
457
-
458
- try {
459
- /**
460
- * switch Holiday
461
- * @type {ioBroker.State | null}
462
- * @private
463
- */
464
- const _holiday = await adapter.getStateAsync('control.Holiday');
465
- if (_holiday && _holiday.val) {
466
- holidayStr = _holiday.val;
467
- }
468
-
469
- /**
470
- * switch autoOnOff
471
- * @type {ioBroker.State | null}
472
- * @private
473
- */
474
- const _autoOnOff = await adapter.getStateAsync('control.autoOnOff');
475
- if (_autoOnOff && _autoOnOff.val) {
476
- autoOnOffStr = _autoOnOff.val;
477
- }
478
-
479
- if (adapter.config.publicHolidays === true && (adapter.config.publicHolInstance !== 'none' || adapter.config.publicHolInstance !== '')) {
480
- /**
481
- * Feiertag Heute
482
- * @type {ioBroker.State | void}
483
- * @private
484
- */
485
- const _publicHolInstanceHeute = await adapter.getForeignStateAsync(
486
- adapter.config.publicHolInstance + '.heute.boolean'
487
- ).catch((e) => adapter.log.warn(e));
488
- if (_publicHolInstanceHeute && _publicHolInstanceHeute.val) {
489
- publicHolidayStr = _publicHolInstanceHeute.val;
490
- }
491
- /**
492
- * Feiertag MORGEN
493
- * @type {ioBroker.State | void}
494
- * @private
495
- */
496
- const _publicHolInstanceMorgen = await adapter.getForeignStateAsync(
497
- adapter.config.publicHolInstance + '.morgen.boolean'
498
- ).catch((e) => adapter.log.warn(e));
499
- if (_publicHolInstanceMorgen && _publicHolInstanceMorgen.val) {
500
- publicHolidayTomorrowStr = _publicHolInstanceMorgen.val;
501
- }
502
- }
503
-
504
- if (adapter.config.weatherForecast === true && (adapter.config.weatherForInstance !== 'none' || adapter.config.weatherForInstance !== '')) {
505
- /**
506
- * Niederschlagsmenge HEUTE in mm
507
- * @type {ioBroker.State | void}
508
- * @private
509
- */
510
- const _weatherForInstanceToday = await adapter.getForeignStateAsync(
511
- weatherForecastTodayPfadStr
512
- ).catch((e) => adapter.log.warn(e));
513
- if (_weatherForInstanceToday && _weatherForInstanceToday.val) {
514
- if (typeof _weatherForInstanceToday.val == 'string') {
515
- weatherForecastTodayNum = parseFloat(_weatherForInstanceToday.val);
516
- } else if (typeof _weatherForInstanceToday.val == 'number') {
517
- weatherForecastTodayNum = _weatherForInstanceToday.val;
518
- } else {
519
- weatherForecastTodayNum = 0;
520
- console.log.info('checkActualStates => Wettervorhersage state.val ( ' + _weatherForInstanceToday.val + '); ' + typeof _weatherForInstanceToday.val + ' kann nicht als Number verarbeitet werden');
521
- }
522
- await adapter.setStateAsync('info.rainToday',
523
- weatherForecastTodayNum,
524
- true
525
- );
526
- }
527
-
528
- /**
529
- * Niederschlagsmenge MORGEN in mm
530
- * @type {ioBroker.State | void}
531
- * @private
532
- */
533
- const _weatherForInstance = await adapter.getForeignStateAsync(
534
- adapter.config.weatherForInstance + '.NextDaysDetailed.Location_1.Day_2.rain_value'
535
- ).catch((e) => adapter.log.warn(e));
536
- if (_weatherForInstance && _weatherForInstance.val) {
537
- weatherForecastTomorrowNum = _weatherForInstance.val;
538
- await adapter.setStateAsync(
539
- 'info.rainTomorrow',
540
- weatherForecastTomorrowNum,
541
- true
542
- );
543
- }
544
- }
545
-
546
- // wenn (...sprinkleName.autoOn == false[off]) so wird der aktuelle Sprenger [sprinkleName]
547
- // bei false nicht automatisch gestartet
548
- /**
549
- * Abfrage von ...sprinkleName.autoOn
550
- * @type {[myConfig.config]}
551
- */
552
- const result = myConfig.config;
553
- if (result) {
554
- for (const res of result) {
555
- /**
556
- * Abfrage ... .autoOn beim Start
557
- * @type {ioBroker.State | void}
558
- * @private
559
- */
560
- const _autoOn = await adapter.getForeignStateAsync(
561
- res.autoOnID
562
- ).catch((e) => adapter.log.warn(e));
563
- if (typeof _autoOn.val === 'boolean') {
564
- res.autoOn = _autoOn.val;
565
- }
566
- }
567
- }
568
-
569
- if (adapter.config.actualValueLevel){
570
- /**
571
- * Füllstand der Zisterne in % holen
572
- * @type {ioBroker.State | void}
573
- * @private
574
- */
575
- const _actualValueLevel = await adapter.getForeignStateAsync(
576
- adapter.config.actualValueLevel
577
- ).catch((e) => adapter.log.warn(e));
578
- if (_actualValueLevel && typeof parseFloat(_actualValueLevel.val) === "number") {
579
- valveControl.setFillLevelCistern(parseFloat(_actualValueLevel.val));
580
- }
581
- }
582
- /**
583
- * return the saved objects under sprinkle.*
584
- * rückgabe der gespeicherten Objekte unter sprinkle.*
585
- * @type {ioBroker.NonNullCallbackReturnTypeOf<(err?: (Error | null), objects?: Record<string, ioBroker.AnyObject & {type: "channel"}>) => void> | void}
586
- * @private
587
- */
588
- const _list = await adapter.getForeignObjectsAsync(adapter.namespace + '.sprinkle.*', 'channel').catch((e) => adapter.log.warn(e));
589
- if (_list) {
590
- ObjSprinkle = _list;
591
- }
592
-
593
- } catch (e) {
594
- adapter.log.warn(`sprinkleControl cannot check actual States ... Please check your sprinkleControl states: ${e}`);
595
- }
596
-
597
- }
598
-
599
-
600
- /**
601
- * at 0:05 start of StartTimeSprinkle
602
- * => um 0:05 start von StartTimeSprinkle
603
- * (..., '(s )m h d m wd')
604
- */
605
- const calcPos = schedule.scheduleJob('calcPosTimer', '5 0 * * *', function() {
606
- // Berechnungen mittels SunCalc
607
- sunPos();
608
- today = formatTime(adapter,'', 'day');
609
-
610
- // History Daten aktualisieren, wenn eine neue Woche beginnt
611
- adapter.log.debug(`calcPos 0:05 old-KW: ${kwStr} new-KW: ${formatTime(adapter, '','kW')} if: ${(kwStr !== formatTime(adapter, '','kW'))}`);
612
- if (kwStr !== formatTime(adapter, '','kW')) {
613
- const result = myConfig.config;
614
- if (result) {
615
- for(const i in result) {
616
- if (result.hasOwnProperty(i)) {
617
- const objectName = result[i].objectName;
618
- adapter.getState('sprinkle.' + objectName + '.history.curCalWeekConsumed', (err, state) => {
619
- if (state) {
620
- adapter.setState('sprinkle.' + objectName + '.history.lastCalWeekConsumed', { val: state.val, ack: true });
621
- adapter.setState('sprinkle.' + objectName + '.history.curCalWeekConsumed', { val: 0, ack: true });
622
- }
623
- });
624
- adapter.getState('sprinkle.' + objectName + '.history.curCalWeekRunningTime', (err, state) => {
625
- if (state) {
626
- adapter.setState('sprinkle.' + objectName + '.history.lastCalWeekRunningTime', { val: state.val, ack: true });
627
- adapter.setState('sprinkle.' + objectName + '.history.curCalWeekRunningTime', { val: '00:00', ack: true });
628
- }
629
- });
630
- }
631
-
632
- }
633
- }
634
- kwStr = formatTime(adapter, '','kW');
635
- }
636
-
637
- // ETpToday und ETpYesterday in evaporation aktualisieren da ein neuer Tag
638
- evaporation.setNewDay();
639
-
640
- // Startzeit Festlegen → verzögert wegen Daten von SunCalc
641
- setTimeout(() => {
642
- startTimeSprinkle();
643
- addStartTimeSprinkle();
644
- },1000);
645
-
646
- });
647
-
648
- // Berechnung mittels sunCalc
649
- function sunPos() {
650
- // get today's sunlight times → Holen Sie sich die heutige Sonnenlichtzeit
651
- const times = SunCalc.getTimes(new Date(), adapter.config.latitude, adapter.config.longitude);
652
-
653
- // format sunrise time from the Date object → Formatieren Sie die Sonnenaufgangszeit aus dem Date-Objekt
654
- sunriseStr = ('0' + times.sunrise.getHours()).slice(-2) + ':' + ('0' + times.sunrise.getMinutes()).slice(-2);
655
-
656
- // format golden hour end time from the Date object → Formatiere golden hour end time aus dem Date-Objekt
657
- goldenHourEnd = ('0' + times.goldenHourEnd.getHours()).slice(-2) + ':' + ('0' + times.goldenHourEnd.getMinutes()).slice(-2);
658
-
659
- // format sunset time from the Date object → formatieren Sie die Sonnenuntergangszeit aus dem Date-Objekt
660
- sunsetStr = sunsetStr = ('0' + times.sunset.getHours()).slice(-2) + ':' + ('0' + times.sunset.getMinutes()).slice(-2);
661
-
662
- }
663
-
664
- function addStartTimeSprinkle() {
665
- if (adapter.config.selectAddStartTime === 'greaterETpCurrent' || adapter.config.selectAddStartTime === 'withExternalSignal') {
666
- let addStartTimeSplit = adapter.config.addWateringStartTime.split(':');
667
- const scheduleAddStartTime = schedule.scheduleJob('sprinkleAddStartTime', addStartTimeSplit[1] + ' ' + addStartTimeSplit[0] + ' * * *', function() {
668
- adapter.log.debug(`greaterETpCurrent: ${(adapter.config.selectAddStartTime === 'greaterETpCurrent')} & ${(adapter.config.triggerAddStartTimeETpCur < evaporation.getETpTodayNum())}, withExternalSignal; ${(adapter.config.selectAddStartTime === 'withExternalSignal')} & ${addStartTimeSwitch}`);
669
- if (((adapter.config.selectAddStartTime === 'greaterETpCurrent') && (adapter.config.triggerAddStartTimeETpCur < evaporation.getETpTodayNum()))
670
- || (adapter.config.selectAddStartTime === 'withExternalSignal' && addStartTimeSwitch)) {
671
- let messageText = '';
672
-
673
- // Filter enabled
674
- const result = myConfig.config.filter(d => d.enabled === true);
675
- if (result) {
676
- /**
677
- * Array zum flüchtigen Sammeln von Bewässerungsaufgaben
678
- * @type {Array.<{auto: Boolean, sprinkleID: Number, wateringTime: Number}>}
679
- */
680
- const memAddList = [];
681
-
682
- /**
683
- * result Rain
684
- * - (aktuelle Wettervorhersage - Schwellwert der Regenberücksichtigung) wenn Sensor sich im Freien befindet
685
- * - (> 0) es regnet - Abbruch -
686
- * - ( 0) Start der Bewässerung
687
- * @param {boolean} inGreenhouse - Sensor befindet sich im Gewächshaus
688
- * @returns {number} - resultierende Regenmenge
689
- */
690
- function resRain (inGreenhouse) {
691
- return (adapter.config.weatherForecast && !inGreenhouse) ? (((+ weatherForecastTodayNum) - parseFloat(adapter.config.thresholdRain)).toFixed(1)) : 0;
692
- }
693
-
694
- for(const res of result) {
695
- adapter.log.debug(`${res.objectName}: ${res.autoOn}, ${res.addWateringTime}, ${resRain(res.inGreenhouse)} <= 0, if(${res.autoOn && (res.addWateringTime > 0) && (resRain(res.inGreenhouse) <= 0)})`);
696
- if (res.autoOn // Ventil aktiv
697
- && (res.addWateringTime > 0) // zusätzliche Bewässerung aktiv time > 0
698
- && (resRain(res.inGreenhouse) <= 0)) { // keine Regenvorhersage
699
-
700
-
701
-
702
- switch (res.methodControlSM) {
703
- case 'bistable':
704
- if (res.soilMoisture.bool) {
705
- messageText += `<b>${res.objectName}</b> (${res.soilMoisture.bool})\n`
706
- + ` START => ${addTime(res.addWateringTime, '')}\n`;
707
- memAddList.push({
708
- auto: true,
709
- sprinkleID: res.sprinkleID,
710
- wateringTime: res.addWateringTime
711
- });
712
- }
713
- break;
714
- case 'fixDay':
715
- messageText += `<b>${res.objectName}</b>\n`
716
- + ` START => ${addTime(Math.round(60 * res.addWateringTime), '')}\n`;
717
- memAddList.push({
718
- auto: true,
719
- sprinkleID: res.sprinkleID,
720
- wateringTime: Math.round(60 * res.addWateringTime)
721
- });
722
- break;
723
- case 'calculation':
724
- let addCountdown = res.wateringTime * (res.soilMoisture.maxIrrigation - res.soilMoisture.val) / (res.soilMoisture.maxIrrigation - res.soilMoisture.triggersIrrigation) - res.wateringTime;
725
- adapter.log.debug(`addCountdown: ${addCountdown}, addWateringTime: ${res.addWateringTime}, if(${(addCountdown - res.addWateringTime) > 0})`);
726
- if ((addCountdown - res.addWateringTime) > 0) {
727
- messageText += `<b>${res.objectName}</b> ${res.soilMoisture.pct}% (${res.soilMoisture.pctTriggerIrrigation}%)\n`
728
- + ` START => ${addTime(Math.round(60 * addCountdown), '')}\n`;
729
- memAddList.push({
730
- auto: true,
731
- sprinkleID: res.sprinkleID,
732
- wateringTime: Math.round(60 * addCountdown)
733
- });
734
- }
735
- break;
736
- case 'analog':
737
- if (res.soilMoisture.pct < res.soilMoisture.pctAddTriggersIrrigation) {
738
- messageText += `<b>${res.objectName}</b> ${res.soilMoisture.pct} %(${res.soilMoisture.pctAddTriggersIrrigation}%)\n`
739
- + ` START => ${addTime(Math.round(60 * res.addWateringTime), '')}\n`;
740
- memAddList.push({
741
- auto: true,
742
- sprinkleID: res.sprinkleID,
743
- wateringTime: Math.round(60 * res.addWateringTime)
744
- });
745
- }
746
- break;
747
- }
748
- valveControl.addList(memAddList);
749
- }
750
- }
751
- if(!sendMessageText.onlySendError() && messageText.length > 0){
752
- sendMessageText.sendMessage(messageText);
753
- }
754
- }
755
- }
756
- });
757
- }
758
- }
759
-
760
- // Determination of the irrigation time => Bestimmung der Bewässerungszeit
761
- function startTimeSprinkle() {
762
- let startTimeSplit = [];
763
- let infoMessage;
764
- let messageText = '';
765
-
766
- schedule.cancelJob('sprinkleStartTime');
767
-
768
- // if autoOnOff == false => keine auto Start
769
- if (!autoOnOffStr) {
770
- adapter.log.info(`Sprinkle: autoOnOff == Aus ( ${autoOnOffStr} )`);
771
- adapter.setState('info.nextAutoStart', {
772
- val: 'autoOnOff = off(0)',
773
- ack: true
774
- });
775
- return;
776
- }
777
-
778
- /**
779
- * next start time (automatic)
780
- * Berechnung des nächsten Starts (Automatik)
781
- * @returns {string}
782
- */
783
- function nextStartTime () {
784
- let newStartTime;
785
- let run = 0;
786
- const curTime = new Date();
787
- const myHours = checkTime(curTime.getHours());
788
- const myMinutes = checkTime(curTime.getMinutes());
789
- let myWeekday = curTime.getDay();
790
- const myWeekdayStr = ['So','Mo','Di','Mi','Do','Fr','Sa'];
791
- const myTime = myHours + ':' + myMinutes;
792
-
793
- /**
794
- * aus 0...9 wird String 00...09
795
- * @param {string|number} i
796
- * @returns {string}
797
- */
798
- function checkTime(i) {
799
- return (i < 10) ? '0' + i : i;
800
- }
801
-
802
- do {
803
- myWeekday += run;
804
- run++;
805
- if (myWeekday>6){myWeekday=0;}
806
- // Start time variant according to configuration => Startzeitvariante gemäß Konfiguration
807
- switch(adapter.config.wateringStartTime) {
808
- case 'livingTime' : /*Startauswahl = festen Zeit*/
809
- infoMessage = 'Start zur festen Zeit ';
810
- newStartTime = adapter.config.weekLiving;
811
- break;
812
- case 'livingSunrise' : /*Startauswahl = Sonnenaufgang*/
813
- infoMessage = 'Start mit Sonnenaufgang ';
814
- // format sunrise time from the Date object
815
- newStartTime = addTime(sunriseStr, parseInt(adapter.config.timeShift));
816
- break;
817
- case 'livingGoldenHourEnd' : /*Startauswahl = Ende der Golden Hour*/
818
- infoMessage = 'Start zum Ende der Golden Hour ';
819
- // format goldenHourEnd time from the Date object
820
- newStartTime = goldenHourEnd;
821
- break;
822
- case 'livingSunset' : /*Startauswahl = Sonnenuntergang*/
823
- infoMessage = 'Start mit Sonnenuntergang ';
824
- // format sunset time from the Date object
825
- newStartTime = addTime(sunsetStr, parseInt(adapter.config.timeShift));
826
- break;
827
- }
828
- // Start am Wochenende →, wenn andere Zeiten verwendet werden soll
829
- if((adapter.config.publicWeekend) && ((myWeekday) === 6 || (myWeekday) === 0)){
830
- infoMessage = 'Start am Wochenende ';
831
- newStartTime = adapter.config.weekEndLiving;
832
- }
833
- // Start an Feiertagen →, wenn Zeiten des Wochenendes verwendet werden soll
834
- if((adapter.config.publicHolidays) && (adapter.config.publicWeekend)
835
- && (((publicHolidayStr === true) && (run === 1)) // heute Feiertag && erster Durchlauf
836
- || ((publicHolidayTomorrowStr === true) && (run === 2)) // morgen Feiertag && zweiter Durchlauf
837
- || (holidayStr === true))) { // Urlaub
838
- infoMessage = 'Start am Feiertag ';
839
- newStartTime = adapter.config.weekEndLiving;
840
- }
841
- } while ((newStartTime <= myTime) && (run === 1));
842
-
843
- const newStartTimeLong = myWeekdayStr[myWeekday] + ' ' + newStartTime;
844
- /**
845
- * next Auto-Start
846
- * @param {string|null} err
847
- * @param {State|null|undefined} state
848
- */
849
- adapter.getState('info.nextAutoStart', (err, state) =>{
850
- if (state) {
851
- if (state.val !== newStartTimeLong) {
852
- adapter.setState('info.nextAutoStart', {
853
- val: newStartTimeLong,
854
- ack: true
855
- });
856
- // next Start Message
857
- if(!sendMessageText.onlySendError){
858
- sendMessageText.sendMessage(infoMessage + '(' + myWeekdayStr[myWeekday] + ') um ' + newStartTime);
859
- }
860
- adapter.log.info(`${infoMessage} (${myWeekdayStr[myWeekday]}) um ${newStartTime}`);
861
- }
862
- }
863
- });
864
- return newStartTime;
865
- }
866
- //
867
- startTimeStr = nextStartTime();
868
- startTimeSplit = startTimeStr.split(':');
869
-
870
- const scheduleStartTime = schedule.scheduleJob('sprinkleStartTime', startTimeSplit[1] + ' ' + startTimeSplit[0] + ' * * *', function() {
871
- // Filter enabled
872
- const result = myConfig.config.filter(d => d.enabled === true);
873
- if (result) {
874
- /**
875
- * Array zum flüchtigen Sammeln von Bewässerungsaufgaben
876
- * @type {Array.<{auto: Boolean, sprinkleID: Number, wateringTime: Number}>}
877
- */
878
- const memAddList = [];
879
-
880
- /**
881
- * result Rain
882
- * - (aktuelle Wettervorhersage - Schwellwert der Regenberücksichtigung) wenn Sensor sich im Freien befindet
883
- * - (> 0) es regnet - Abbruch -
884
- * - ( 0) Start der Bewässerung
885
- * @param {boolean} inGreenhouse - Sensor befindet sich im Gewächshaus
886
- * @returns {number} - resultierende Regenmenge
887
- */
888
- function resRain (inGreenhouse) {
889
- return (adapter.config.weatherForecast && !inGreenhouse) ? (((+ weatherForecastTodayNum) - parseFloat(adapter.config.thresholdRain)).toFixed(1)) : 0;
890
- }
891
-
892
- for(const res of result) {
893
- messageText += '<b>' + res.objectName + '</b>';
894
- switch (res.methodControlSM) {
895
- case 'bistable':
896
- messageText += ' (' + res.soilMoisture.bool + ')\n';
897
- break;
898
- case 'analog':
899
- messageText += ' ' + res.soilMoisture.pct + '% (' + res.soilMoisture.pctTriggerIrrigation + '%)\n';
900
- break;
901
- case 'fixDay':
902
- messageText += ' (' + curNextFixDay(res.sprinkleID, true) + ')\n';
903
- break;
904
- case 'calculation':
905
- messageText += ' ' + res.soilMoisture.pct + '% (' + res.soilMoisture.pctTriggerIrrigation + '%)\n';
906
- break;
907
- }
908
-
909
- // Test Bodenfeuchte
910
- adapter.log.debug(`Bodenfeuchte: ${res.soilMoisture.val} <= ${res.soilMoisture.triggersIrrigation} AutoOn: ${res.autoOn}`);
911
- if (res.autoOn) {
912
- switch (res.methodControlSM) {
913
- // -- bistable -- Bodenfeuchte-Sensor mit 2-Punkt-Regler true und false -- //
914
- case 'bistable':
915
- if(res.soilMoisture.bool) {
916
- /* Wenn in der Config Regenvorhersage aktiviert: Startvorgang abbrechen, wenn der Regen den eingegebenen Schwellwert überschreitet. */
917
- if (resRain(res.inGreenhouse) <= 0) {
918
- const curWateringTime = Math.round(60 * res.wateringTime * evaporation.timeExtension(res.wateringAdd));
919
- memAddList.push({
920
- auto: true,
921
- sprinkleID: res.sprinkleID,
922
- wateringTime: curWateringTime
923
- });
924
- messageText += ' START => ' + addTime(curWateringTime, '') + '\n';
925
- } else if (adapter.config.weatherForecast) {
926
- /* Bewässerung unterdrückt da ausreichende Regenvorhersage */
927
- messageText += ' ' + '<i>' + 'Start verschoben, da heute ' + weatherForecastTodayNum + 'mm Niederschlag' + '</i> ' + '\n';
928
- adapter.log.info(`${res.objectName}: Start verschoben, da Regenvorhersage für Heute ${weatherForecastTodayNum} mm [ ${resRain(res.inGreenhouse)} > 0 ]`);
929
- }
930
- }
931
- break;
932
- // --- analog -- Bodenfeuchte-Sensor im Wertebereich von 0 bis 100% --- //
933
- // -- Prozentuale Bodenfeuchte zu gering -- //
934
- case 'analog':
935
- if(res.soilMoisture.pct <= res.soilMoisture.pctTriggerIrrigation) {
936
- /* Wenn in der Config Regenvorhersage aktiviert: Startvorgang abbrechen, wenn der Regen den eingegebenen Schwellwert überschreitet. */
937
- if (resRain(res.inGreenhouse) <= 0) {
938
- let countdown = res.wateringTime * (100 - res.soilMoisture.pct) / (100 - res.soilMoisture.pctTriggerIrrigation); // in min
939
- // Begrenzung der Bewässerungszeit auf dem in der Config eingestellten Überschreitung (in Prozent)
940
- if (countdown > (res.wateringTime * res.wateringAdd / 100)) {countdown = res.wateringTime * res.wateringAdd / 100;}
941
- memAddList.push({
942
- auto: true,
943
- sprinkleID: res.sprinkleID,
944
- wateringTime: Math.round(60* countdown)
945
- });
946
- messageText += ' START => ' + addTime(Math.round(60*countdown), '') + '\n';
947
- } else if (adapter.config.weatherForecast) {
948
- /* Bewässerung unterdrückt da ausreichende Regenvorhersage */
949
- messageText += ' ' + '<i>' + 'Start verschoben, da heute ' + weatherForecastTodayNum + 'mm Niederschlag' + '</i> ' + '\n';
950
- adapter.log.info(`${res.objectName}: Start verschoben, da Regenvorhersage für Heute ${weatherForecastTodayNum} mm [ ${resRain(res.inGreenhouse)} > 0 ]`);
951
- }
952
- }
953
- break;
954
- // --- fixDay -- Start an festen Tagen ohne Sensoren --- //
955
- // -- Bewässerungstag erreicht //
956
- case 'fixDay':
957
- /* Wenn in der Config Regenvorhersage aktiviert: Startvorgang abbrechen, wenn der Regen den eingegebenen Schwellwert überschreitet. */
958
- if (resRain(false) <= 0) {
959
- // Bewässerungstag erreicht
960
- if (res.startFixDay[today]) {
961
- const curWateringTime = Math.round(60 * res.wateringTime * evaporation.timeExtension(res.wateringAdd));
962
- memAddList.push({
963
- auto: true,
964
- sprinkleID: res.sprinkleID,
965
- wateringTime: curWateringTime
966
- });
967
- messageText += ' START => ' + addTime(curWateringTime, '') + '\n';
968
- if (res.startDay === 'threeRd'){ // Next Start in 3 Tagen
969
- res.startFixDay[today] = false;
970
- res.startFixDay[(+ today + 3 > 6) ? (+ today-4) : (+ today+3)] = true;
971
- }else if (res.startDay === 'twoNd') { // Next Start in 2 Tagen
972
- res.startFixDay[today] = false;
973
- res.startFixDay[(+ today + 2 > 6) ? (+ today-5) : (+ today+2)] = true;
974
- }
975
- }
976
- } else if (adapter.config.weatherForecast){
977
- messageText += ' ' + '<i>' + 'Start verschoben, da heute ' + weatherForecastTodayNum + 'mm Niederschlag' + '</i> ' + '\n';
978
- adapter.log.info(`${res.objectName}: Start verschoben, da Regenvorhersage für Heute ${weatherForecastTodayNum} mm [ ${resRain(false)} > 0 ]`);
979
- res.startFixDay[today] = false;
980
- res.startFixDay[(+ today + 1 > 6) ? (+ today-6) : (+ today+1)] = true;
981
- }
982
- curNextFixDay(res.sprinkleID, false);
983
- break;
984
- // --- calculation -- Berechnung der Bodenfeuchte --- //
985
- // -- Bodenfeuchte zu gering -- //
986
- case 'calculation':
987
- if (res.soilMoisture.val <= res.soilMoisture.triggersIrrigation) {
988
- /* Wenn in der Config Regenvorhersage aktiviert: Startvorgang abbrechen, wenn es heute ausreichend regnen sollte. */
989
- const resMoisture = (adapter.config.weatherForecast)?((+ res.soilMoisture.val) + (+ weatherForecastTodayNum) - parseFloat(adapter.config.thresholdRain)):(res.soilMoisture.val); // aktualisierte Bodenfeuchte mit Regenvorhersage
990
- if ((resMoisture <= res.soilMoisture.triggersIrrigation) || res.inGreenhouse) { // Kontrolle ob Regenvorhersage ausreicht || Bewässerung inGreenhouse
991
- let countdown = res.wateringTime * (res.soilMoisture.maxIrrigation - res.soilMoisture.val) / (res.soilMoisture.maxIrrigation - res.soilMoisture.triggersIrrigation); // in min
992
- // Begrenzung der Bewässerungszeit auf dem in der Config eingestellten Überschreitung (in Prozent)
993
- if (countdown > (res.wateringTime * res.wateringAdd / 100)) {countdown = res.wateringTime * res.wateringAdd / 100;}
994
- memAddList.push({
995
- auto: true,
996
- sprinkleID: res.sprinkleID,
997
- wateringTime: Math.round(60*countdown)
998
- });
999
- messageText += ' START => ' + addTime(Math.round(60*countdown), '') + '\n';
1000
- } else if (adapter.config.weatherForecast) {
1001
- /* Bewässerung unterdrückt da ausreichende Regenvorhersage */
1002
- messageText += ' ' + '<i>' + 'Start verschoben, da heute ' + weatherForecastTodayNum + 'mm Niederschlag' + '</i> ' + '\n';
1003
- adapter.log.info(`${res.objectName}: Start verschoben, da Regenvorhersage für Heute ${weatherForecastTodayNum} mm [ ${res.soilMoisture.val} (${resMoisture}) <= ${res.soilMoisture.triggersIrrigation} ]`);
1004
- }
1005
- }
1006
- break;
1007
- }
1008
- } else {
1009
- messageText += ' ' + '<i>' + 'Ventil auf Handbetrieb' + '</i>' + '\n';
1010
- }
1011
- }
1012
- valveControl.addList(memAddList);
1013
- }
1014
- if(!sendMessageText.onlySendError()){
1015
- sendMessageText.sendMessage(messageText);
1016
- }
1017
- setTimeout (() => {
1018
- setTimeout(()=>{
1019
- nextStartTime();
1020
- }, 800);
1021
- schedule.cancelJob('sprinkleStartTime');
1022
- }, 200);
1023
- });
1024
- }
1025
-
1026
- //
1027
- async function createSprinklers() {
1028
- /**Creates an Object .control.addStartTimeSwitch, when additional watering has been activated via an external signal
1029
- * - Erzeugt ein Object .control.addStartTimeSwitch, wenn die Zusatzbewässerung über ein externes Signal aktiviert wurde
1030
- * @type {{id: string, name: string} | void}
1031
- * @private
1032
- */
1033
- const _addStartTimeSwitch = await adapter.findForeignObjectAsync(`${adapter.namespace}.control.addStartTimeSwitch`, 'boolean').catch((e) => adapter.log.warn(`.control.addStartTimeSwitch ${e}`));
1034
- if (_addStartTimeSwitch.id !== `${adapter.namespace}.control.addStartTimeSwitch` && adapter.config.selectAddStartTime === 'withExternalSignal') {
1035
- adapter.setObjectNotExistsAsync(`${adapter.namespace}.control.addStartTimeSwitch`, {
1036
- 'type': 'state',
1037
- 'common': {
1038
- "role": "switch",
1039
- 'name': 'additional irrigation enabled',
1040
- 'type': 'boolean',
1041
- 'read': true,
1042
- 'write': true,
1043
- 'def': false
1044
- },
1045
- 'native': {}
1046
- }).catch((e) => adapter.log.warn(`.control.addStartTimeSwitch ${e}`));
1047
- } else if (_addStartTimeSwitch.id === `${adapter.namespace}.control.addStartTimeSwitch`) {
1048
- if (adapter.config.selectAddStartTime !== 'withExternalSignal') {
1049
- adapter.delObjectAsync(`${adapter.namespace}.control.addStartTimeSwitch`).catch((e) => adapter.log.warn(`.control.addStartTimeSwitch ${e}`));
1050
- addStartTimeSwitch = false;
1051
- } else {
1052
- /** auslesen .control.addStartTimeSwitch.val
1053
- * @type {ioBroker.State | void}
1054
- * @private
1055
- */
1056
- const _state = await adapter.getStateAsync(`${adapter.namespace}.control.addStartTimeSwitch`).catch((e) => adapter.log.warn(`.control.addStartTimeSwitch ${e}`));
1057
- if (typeof _state.val === 'boolean') {
1058
- addStartTimeSwitch = _state.val;
1059
- }
1060
- }
1061
- }
1062
-
1063
-
1064
-
1065
- const result = adapter.config.events;
1066
- if (result) {
1067
- for(const res of result) {
1068
- let objectName;
1069
-
1070
- if(res.sprinkleName !== '') {
1071
- objectName = res.sprinkleName.replace(/[.;, ]/g, '_');
1072
- } else if (res.sprinkleName === '') {
1073
- objectName = res.name.replace(/[.;, ]/g, '_');
1074
- }
1075
-
1076
- const objPfad = 'sprinkle.' + objectName;
1077
- const j = myConfig.config.findIndex(d => d.objectName === objectName);
1078
-
1079
- // Create bzw. update .actualSoilMoisture
1080
-
1081
- let nameMetConSM, objMetConSM;
1082
- await fillMetConSM(res);
1083
- async function fillMetConSM(res) {
1084
- //adapter.log.debug(JSON.stringify(res));
1085
- switch (res.methodControlSM) {
1086
- case 'calculation':
1087
- nameMetConSM = objectName + ' => Calculated soil moisture in %';
1088
- objMetConSM = {
1089
- 'type': 'state',
1090
- 'common': {
1091
- 'role': 'state',
1092
- 'name': nameMetConSM,
1093
- 'type': 'number',
1094
- 'min': 0,
1095
- 'max': 150,
1096
- 'unit': '%',
1097
- 'read': true,
1098
- 'write': false,
1099
- 'def': 50
1100
- },
1101
- 'native': {},
1102
- };
1103
- break;
1104
- case 'bistable':
1105
- nameMetConSM = objectName + ' => bistable soil moisture sensor';
1106
- objMetConSM = {
1107
- 'type': 'state',
1108
- 'common': {
1109
- 'role': 'state',
1110
- 'name': nameMetConSM,
1111
- 'type': 'boolean',
1112
- 'read': true,
1113
- 'write': false,
1114
- 'def': false
1115
- },
1116
- 'native': {},
1117
- };
1118
- break;
1119
- case 'analog':
1120
- nameMetConSM = objectName + ' => analog soil moisture sensor in %';
1121
- objMetConSM = {
1122
- 'type': 'state',
1123
- 'common': {
1124
- 'role': 'state',
1125
- 'name': nameMetConSM,
1126
- 'type': 'number',
1127
- 'min': 0,
1128
- 'max': 150,
1129
- 'unit': '%',
1130
- 'read': true,
1131
- 'write': false
1132
- },
1133
- 'native': {},
1134
- };
1135
- break;
1136
- case 'fixDay':
1137
- nameMetConSM = objectName + ' => start on a fixed day';
1138
- objMetConSM = {
1139
- 'type': 'state',
1140
- 'common': {
1141
- 'role': 'state',
1142
- 'name': nameMetConSM,
1143
- 'type': 'number',
1144
- 'min': 0,
1145
- 'max': 7,
1146
- 'states': {"0":"Sun", "1":"Mon", "2":"Tue", "3":"Wed", "4":"Thur", "5":"Fri", "6":"Sat", "7":"off"},
1147
- 'read': true,
1148
- 'write': false,
1149
- 'def': 7
1150
- },
1151
- 'native': {},
1152
- };
1153
- break;
1154
- default:
1155
- adapter.log.warn(`sprinkleControl cannot created ... Please check your sprinkleControl config ${objectName} methodControl`);
1156
- nameMetConSM = objectName + ' => Emergency program! start on a fixed day';
1157
- objMetConSM = {
1158
- 'type': 'state',
1159
- 'common': {
1160
- 'role': 'state',
1161
- 'name': nameMetConSM,
1162
- 'type': 'number',
1163
- 'min': 0,
1164
- 'max': 7,
1165
- 'states': {"0":"Sun", "1":"Mon", "2":"Tue", "3":"Wed", "4":"Thur", "5":"Fri", "6":"Sat", "7":"off"},
1166
- 'read': true,
1167
- 'write': false,
1168
- 'def': 7
1169
- },
1170
- 'native': {},
1171
- };
1172
- }
1173
- }
1174
- // ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ //
1175
- // +++++ Objekte erstellen +++++ //
1176
- // ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ //
1177
- if (objectName && objectName !== '') {
1178
- try {
1179
- // Create Object for sprinkle. (ID)
1180
- const _sprinkleNotExist = await adapter.setObjectNotExistsAsync('sprinkle.' + objectName, {
1181
- 'type': 'channel',
1182
- 'common': {
1183
- 'name': res.sprinkleName
1184
- },
1185
- 'native': {},
1186
- });
1187
- // Create Object for .history
1188
- const _historyNotExist = await adapter.setObjectNotExistsAsync('sprinkle.' + objectName + '.history', {
1189
- 'type': 'channel',
1190
- 'common': {
1191
- 'name': res.sprinkleName + ' => History'
1192
- },
1193
- 'native': {},
1194
- });
1195
- // Create Object for .history.curCalWeekConsumed
1196
- // Sprinkler consumption of the current calendar week => History - Sprinkler-Verbrauch der aktuellen Kalenderwoche (783 Liter)
1197
- const _curCalWeekConsumedNotExist = adapter.setObjectNotExistsAsync(objPfad + '.history.curCalWeekConsumed', {
1198
- 'type': 'state',
1199
- 'common': {
1200
- 'role': 'state',
1201
- 'name': objectName + ' => History - Sprinkler consumption of the current calendar week',
1202
- 'type': 'number',
1203
- 'unit': 'Liter',
1204
- 'read': true,
1205
- 'write': false,
1206
- 'def': 0
1207
- },
1208
- 'native': {},
1209
- });
1210
- // Create Object for .history.curCalWeekRunningTime
1211
- // Sprinkler running time of the current calendar week => History - Sprinkler-Laufzeit der aktuellen Kalenderwoche (783 Liter)
1212
- const _curCalWeekRunningTimeNotExist = adapter.setObjectNotExistsAsync(objPfad + '.history.curCalWeekRunningTime', {
1213
- 'type': 'state',
1214
- 'common': {
1215
- 'role': 'state',
1216
- 'name': objectName + ' => History - Sprinkler running time of the current calendar week',
1217
- 'type': 'string',
1218
- 'read': true,
1219
- 'write': false,
1220
- 'def': '00:00'
1221
- },
1222
- 'native': {},
1223
- });
1224
- // Create Object for .history.lastCalWeekConsumed
1225
- // Sprinkler consumption of the last calendar week => History - Sprinkler-Verbrauch der letzten Kalenderwoche (783 Liter)
1226
- const _lastCalWeekConsumedNotExist = adapter.setObjectNotExistsAsync(objPfad + '.history.lastCalWeekConsumed', {
1227
- 'type': 'state',
1228
- 'common': {
1229
- 'role': 'state',
1230
- 'name': objectName + ' => History - Sprinkler consumption of the last calendar week',
1231
- 'type': 'number',
1232
- 'unit': 'Liter',
1233
- 'read': true,
1234
- 'write': false,
1235
- 'def': 0
1236
- },
1237
- 'native': {},
1238
- });
1239
- // Create Object for .history.lastCalWeekRunningTime
1240
- // Sprinkler running time of the last calendar week => History - Sprinkler-Laufzeit der letzten Kalenderwoche (783 Liter)
1241
- const _lastCalWeekRunningTimeNotExist = adapter.setObjectNotExistsAsync(objPfad + '.history.lastCalWeekRunningTime', {
1242
- 'type': 'state',
1243
- 'common': {
1244
- 'role': 'state',
1245
- 'name': objectName + ' => History - Sprinkler running time of the last calendar week',
1246
- 'type': 'string',
1247
- 'read': true,
1248
- 'write': false,
1249
- 'def': '00:00'
1250
- },
1251
- 'native': {},
1252
- });
1253
- // Create Object for .history.lastConsumed
1254
- // Last consumed of sprinkler => History - Letzte Verbrauchsmenge des Ventils (783 Liter)
1255
- const _lastConsumedNotExist = adapter.setObjectNotExistsAsync(objPfad + '.history.lastConsumed', {
1256
- 'type': 'state',
1257
- 'common': {
1258
- 'role': 'state',
1259
- 'name': objectName + ' => History - Last consumed of sprinkler',
1260
- 'type': 'number',
1261
- 'unit': 'Liter',
1262
- 'read': true,
1263
- 'write': false,
1264
- 'def': 0
1265
- },
1266
- 'native': {},
1267
- });
1268
- // Create Object for .history.lastOn
1269
- // Last On of sprinkler => History - Letzter Start des Ventils (30.03 06:30)
1270
- const _lastOnNotExist = adapter.setObjectNotExistsAsync(objPfad + '.history.lastOn', {
1271
- 'type': 'state',
1272
- 'common': {
1273
- 'role': 'state',
1274
- 'name': objectName + ' => History - Last On of sprinkler',
1275
- 'type': 'string',
1276
- 'read': true,
1277
- 'write': false,
1278
- 'def': '-'
1279
- },
1280
- 'native': {},
1281
- });
1282
- // Create Object for .history.lastRunningTime
1283
- // Last running time of sprinkler => History - Letzte Laufzeit des Ventils (0 sek, 47:00 min, 1:03:45 )
1284
- const _lastRunningTimeNotExist = adapter.setObjectNotExistsAsync(objPfad + '.history.lastRunningTime', {
1285
- 'type': 'state',
1286
- 'common': {
1287
- 'role': 'state',
1288
- 'name': objectName + ' => History - Last running time',
1289
- 'type': 'string',
1290
- 'read': true,
1291
- 'write': false,
1292
- 'def': '00:00'
1293
- },
1294
- 'native': {},
1295
- });
1296
- // Create Object for .autoOn
1297
- const _autoOnNotExist = adapter.setObjectNotExistsAsync(objPfad + '.autoOn', {
1298
- 'type': 'state',
1299
- 'common': {
1300
- 'role': 'Switch',
1301
- 'name': objectName + ' => Switch automatic mode on / off',
1302
- 'type': 'boolean',
1303
- "states": {
1304
- "false": "off",
1305
- "true": "on"
1306
- },
1307
- 'read': true,
1308
- 'write': true,
1309
- 'def': true
1310
- },
1311
- 'native': {},
1312
- }).catch((e) => adapter.log.warn(`set .autoOn ${e}`));
1313
- // Create Object for .actualSoilMoisture
1314
- const _actualSoilMoistureFind = await adapter.findForeignObjectAsync(`${adapter.namespace}.${objPfad}.actualSoilMoisture`, `${objMetConSM.common.type}`);
1315
- if (_actualSoilMoistureFind.id !== `${adapter.namespace}.${objPfad}.actualSoilMoisture` || _actualSoilMoistureFind.name !== nameMetConSM) {
1316
- await adapter.setObjectAsync(
1317
- objPfad + '.actualSoilMoisture',
1318
- objMetConSM
1319
- ).catch((e) => adapter.log.warn(e));
1320
- adapter.log.info(`sprinkleControl [sprinkle.${objectName}.actualSoilMoisture] was updated`);
1321
- }
1322
-
1323
- // postponeByOneDay um einen Tag verschieben bei fixDay (twoNd & threeRd)
1324
- const _postponeByOneDay = await adapter.findForeignObjectAsync(`${adapter.namespace}.${objPfad}.postponeByOneDay`, `boolean`);
1325
- if (_postponeByOneDay.id !== `${adapter.namespace}.${objPfad}.postponeByOneDay`
1326
- && res.methodControlSM === "fixDay"
1327
- && (res.startDay === "twoNd"
1328
- || res.startDay === "threeRd")) {
1329
- await adapter.setObjectNotExistsAsync(objPfad + '.postponeByOneDay', {
1330
- 'type': 'state',
1331
- 'common': {
1332
- "role": 'button',
1333
- "name": objectName + 'Postpone start by one day',
1334
- "type": 'boolean',
1335
- "read": true,
1336
- "write": true,
1337
- "def": false
1338
- },
1339
- 'native': {
1340
- "UNIT": "",
1341
- "TAB_ORDER": 0,
1342
- "OPERATIONS": 6,
1343
- "FLAGS": 1,
1344
- "TYPE": "ACTION",
1345
- "MIN": false,
1346
- "MAX": true,
1347
- "DEFAULT": false
1348
- }
1349
- });
1350
- adapter.subscribeStates(`${adapter.namespace}.${objPfad}.postponeByOneDay`);
1351
- } else if (res.methodControlSM === "fixDay"
1352
- && (res.startDay === "twoNd"
1353
- || res.startDay === "threeRd")) {
1354
- adapter.subscribeStates(`${adapter.namespace}.${objPfad}.postponeByOneDay`);
1355
- } else {
1356
- await adapter.delObjectAsync(`${adapter.namespace}.${objPfad}.postponeByOneDay`); // "sprinklecontrol.0.sprinkle.???.actualSoilMoisture"
1357
- }
1358
-
1359
- // Create Object for .countdown => Countdown des Ventils
1360
- const _countdownNotExist = adapter.setObjectNotExistsAsync(objPfad + '.countdown', {
1361
- 'type': 'state',
1362
- 'common': {
1363
- 'role': 'state',
1364
- 'name': objectName + ' => countdown of sprinkler',
1365
- 'type': 'string',
1366
- 'read': true,
1367
- 'write': false,
1368
- 'def': '-'
1369
- },
1370
- 'native': {},
1371
- });
1372
- // Create Object for .runningTime => Laufzeit des Ventils
1373
- const _runningTimeNotExist = await adapter.setObjectNotExistsAsync(objPfad + '.runningTime', {
1374
- 'type': 'state',
1375
- 'common': {
1376
- 'role': 'state',
1377
- 'name': objectName + ' => running time of sprinkler',
1378
- 'type': 'string',
1379
- 'read': true,
1380
- 'write': true,
1381
- 'def': '-'
1382
- },
1383
- 'native': {},
1384
- });
1385
- // Create Object for .sprinklerState => Zustand des Ventils im Thread
1386
- // <<< 1 = warten >>> ( 0:off; 1:wait; 2:on; 3:break; 4:Boost(on); 5:off(Boost) )
1387
- // Create .sprinklerState
1388
- const _sprinklerStateNotExists = await adapter.setObjectNotExistsAsync(objPfad + '.sprinklerState', {
1389
- 'type': 'state',
1390
- 'common': {
1391
- 'role': 'state',
1392
- 'name': objectName + ' => actual state of sprinkler',
1393
- 'type': 'number',
1394
- 'min': 0,
1395
- 'max': 5,
1396
- 'states': {
1397
- "0": "off",
1398
- "1": "wait",
1399
- "2": "on",
1400
- "3": "break",
1401
- "4": "Boost(on)",
1402
- "5": "off(Boost)"
1403
- },
1404
- 'read': true,
1405
- 'write': false,
1406
- 'def': 0
1407
- },
1408
- 'native': {},
1409
- });
1410
- // Create Object for triggerPoint Schaltpunkt der Bodenfeuchte
1411
- const _triggerPointNotExist = await adapter.setObjectNotExistsAsync(objPfad + '.triggerPoint', {
1412
- 'type': 'state',
1413
- 'common': {
1414
- 'role': 'state',
1415
- 'name': objectName + ' => Trigger point of sprinkler',
1416
- 'type': 'string',
1417
- 'read': true,
1418
- 'write': false,
1419
- 'def': '-'
1420
- },
1421
- 'native': {},
1422
- });
1423
- // Object created
1424
- let value = true;
1425
- await (await Promise.all([
1426
- _sprinkleNotExist,
1427
- _historyNotExist,
1428
- _curCalWeekConsumedNotExist,
1429
- _curCalWeekRunningTimeNotExist,
1430
- _lastCalWeekConsumedNotExist,
1431
- _lastCalWeekRunningTimeNotExist,
1432
- _lastConsumedNotExist,
1433
- _lastOnNotExist,
1434
- _lastRunningTimeNotExist,
1435
- _autoOnNotExist,
1436
- _countdownNotExist,
1437
- _runningTimeNotExist,
1438
- _sprinklerStateNotExists,
1439
- _triggerPointNotExist
1440
- ])).forEach((val) => {
1441
- value &= val;
1442
- });
1443
- if(value) {
1444
- adapter.log.info(`sprinkleControl [sprinkle.${objectName}] was created`);
1445
- }
1446
-
1447
- // ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ //
1448
- // +++++ zustände der States aktualisieren +++++ //
1449
- // ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ //
1450
-
1451
- //
1452
- if(await _countdownNotExist){
1453
- const _countdown = await adapter.getStateAsync(objPfad + '.countdown').catch((e) => adapter.log.warn(`${objectName}.countdown ${e}`));
1454
- if (_countdown && _countdown.val !== '0') {
1455
- adapter.setStateAsync(
1456
- objPfad + '.countdown',
1457
- '0',
1458
- true
1459
- ).catch((e) => adapter.log.warn(e));
1460
- }
1461
- }
1462
- //
1463
- if (_runningTimeNotExist) {
1464
- const _runningTime = await adapter.getStateAsync(objPfad + '.runningTime').catch((e) => adapter.log.warn(`${objectName}.runningTime ${e}`));
1465
- if (_runningTime && _runningTime.val !== '00:00') {
1466
- adapter.setStateAsync(objPfad + '.runningTime',
1467
- '00:00',
1468
- true
1469
- ).catch((e) => adapter.log.warn(e));
1470
- }
1471
- }
1472
- //
1473
- if (_sprinklerStateNotExists){
1474
- const _sprinklerState = await adapter.getStateAsync(objPfad + '.sprinklerState').catch((e) => adapter.log.warn(`${objectName}.sprinklerState ${e}`));
1475
- if (_sprinklerState && _sprinklerState.val !== 0) {
1476
- await adapter.setStateAsync(objPfad + '.sprinklerState',
1477
- 0,
1478
- true
1479
- ).catch((e) => adapter.log.warn(`${objectName}.sprinklerState ${e}`));
1480
- }
1481
- }
1482
- // Festlegen des Schaltpunktes für den nächsten Start
1483
- switch (myConfig.config[j].methodControlSM) {
1484
- case 'bistable':
1485
- // Sensor soil moisture => Sensor Bodenfeuchte
1486
- const _triggerSMBistabile = await adapter.getForeignStateAsync(myConfig.config[j].triggerSM).catch((e) => adapter.log.warn(`${objectName}.triggerSMBistabile ${e}`));
1487
- if (_triggerSMBistabile && typeof _triggerSMBistabile.val === 'boolean') {
1488
- myConfig.setSoilMoistBool(myConfig.config[j].sprinkleID, _triggerSMBistabile.val);
1489
- } else {
1490
- myConfig.setSoilMoistBool(myConfig.config[j].sprinkleID, true);
1491
- adapter.log.warn(`The bistable sensor ${myConfig.config[j].triggerSM} in ${objectName} does not deliver correct values!`)
1492
- }
1493
-
1494
- adapter.setStateAsync(objPfad + '.triggerPoint',
1495
- '-',
1496
- true
1497
- ).catch((e) => adapter.log.warn(`${objectName}.triggerPoint ${e}`));
1498
- break;
1499
-
1500
- case 'analog':
1501
- // Sensor soil moisture => Sensor Bodenfeuchte
1502
- /**
1503
- * Schaltpunkt '.triggerSM' auslesen
1504
- * @type {ioBroker.State | void} _triggerSM - Schaltpunkt
1505
- * @private
1506
- */
1507
- const _triggerSMAnalog = await adapter.getForeignStateAsync(myConfig.config[j].triggerSM).catch((e) => adapter.log.warn(`${objectName}.triggerSMAnalog ${e}`));
1508
- if (_triggerSMAnalog && (typeof _triggerSMAnalog.val === 'number' || typeof _triggerSMAnalog.val === 'string')) {
1509
- myConfig.setSoilMoistPct(myConfig.config[j].sprinkleID, _triggerSMAnalog.val);
1510
- } else {
1511
- await adapter.setStateAsync(objPfad + '.actualSoilMoisture',
1512
- 50,
1513
- true
1514
- );
1515
- adapter.log.warn(`The analoge sensor ${myConfig.config[j].triggerSM} in ${objectName} does not deliver correct values!`)
1516
- }
1517
- adapter.setStateAsync(objPfad + '.triggerPoint',
1518
- (myConfig.config[j].soilMoisture.pctTriggerIrrigation).toString(),
1519
- true
1520
- ).catch((e) => adapter.log.warn(`${objectName}.triggerPoint setState ${e}`));
1521
- break;
1522
-
1523
- case 'fixDay':
1524
-
1525
- /** @type {number} */
1526
- const nextStartDay = ((today + 1) > 6 ? 0 : (today + 1));
1527
-
1528
- /**
1529
- * Neuen Start-Tag für Dreitage- und Zweitage-modus setzen
1530
- * @param {boolean} threeRd - Dreitage-modus Ja/Nein
1531
- * true → Dreitage-modus (treeRD)
1532
- * false → Zweitage-modus (twoNd)
1533
- */
1534
- async function setNewDay (threeRd) {
1535
- const today = formatTime(adapter,'', 'day');
1536
- /**
1537
- *
1538
- * @type {ioBroker.GetStatePromise} _actualSoilMoisture
1539
- * @private {ioBroker.State|Void} _actualSoilMoisture
1540
- */
1541
- const _actualSoilMoisture = await adapter.getStateAsync(
1542
- objPfad + '.actualSoilMoisture'
1543
- ).catch((e) => adapter.log.warn(`${objectName}.actualSoilMoisture fixDay setState ${e}`));
1544
- if (_actualSoilMoisture && (typeof _actualSoilMoisture.val === 'number')) {
1545
- if ((_actualSoilMoisture.val >= 0) && (_actualSoilMoisture.val <= 6)) {
1546
- if ((threeRd)
1547
- && (_actualSoilMoisture.val === (((today + 3) > 6) ? 2 : (today + 3)))
1548
- || (_actualSoilMoisture.val === (((today + 2) > 6) ? 1 : (today + 2)))
1549
- || (_actualSoilMoisture.val === (((today + 1) > 6) ? 0 : (today + 1)))
1550
- || (_actualSoilMoisture.val === today)) {
1551
- myConfig.config[j].startFixDay[_actualSoilMoisture.val] = true;
1552
- } else {
1553
- myConfig.config[j].startFixDay[nextStartDay] = true;
1554
- }
1555
- } else {
1556
- myConfig.config[j].startFixDay[nextStartDay] = true;
1557
- }
1558
- curNextFixDay(myConfig.config[j].sprinkleID, false);
1559
- }
1560
- }
1561
-
1562
- if (myConfig.config[j].startDay === 'threeRd') {
1563
- await setNewDay(true);
1564
- } else if (myConfig.config[j].startDay === 'twoNd') {
1565
- await setNewDay(false);
1566
- } else if (myConfig.config[j].startDay === 'fixDay') {
1567
- adapter.log.info(`set Day (fixDay): ${myConfig.config[j].objectName}`);
1568
- curNextFixDay(myConfig.config[j].sprinkleID, false);
1569
- }
1570
-
1571
- adapter.setStateAsync(objPfad + '.triggerPoint',
1572
- '-',
1573
- true
1574
- ).catch((e) => adapter.log.warn(`${objectName}.triggerPoint fixDay setState ${e}`));
1575
- break;
1576
-
1577
- case 'calculation':
1578
-
1579
- const _actualSoilMoisture = await adapter.getStateAsync(objPfad + '.actualSoilMoisture').catch((e) => adapter.log.warn(e));
1580
- if (_actualSoilMoisture) {
1581
- if (await _actualSoilMoisture && typeof _actualSoilMoisture.val !== 'number' || _actualSoilMoisture.val === 0) {
1582
- adapter.setStateAsync(objPfad + '.actualSoilMoisture',
1583
- myConfig.config[j].soilMoisture.pct,
1584
- true
1585
- ).catch((e) => adapter.log.warn(e));
1586
- } else {
1587
- // num Wert der Bodenfeuchte berechnen und in der config speichern, wenn Wert zwischen 0 und max liegt
1588
- if ((0 < _actualSoilMoisture.val) && (_actualSoilMoisture.val <= (myConfig.config[j]).soilMoisture.maxRain*100/myConfig.config[j].soilMoisture.maxIrrigation)) {
1589
- myConfig.config[j].soilMoisture.val = _actualSoilMoisture.val * myConfig.config[j].soilMoisture.maxIrrigation / 100;
1590
- myConfig.config[j].soilMoisture.pct = _actualSoilMoisture.val;
1591
- } else {
1592
- // Wert aus config übernehmen
1593
- adapter.setStateAsync(objPfad + '.actualSoilMoisture',
1594
- myConfig.config[j].soilMoisture.pct,
1595
- true
1596
- ).catch((e) => adapter.log.warn(e));
1597
- }
1598
- }
1599
- }
1600
-
1601
- adapter.setStateAsync(objPfad + '.triggerPoint',
1602
- (myConfig.config[j].soilMoisture.pctTriggerIrrigation).toString(),
1603
- true
1604
- ).catch((e) => adapter.log.warn(e));
1605
- break;
1606
-
1607
- }
1608
-
1609
- } catch (e) {
1610
- adapter.log.warn(`sprinkleControl cannot created ... Please check your sprinkleControl config: ${e}`);
1611
- }
1612
- } else {
1613
- adapter.log.warn('sprinkleControl cannot created ... Please check in your config the sprinkle Name')
1614
- }
1615
- }
1616
-
1617
- // ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ //
1618
- // +++++ Objekte löschen +++++ //
1619
- // ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ //
1620
- for(const i in ObjSprinkle) {
1621
-
1622
- const resID = ObjSprinkle[i]._id;
1623
- const objectID = resID.split('.');
1624
- const resultID = objectID[3];
1625
-
1626
- const resultName = result.map(({ sprinkleName }) => ({ sprinkleName }));
1627
- const fullRes = [];
1628
-
1629
- for(const i in resultName) {
1630
- if (resultName.hasOwnProperty(i)) {
1631
- const res = resultName[i].sprinkleName.replace(/[.;, ]/g, '_');
1632
- fullRes.push(res);
1633
- }
1634
- }
1635
-
1636
- if (fullRes.indexOf(resultID) === -1) {
1637
- try {
1638
- // object deleted
1639
- Promise.all([
1640
- adapter.delObjectAsync(resID + '.actualSoilMoisture'), // "sprinklecontrol.0.sprinkle.???.actualSoilMoisture"
1641
- adapter.delObjectAsync(resID + '.triggerPoint'), // "sprinklecontrol.0.sprinkle.???.triggerPoint"
1642
- adapter.delObjectAsync(resID + '.sprinklerState'), // "sprinklecontrol.0.sprinkle.???.sprinklerState"
1643
- adapter.delObjectAsync(resID + '.runningTime'), // "sprinklecontrol.0.sprinkle.???.runningTime"
1644
- adapter.delObjectAsync(resID + '.countdown'), // "sprinklecontrol.0.sprinkle.???.countdown"
1645
- adapter.delObjectAsync(resID + '.autoOn'), // "sprinklecontrol.0.sprinkle.???.autoOn"
1646
- adapter.delObjectAsync(resID + '.history.lastOn'), // "sprinklecontrol.0.sprinkle.???..history.lastOn"
1647
- adapter.delObjectAsync(resID + '.history.lastConsumed'), // "sprinklecontrol.0.sprinkle.???..history.lastConsumed"
1648
- adapter.delObjectAsync(resID + '.history.lastRunningTime'), // "sprinklecontrol.0.sprinkle.???.history.lastRunningTime"
1649
- adapter.delObjectAsync(resID + '.history.curCalWeekConsumed'), // "sprinklecontrol.0.sprinkle.???.history.curCalWeekConsumed"
1650
- adapter.delObjectAsync(resID + '.history.lastCalWeekConsumed'), // "sprinklecontrol.0.sprinkle.???.history.lastCalWeekConsumed"
1651
- adapter.delObjectAsync(resID + '.history.curCalWeekRunningTime'), // "sprinklecontrol.0.sprinkle.???.history.curCalWeekRunningTime"
1652
- adapter.delObjectAsync(resID + '.history.lastCalWeekRunningTime'), // "sprinklecontrol.0.sprinkle.???.history.lastCalWeekRunningTime"
1653
- // History - Objekt(Ordner.history) löschen
1654
- await adapter.delObjectAsync(resID + '.history'),
1655
- // Objekt(Ordner) löschen
1656
- await adapter.delObjectAsync(resID)
1657
- ]).then((resultID)=>{
1658
- adapter.log.info(`sprinkleControl [${resultID}] was deleted`);
1659
- })
1660
- } catch (e) {
1661
- adapter.log.warn(e);
1662
- }
1663
- }
1664
- }
1665
- }
1666
- }
1667
- ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
1668
-
1669
- /**
1670
- *
1671
- * @param {number} ms => Waiting time in ms
1672
- * @return {Promise<unknown>}
1673
- */
1674
- /*
1675
- async function sleep(ms) {
1676
- return new Promise(async (resolve) => {
1677
- // @ts-ignore
1678
- timerSleep = setTimeout(async () => resolve(), ms);
1679
- });
1680
- }
1681
- */
1682
- ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
1683
- /**
1684
- *
1685
- * @param {ioBroker.Adapter} adapter
1686
- */
1687
- function main(adapter) {
1688
-
1689
- /* The adapters' config (in the instance object everything under the attribute "native") is accessible via
1690
- * adapter.config:
1691
- * => Auf die Adapterkonfiguration (im Instanz objekt alles unter dem Attribut "native") kann zugegriffen werden über
1692
- adapter.config:
1693
- */
1694
- adapter.log.debug(`adapter.config.events: ${JSON.stringify(adapter.config.events)}`);
1695
- /**
1696
- * The adapters' config (in the instance object everything under the attribute "native") is accessible via adapter.config:
1697
- * => Auf die Adapterkonfiguration (im Instanz objekt alles unter dem Attribut "native") kann zugegriffen werden über adapter.config:
1698
- * @param {any} err
1699
- * @param {any} obj
1700
- */
1701
- adapter.getForeignObject('system.config', (err, obj) => {
1702
- if (obj) {
1703
- checkStates();
1704
- }
1705
- });
1706
- createSprinklers().catch((e) => adapter.log.warn(e));
1707
- GetSystemData().catch((e) => adapter.log.warn(e));
1708
- sendMessageText.initConfigMessage(adapter);
1709
- evaporation.initEvaporation(adapter); // init evaporation
1710
- checkActualStates().catch((e) => adapter.log.warn(e));
1711
- sunPos();
1712
- timer = setTimeout(() => {
1713
- startTimeSprinkle();
1714
- addStartTimeSprinkle()
1715
- }, 2000);
1716
-
1717
- /*
1718
- * in this template all states changes inside the adapters namespace are subscribed
1719
- * => In dieser Vorlage werden alle Statusänderungen im Namensraum des Adapters abonniert
1720
- * adapter.subscribeStates('*');
1721
- */
1722
-
1723
- //
1724
- adapter.subscribeStates('control.*');
1725
-
1726
- //adapter.subscribeStates('info.Elevation');
1727
- //adapter.subscribeStates('info.Azimut');
1728
-
1729
- // Request a notification from a third-party adapter => Fordern Sie eine Benachrichtigung von einem Drittanbieter-Adapter an
1730
- if (adapter.config.publicHolidays === true && (adapter.config.publicHolInstance + '.heute.*')) {
1731
- adapter.subscribeForeignStates(adapter.config.publicHolInstance + '.heute.*');
1732
- }
1733
- if (adapter.config.publicHolidays === true && (adapter.config.publicHolInstance + '.morgen.*')) {
1734
- adapter.subscribeForeignStates(adapter.config.publicHolInstance + '.morgen.*');
1735
- }
1736
- if (adapter.config.weatherForecast === true) {
1737
- if (adapter.config.weatherForecastService === 'ownDataPoint') {
1738
- weatherForecastTodayPfadStr = adapter.config.pathRainForecast;
1739
- adapter.subscribeForeignStates(weatherForecastTodayPfadStr);
1740
- } else if (adapter.config.weatherForecastService === 'dasWetter' && adapter.config.weatherForInstance) {
1741
- weatherForecastTodayPfadStr = adapter.config.weatherForInstance + '.NextDaysDetailed.Location_1.Day_1.rain_value';
1742
- adapter.subscribeForeignStates(weatherForecastTodayPfadStr);
1743
- adapter.subscribeForeignStates(adapter.config.weatherForInstance + '.NextDaysDetailed.Location_1.Day_2.rain_value');
1744
- } else {
1745
- adapter.log.warn('There is no valid data record stored in the weather forecast, please correct it!')
1746
- }
1747
- }
1748
- if (adapter.config.actualValueLevel !== '') {
1749
- adapter.subscribeForeignStates(adapter.config.actualValueLevel);
1750
- } else {
1751
- adapter.setState('info.cisternState', { val: 'The level sensor of the water cistern is not specified', ack: true });
1752
- }
1753
- }
1754
-
1755
- ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
1756
-
1757
- // ++++++++++++++++++ start option of Adapter ++++++++++++++++++++++
1758
- // If started as allInOne/compact mode => return function to create instance
1759
- if (module && module.parent) {
1760
- module.exports = startAdapter;
1761
- } else {
1762
- // or start the instance directly
1763
- startAdapter();
1764
- }
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
+ }