iobroker.sprinklecontrol 1.0.0 → 1.0.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/lib/myConfig.js CHANGED
@@ -4,8 +4,7 @@
4
4
  /*
5
5
  info: log aufbau myConfig.js: #1.*
6
6
  */
7
- const trend = require('./tools').trend; // tools => laden von Hilfsfunktionen
8
- const formatTime = require('./tools').formatTime; // tools => laden von Hilfsfunktionen
7
+ const tools = require('./tools').tools; // tools => laden von Hilfsfunktionen
9
8
 
10
9
  /**
11
10
  * The adapter instance
@@ -39,27 +38,44 @@ const myConfig = {
39
38
  const objectName = (res.sprinkleName !== '') ? res.sprinkleName.replace(/[.;, ]/g, '_') : res.name.replace(/[.;, ]/g, '_');
40
39
 
41
40
  const newEntry = {
42
- enabled: res.enabled || false, // Schaltzustand des Ventils
41
+ // Schaltzustand des Ventils
42
+ enabled: res.enabled || false,
43
43
  booster: res.booster,
44
44
  autoOn: true,
45
- autoOnID: `${ adapter.namespace }.sprinkle.${ objectName }.autoOn`, // sprinklecontrol.0.sprinkle.Rasenumrandung.autoOnID
46
- extBreak: false, // extern break single (unterbrechung eines Kanals)
47
- extBreakID: `${ adapter.namespace }.sprinkle.${ objectName }.extBreak`, // sprinklecontrol.0.sprinkle.Rasenumrandung.breakID
48
- objectName: objectName, // z.B. Rasenumrandung
49
- objectID: `${ adapter.namespace }.sprinkle.${ objectName }.runningTime`, // sprinklecontrol.0.sprinkle.Rasenumrandung.runningTime
50
- idState: res.name, // "hm-rpc.0.MEQ1234567.3.STATE"
51
- triggerSM: res.triggerSM, // Sensor für die Bodenfeuchte
52
- updateStateTimerID: null, // Timer wird gelöscht wenn Rückmeldung erfolgte
53
- sprinkleID: myConfig.config.length, // Array[0...]
54
- wateringTime: parseInt(res.wateringTime), // ...min
55
- wateringAdd: parseInt(res.wateringAdd), // 0 ... 200%
56
- wateringIntervalOff: ((60 * parseInt(res.wateringIntervalOff)) || 0), // 5,10,15min Ausschaltzeit
57
- wateringIntervalOn: ((60 * parseInt(res.wateringIntervalOn)) || 0), // 5,10,15min Einschaltzeit
58
- addWateringTime: (parseInt(res.addWateringTime) || 0), // ...min Zusatzbewässerung bei hohen Temperaturen
59
- pipeFlow: parseInt(res.pipeFlow), // Wasserverbrauch des sprinkler-Kreises
60
- methodControlSM: res.methodControlSM, // Art der Kontrolle der Bodenfeuchte ('calculation'; 'bistable'; 'analog'; fixDay)
61
- inGreenhouse: res.inGreenhouse || false // keine Wettervorhersage verwenden (Gewächshaus)
45
+ // sprinklecontrol.0.sprinkle.Rasenumrandung.autoOnID
46
+ autoOnID: `${ adapter.namespace }.sprinkle.${ objectName }.autoOn`,
47
+ // extern break single (unterbrechung eines Kanals)
48
+ extBreak: false,
49
+ // sprinklecontrol.0.sprinkle.Rasenumrandung.breakID
50
+ extBreakID: `${ adapter.namespace }.sprinkle.${ objectName }.extBreak`,
51
+ // z.B. Rasenumrandung
52
+ objectName: objectName,
53
+ // sprinklecontrol.0.sprinkle.Rasenumrandung.runningTime
54
+ objectID: `${ adapter.namespace }.sprinkle.${ objectName }.runningTime`,
55
+ // Sensor für die Bodenfeuchte
56
+ triggerSM: res.triggerSM,
57
+ // Timer wird gelöscht wenn Rückmeldung erfolgte
58
+ updateStateTimerID: null,
59
+ // Array[0...]
60
+ sprinkleID: myConfig.config.length,
61
+ // ...min
62
+ wateringTime: parseInt(res.wateringTime),
63
+ // 0 ... 200%
64
+ wateringAdd: parseInt(res.wateringAdd),
65
+ // 5,10,15min Ausschaltzeit
66
+ wateringIntervalOff: ((60 * parseInt(res.wateringIntervalOff)) || 0),
67
+ // 5,10,15min Einschaltzeit
68
+ wateringIntervalOn: ((60 * parseInt(res.wateringIntervalOn)) || 0),
69
+ // ...min Zusatzbewässerung bei hohen Temperaturen
70
+ addWateringTime: (parseInt(res.addWateringTime) || 0),
71
+ // Wasserverbrauch des sprinkler-Kreises
72
+ pipeFlow: parseInt(res.pipeFlow),
73
+ // Art der Kontrolle der Bodenfeuchte ('calculation'; 'bistable'; 'analog'; fixDay)
74
+ methodControlSM: res.methodControlSM,
75
+ // keine Wettervorhersage verwenden (Gewächshaus)
76
+ inGreenhouse: res.inGreenhouse || false
62
77
  };
78
+
63
79
  switch (res.methodControlSM) {
64
80
  case 'calculation': {
65
81
  newEntry.calculation = {
@@ -115,6 +131,13 @@ const myConfig = {
115
131
  default: {adapter.log.error(`No watering type was selected in the "${objectName}" watering circuit.`);}
116
132
  }
117
133
 
134
+ // abonnieren der Statusänderungen des Objekts (reagiert auf Änderung des 'Ventils' der einzelnen Bewässerungskreise zur Fehlerkontrolle bzw. Verbrauchsermittlung)
135
+ newEntry.control = await tools.idStateControl(adapter, res.name);
136
+
137
+ /* Abonnieren der Statusänderungen des Objekts (reagieren auf Änderung des 'Ventils'
138
+ der einzelnen Bewässerungskreise zur Fehlerkontrolle bzw. Verbrauchsermittlung) */
139
+ adapter.subscribeForeignStates(newEntry.control.idACK);
140
+
118
141
  // @ts-ignore
119
142
  myConfig.config.push(newEntry);
120
143
 
@@ -122,8 +145,7 @@ const myConfig = {
122
145
  // Report a change in the status of the trigger IDs (.runningTime; .name) => Melden einer Änderung des Status der Trigger-IDs
123
146
  adapter.subscribeStates(newEntry.objectID); // abonnieren der Statusänderungen des Objekts (reagieren auf 'runningTime' der einzelnen Bewässerungskreise)
124
147
  adapter.subscribeStates(newEntry.autoOnID); // abonnieren der Statusänderungen des Objekts (reagieren auf 'autoOn' der einzelnen Bewässerungskreise)
125
- adapter.subscribeStates(newEntry.extBreakID); // abonnieren der Statusänderungen des Objekts (reagieren auf 'autoOn' der einzelnen Bewässerungskreise)
126
- adapter.subscribeForeignStates(newEntry.idState); // abonnieren der Statusänderungen des Objekts (reagiert auf Änderung des 'Ventils' der einzelnen Bewässerungskreise zur Fehlerkontrolle bzw. Verbrauchsermittlung)
148
+ adapter.subscribeStates(newEntry.extBreakID); // abonnieren der Statusänderungen des Objekts (reagieren auf 'autoOn' der einzelnen Bewässerungskreise)
127
149
  }
128
150
  adapter.log.debug(`Config ${objectName} created (${newEntry.sprinkleID}) - ${JSON.stringify(myConfig.config[newEntry.sprinkleID])}`);
129
151
  }
@@ -232,7 +254,7 @@ const myConfig = {
232
254
  myVal = newVal;
233
255
  }
234
256
 
235
- myConfig.config[mySprinkleID].analog.pct = Math.round(10 * trend(myConfig.config[mySprinkleID].analog.analogZPct, myConfig.config[mySprinkleID].analog.analogOHPct, 0, 100, myVal)) / 10;
257
+ myConfig.config[mySprinkleID].analog.pct = Math.round(10 * tools.trend(myConfig.config[mySprinkleID].analog.analogZPct, myConfig.config[mySprinkleID].analog.analogOHPct, 0, 100, myVal)) / 10;
236
258
  adapter.setState(`sprinkle.${ [myConfig.config[mySprinkleID].objectName] }.actualSoilMoisture`, {
237
259
  val: myConfig.config[mySprinkleID].analog.pct,
238
260
  ack: true
@@ -269,7 +291,7 @@ const myConfig = {
269
291
  if (myConfig.config[mySprinkleID].fixDay.startDay === 'threeRd' || // Next Start in 3 Tagen
270
292
  myConfig.config[mySprinkleID].fixDay.startDay === 'twoNd') { // Next Start in 2 Tagen
271
293
  try {
272
- const today = await formatTime().day;
294
+ const today = await tools.formatTime().day;
273
295
  const id = `${adapter.namespace}.sprinkle.${myConfig.config[mySprinkleID].objectName}.actualSoilMoisture`;
274
296
  let curDay, nextDay;
275
297
  /** Wert von actualSoilMoisture auslesen */
@@ -24,7 +24,7 @@ const sendMessageText = {
24
24
  switch (adapter.config.notificationsType) {
25
25
  case 'Telegram':
26
26
  ObjMessage = {
27
- enabled: adapter.config.notificationEnabled || false,
27
+ enabled: adapter.config.notificationsType !== 'noNotification' || false,
28
28
  notificationsType: adapter.config.notificationsType,
29
29
  type: 'message',
30
30
  instance: adapter.config.telegramInstance,
@@ -38,7 +38,7 @@ const sendMessageText = {
38
38
 
39
39
  case 'E-Mail':
40
40
  ObjMessage = {
41
- enabled: adapter.config.notificationEnabled || false,
41
+ enabled: adapter.config.notificationsType !== 'noNotification' || false,
42
42
  notificationsType: adapter.config.notificationsType,
43
43
  type: 'message',
44
44
  instance: adapter.config.emailInstance,
@@ -52,7 +52,7 @@ const sendMessageText = {
52
52
 
53
53
  case 'Pushover':
54
54
  ObjMessage = {
55
- enabled: adapter.config.notificationEnabled || false,
55
+ enabled: adapter.config.notificationsType !== 'noNotification' || false,
56
56
  notificationsType: adapter.config.notificationsType,
57
57
  type: 'message',
58
58
  sound: adapter.config.pushoverSound,
@@ -67,7 +67,7 @@ const sendMessageText = {
67
67
 
68
68
  case 'WhatsApp':
69
69
  ObjMessage = {
70
- enabled: adapter.config.notificationEnabled || false,
70
+ enabled: adapter.config.notificationsType !== 'noNotification' || false,
71
71
  notificationsType: adapter.config.notificationsType,
72
72
  type: 'message',
73
73
  instance: adapter.config.whatsappInstance,
@@ -189,7 +189,7 @@ const sendMessageText = {
189
189
  * @returns {boolean}
190
190
  */
191
191
  onlySendError () {
192
- return (adapter.config.notificationEnabled && adapter.config.notificationsType && ObjMessage.onlyError);
192
+ return (adapter.config.notificationsType !== 'noNotification' && adapter.config.notificationsType && ObjMessage.onlyError);
193
193
  }
194
194
 
195
195
  };
package/lib/tools.js CHANGED
@@ -1,211 +1,253 @@
1
1
  'use strict';
2
2
 
3
- /**
4
- * func addTime (02:12:24 + 00:15) || (807) => 02:12:39
5
- *
6
- * @param time1 {string|number} z.B. 02:12:24 || 807 => 02:12:39
7
- * @param time2 {string|number|undefined} z.B. 02:12:24 || 807 => 02:12:39 || undef.
8
- * @returns {string} z.B. mm:ss || hh:mm:ss
9
- */
10
- function addTime(time1, time2){
11
- const wert = string2seconds(time1) + string2seconds(time2);
12
- return seconds2string(wert);
13
-
14
- // private functions
15
- function seconds2string(n){
16
- n = Math.abs(n);
17
- const h = Math.trunc(n / 3600);
18
- const m = Math.trunc((n / 60 ) % 60);
19
- const sec = Math.trunc(n % 60);
20
- return (h === 0)?(`${frmt(m)}:${frmt(sec)}`):(`${frmt(h)}:${m}:${frmt(sec)}`);
21
- } // end function seconds2string
22
-
23
- function string2seconds(n) {
24
- if(!n || (n === '--:--')) return 0;
25
- if(Number.isInteger(n)) return n;
26
- const tmp = n.split(':').reverse();
27
- if(!tmp.length) tmp[0] = 0; // Sekunden
28
- if(tmp.length < 2) tmp[1] = 0; // Minuten
29
- if(tmp.length < 3) tmp[2] = 0; // Stunden
30
- while(tmp[0] > 59) {
31
- tmp[0] -= 60;
32
- ++tmp[1];
3
+ const tools = {
4
+ /**
5
+ * laterThanTime Funktion
6
+ *
7
+ * @param {string} time - Zeit im Format "HH:MM"
8
+ * @returns {boolean} true, wenn die angegebene Zeit noch in der Zukunft liegt, andernfalls false die Zeitliegt in der Vergangenheit
9
+ */
10
+ laterThanTime: function (time) {
11
+ const now = new Date();
12
+ const [hours, minutes] = time.split(':').map(Number);
13
+ const targetTime = new Date();
14
+ targetTime.setHours(hours, minutes, 0, 0);
15
+ return now < targetTime;
16
+ },
17
+
18
+ /**
19
+ * TREND - Zwischenwert ermitteln
20
+ *
21
+ * @param {number} a1 - Istwert a1 der Zeitachse
22
+ * @param {number} a2 - Istwert a2 der Zeitachse
23
+ * @param {number} b1 - Sollwert b1 der Wertachse
24
+ * @param {number} b2 - Sollwert b2 der Wertachse
25
+ * @param {number} sollA3 - Sollwert soll_A3 der Zeitachse
26
+ * @returns {number} Zwischenwert
27
+ */
28
+ trend: function (a1,a2,b1,b2,sollA3) {
29
+ return (sollA3-a1)*(b2-b1)/(a2-a1)+b1;
30
+ },
31
+
32
+ // Signature of the callback
33
+ // type CallBackFind<T> = (
34
+ // value: T,
35
+ // index?: number,
36
+ // collection?: T[]
37
+ // ) => Promise<boolean>;
38
+
39
+ /**
40
+ * Async Find function
41
+ *
42
+ * You can use as follows
43
+ * const array = [1, 2, 3, 4];
44
+ * const output = await findAsync<number>(array, async (i) => {
45
+ * return Promise.resolve(i === 2);
46
+ * });
47
+ *
48
+ * @template T
49
+ * @param {T[]} elements
50
+ * @param {any} cb
51
+ * @returns {Promise<T | undefined>}
52
+ */
53
+ findAsync: async function ( elements, cb) {
54
+ for (const [index, element] of elements.entries()) {
55
+ if (await cb(element, index, elements)) {
56
+ return element;
57
+ }
33
58
  }
34
- while(tmp[1] > 59) {
35
- tmp[1] -= 60;
36
- ++tmp[2];
59
+
60
+ return undefined;
61
+ },
62
+
63
+ /**
64
+ * ID Homematic
65
+ * Hiermit kann die ID-Adresse State, ON_TIME, WORKING, PROCESS eines Homematic-Schaltaktors
66
+ * ermittelt werden, wenn die ID des Geräts bekannt ist.
67
+ *
68
+ * @param {object} adapter - Der ioBroker-Adapter, der die Funktion aufruft
69
+ * @param {string} id - ID des Geräts
70
+ * @returns {Promise} Promise, die die ID-Adresse zurückgibt
71
+ */
72
+ idStateControl: async function (adapter, id) {
73
+ // "hm-rpc.0.MEQ1234567.3.STATE" => "hm-rpc.0.MEQ1234567.3"
74
+ const pfad = id.substring(0, id.lastIndexOf('.'));
75
+
76
+ switch (adapter.config.switchingBehavior) {
77
+
78
+ case "standard": {
79
+ // standard => "hm-rpc.0.MEQ1234567.3.STATE"
80
+ return {
81
+ idState: id,
82
+ idON_TIME: null,
83
+ idACK: id,
84
+ maker: "standard"
85
+ };
86
+ }
87
+
88
+ case "homematic": {
89
+ try {
90
+ const _state = await adapter.getForeignStateAsync(`${pfad}.STATE`);
91
+ const _ON_TIME = await adapter.getForeignStateAsync(`${pfad}.ON_TIME`);
92
+ const _WORKING = await adapter.getForeignStateAsync(`${pfad}.WORKING`);
93
+ const _PROCESS = await adapter.getForeignStateAsync(`${pfad}.PROCESS`);
94
+ if (typeof _state?.val !== 'boolean') throw new Error(`idStateControl: State ${pfad}.STATE nicht gefunden`);
95
+
96
+ if (_WORKING) {
97
+ return {
98
+ idState: _state ? `${pfad}.STATE` : null,
99
+ idON_TIME: _ON_TIME ? `${pfad}.ON_TIME` : null,
100
+ idACK: _WORKING ? `${pfad}.WORKING` : null,
101
+ maker: 'HM'
102
+ };
103
+ } else if (_PROCESS) {
104
+ return {
105
+ idState: _state ? `${pfad}.STATE` : null,
106
+ idON_TIME: _ON_TIME ? `${pfad}.ON_TIME` : null,
107
+ idACK: _PROCESS ? `${pfad}.PROCESS` : null,
108
+ maker: 'HmIP'
109
+ };
110
+ } else {
111
+ return {
112
+ idState: _state ? `${pfad}.STATE` : null,
113
+ idON_TIME: null,
114
+ idACK: _state ? `${pfad}.STATE` : null,
115
+ maker: 'standard'
116
+ };
117
+ }
118
+ } catch (error) {
119
+ adapter.log.error(`idStateControl: Fehler bei der Ermittlung der ID-Adresse für ${id}: ${error.message}`);
120
+ return {
121
+ idState: id,
122
+ idON_TIME: null,
123
+ idACK: id,
124
+ maker: 'unknown'
125
+ };
126
+ }
127
+ }
128
+
129
+ case "noResponse": {
130
+ // standard => "hm-rpc.0.MEQ1234567.3.STATE"
131
+ return {
132
+ idState: id,
133
+ idON_TIME: null,
134
+ idACK: null,
135
+ maker: "noResponse"
136
+ };
137
+ }
138
+
139
+ default: {
140
+ adapter.log.error(`No switching behavior was selected for the "${id}" watering circuit.`);
141
+ }
37
142
  }
38
- return (tmp[2] * 3600 + tmp[1] * 60 + 1 * tmp[0]);
39
- } // string2seconds
40
-
41
- function frmt(n) {
42
- return n < 10 ? `0${ n }` : n;
43
- }
44
-
45
- } // end - function addTime
46
-
47
-
48
- /**
49
- * func Format Time
50
- * hier wird der übergebene Zeitstempel, myDate, in das angegebene Format, timeFormat, umgewandelt.
51
- * Ist myDate nicht angegeben, so wird die aktuelle Zeit verwendet.
52
- *
53
- * @param {Date=} myDate - wenn nicht angegeben; dann wird aktuelles Datum verwendet
54
- * @returns {{dayNr: number; kW: number; day: number; dayTime: string; past: boolean}}
55
- * - kW: (number) Rückgabe der KW;
56
- * - dayNr: (number) Tag des Jahres (Tagesnummer)
57
- * - day: (number) Wochentag
58
- * - dd.mm. hh:mm: (string) Rückgabe Datum und Zeit
59
- * - past: true => (boolean) Heute schon vorbei
60
- */
61
- function formatTime(myDate) { // 'kW' 'dd.mm. hh:mm'
62
- function zweiStellen (s) {
63
- while (s.toString().length < 2) {
64
- s = `0${ s }`;
143
+ },
144
+
145
+ /**
146
+ * func Format Time
147
+ * hier wird der übergebene Zeitstempel, myDate, in das angegebene Format, timeFormat, umgewandelt.
148
+ * Ist myDate nicht angegeben, so wird die aktuelle Zeit verwendet.
149
+ *
150
+ * @param {Date=} myDate - wenn nicht angegeben; dann wird aktuelles Datum verwendet
151
+ * @returns {{dayNr: number; kW: number; day: number; dayTime: string; past: boolean}}
152
+ * - kW: (number) Rückgabe der KW;
153
+ * - dayNr: (number) Tag des Jahres (Tagesnummer)
154
+ * - day: (number) Wochentag
155
+ * - dd.mm. hh:mm: (string) Rückgabe Datum und Zeit
156
+ * - past: true => (boolean) Heute schon vorbei
157
+ */
158
+ formatTime: function (myDate) { // 'kW' 'dd.mm. hh:mm'
159
+ function zweiStellen (s) {
160
+ while (s.toString().length < 2) {
161
+ s = `0${ s }`;
162
+ }
163
+ return s;
65
164
  }
66
- return s;
67
- }
68
- const d = (myDate)? new Date(myDate):new Date();
69
- const tag = zweiStellen(d.getDate());
70
- const monat = zweiStellen(d.getMonth() + 1);
71
- const stunde = zweiStellen(d.getHours());
72
- const minute = zweiStellen(d.getMinutes());
73
- const currentThursday = new Date(d.getTime() +(3-((d.getDay()+6) % 7)) * 86400000);
74
- // At the beginning or end of a year the thursday could be in another year.
75
- const yearOfThursday = currentThursday.getFullYear();
76
- // Get first Thursday of the year
77
- const firstThursday = new Date(new Date(yearOfThursday,0,4).getTime() +(3-((new Date(yearOfThursday,0,4).getDay()+6) % 7)) * 86400000);
78
-
79
- const start = new Date(d.getFullYear(), 0, 0);
80
- const diff = (+d - +start) + ((start.getTimezoneOffset() - d.getTimezoneOffset()) * 60 * 1000);
81
- const oneDay = 1000 * 60 * 60 * 24;
82
- const dayNr = Math.floor(diff / oneDay);
83
-
84
- // +1 we start with week number 1
85
- // +0.5 an easy and dirty way to round result (in combination with Math.floor)
86
- const kW = Math.floor(1 + 0.5 + (currentThursday.getTime() - firstThursday.getTime()) / 86400000/7);
87
-
88
- // Wochentag 0:So;1:Mo;...6:Sa
89
- const day = d.getDay();
90
-
91
- // 'dd.mm. hh:mm':
92
- const dayTime = `${tag}.${monat} ${stunde}:${minute}`;
93
-
94
- // true Heute schon vorbei
95
- const cd = new Date();
96
- const cStunde = zweiStellen(cd.getHours());
97
- const cMinute = zweiStellen(cd.getMinutes());
98
- const past = ((stunde > cStunde) || ((stunde === cStunde) && (minute > cMinute)));
99
-
100
- return {
101
- dayNr: dayNr,
102
- kW: kW,
103
- day: day,
104
- dayTime: dayTime,
105
- past: past
106
- };
107
- }
108
-
109
- /**
110
- * TREND - Zwischenwert ermitteln
111
- *
112
- * @param {number} a1 - Istwert a1 der Zeitachse
113
- * @param {number} a2 - Istwert a2 der Zeitachse
114
- * @param {number} b1 - Sollwert b1 der Wertachse
115
- * @param {number} b2 - Sollwert b2 der Wertachse
116
- * @param {number} sollA3 - Sollwert soll_A3 der Zeitachse
117
- * @returns {number} Zwischenwert
118
- */
119
- function trend (a1,a2,b1,b2,sollA3) {
120
- return (sollA3-a1)*(b2-b1)/(a2-a1)+b1;
121
- }
122
-
123
- // Signature of the callback
124
- // type CallBackFind<T> = (
125
- // value: T,
126
- // index?: number,
127
- // collection?: T[]
128
- // ) => Promise<boolean>;
129
-
130
- /**
131
- * Async Find function
132
- *
133
- * You can use as follows
134
- * const array = [1, 2, 3, 4];
135
- * const output = await findAsync<number>(array, async (i) => {
136
- * return Promise.resolve(i === 2);
137
- * });
138
- *
139
- * @template T
140
- * @param {T[]} elements
141
- * @param {any} cb
142
- * @returns {Promise<T | undefined>}
143
- */
144
-
145
- async function findAsync( elements, cb) {
146
- for (const [index, element] of elements.entries()) {
147
- if (await cb(element, index, elements)) {
148
- return element;
165
+ const d = (myDate)? new Date(myDate):new Date();
166
+ const tag = zweiStellen(d.getDate());
167
+ const monat = zweiStellen(d.getMonth() + 1);
168
+ const stunde = zweiStellen(d.getHours());
169
+ const minute = zweiStellen(d.getMinutes());
170
+ const currentThursday = new Date(d.getTime() +(3-((d.getDay()+6) % 7)) * 86400000);
171
+ // At the beginning or end of a year the thursday could be in another year.
172
+ const yearOfThursday = currentThursday.getFullYear();
173
+ // Get first Thursday of the year
174
+ const firstThursday = new Date(new Date(yearOfThursday,0,4).getTime() +(3-((new Date(yearOfThursday,0,4).getDay()+6) % 7)) * 86400000);
175
+
176
+ const start = new Date(d.getFullYear(), 0, 0);
177
+ const diff = (+d - +start) + ((start.getTimezoneOffset() - d.getTimezoneOffset()) * 60 * 1000);
178
+ const oneDay = 1000 * 60 * 60 * 24;
179
+ const dayNr = Math.floor(diff / oneDay);
180
+
181
+ // +1 we start with week number 1
182
+ // +0.5 an easy and dirty way to round result (in combination with Math.floor)
183
+ const kW = Math.floor(1 + 0.5 + (currentThursday.getTime() - firstThursday.getTime()) / 86400000/7);
184
+
185
+ // Wochentag 0:So;1:Mo;...6:Sa
186
+ const day = d.getDay();
187
+
188
+ // 'dd.mm. hh:mm':
189
+ const dayTime = `${tag}.${monat} ${stunde}:${minute}`;
190
+
191
+ // true Heute schon vorbei
192
+ const cd = new Date();
193
+ const cStunde = zweiStellen(cd.getHours());
194
+ const cMinute = zweiStellen(cd.getMinutes());
195
+ const past = ((stunde > cStunde) || ((stunde === cStunde) && (minute > cMinute)));
196
+
197
+ return {
198
+ dayNr: dayNr,
199
+ kW: kW,
200
+ day: day,
201
+ dayTime: dayTime,
202
+ past: past
203
+ };
204
+ },
205
+
206
+ /**
207
+ * func addTime (02:12:24 + 00:15) || (807) => 02:12:39
208
+ *
209
+ * @param time1 {string|number} z.B. 02:12:24 || 807 => 02:12:39
210
+ * @param time2 {string|number|undefined} z.B. 02:12:24 || 807 => 02:12:39 || undef.
211
+ * @returns {string} z.B. mm:ss || hh:mm:ss
212
+ */
213
+ addTime: function (time1, time2){
214
+ const wert = string2seconds(time1) + string2seconds(time2);
215
+ return seconds2string(wert);
216
+
217
+ // private functions
218
+ function seconds2string(n){
219
+ n = Math.abs(n);
220
+ const h = Math.trunc(n / 3600);
221
+ const m = Math.trunc((n / 60 ) % 60);
222
+ const sec = Math.trunc(n % 60);
223
+ return (h === 0)?(`${frmt(m)}:${frmt(sec)}`):(`${frmt(h)}:${m}:${frmt(sec)}`);
224
+ } // end function seconds2string
225
+
226
+ function string2seconds(n) {
227
+ if(!n || (n === '--:--')) return 0;
228
+ if(Number.isInteger(n)) return n;
229
+ const tmp = n.split(':').reverse();
230
+ if(!tmp.length) tmp[0] = 0; // Sekunden
231
+ if(tmp.length < 2) tmp[1] = 0; // Minuten
232
+ if(tmp.length < 3) tmp[2] = 0; // Stunden
233
+ while(tmp[0] > 59) {
234
+ tmp[0] -= 60;
235
+ ++tmp[1];
236
+ }
237
+ while(tmp[1] > 59) {
238
+ tmp[1] -= 60;
239
+ ++tmp[2];
240
+ }
241
+ return (tmp[2] * 3600 + tmp[1] * 60 + 1 * tmp[0]);
242
+ } // string2seconds
243
+
244
+ function frmt(n) {
245
+ return n < 10 ? `0${ n }` : n;
149
246
  }
150
- }
151
247
 
152
- return undefined;
153
- }
154
-
155
- // You can use as follows
156
- // const array = [1, 2, 3, 4];
157
-
158
- // const output = await findAsync<number>(array, async (i) => {
159
- // return Promise.resolve(i === 2);
160
- // });
161
-
162
-
163
- /**
164
- * sleep Funktion mit .cancel bzw .continues Token
165
- *
166
- * @param {number} ms - Zeit in Millisekunden
167
- * @param {object} cancellationToken Objekt zur ansteuerung der inneren Funktion
168
- * @returns {Promise <string>}
169
- */
170
- function sleep(ms, cancellationToken) {
171
- return new Promise((resolve) => {
172
- cancellationToken.clearEntireList = function() {
173
- clearTimeout(timeout);
174
- resolve('clearEntireList');
175
- };
176
- cancellationToken.boostKill = function() {
177
- clearTimeout(timeout);
178
- resolve('boostKill');
179
- };
180
- cancellationToken.ack = function() {
181
- clearTimeout(timeout);
182
- resolve('ack');
183
- };
184
- const timeout = setTimeout(() => {
185
- resolve('resolved');
186
- }, ms);
187
- });
248
+ } // end - function addTime
188
249
  }
189
- // async function beginTest() {
190
- // try {
191
- // const token = {};
192
- // const promise = sleep(5000, token);
193
- // await promise;
194
- // // ... test code ...
195
- // // ... return whatever;
196
- // }
197
- // catch(error) {
198
- // console.log(error.message);
199
- // // If button was clicked before the 5000 ms has expired,
200
- // // and no other error has been thrown,
201
- // // then the log will show "sleep() cancelled".
202
- // throw error; // rethrow error to keep beginTest's caller informed.
203
- // }
204
250
 
205
251
  module.exports = {
206
- addTime,
207
- formatTime,
208
- trend,
209
- findAsync,
210
- sleep
252
+ tools
211
253
  };