iobroker.device-watcher 0.2.2 → 1.0.0

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/main.js CHANGED
@@ -7,6 +7,9 @@
7
7
  const utils = require('@iobroker/adapter-core');
8
8
  const adapterName = require('./package.json').name.split('.').pop();
9
9
 
10
+ // Sentry error reporting, disable when testing code!
11
+ const enableSendSentry = true;
12
+
10
13
  class DeviceWatcher extends utils.Adapter {
11
14
 
12
15
  constructor(options) {
@@ -16,246 +19,384 @@ class DeviceWatcher extends utils.Adapter {
16
19
  useFormatDate: true,
17
20
  });
18
21
 
19
- this.on('ready', this.onReady.bind(this));
20
- //this.on('stateChange', this.onStateChange.bind(this));
21
- // this.on('objectChange', this.onObjectChange.bind(this));
22
- // this.on('message', this.onMessage.bind(this));
23
- this.on('unload', this.onUnload.bind(this));
24
-
25
22
  // arrays
26
- this.offlineDevices = [],
27
- this.linkQualityDevices = [];
28
- this.batteryPowered = [];
29
- this.batteryLowPowered = [];
30
- this.listAllDevices = [];
31
- this.blacklistArr = [];
32
- this.arrDev = [];
33
- this.adapterSelected = [];
23
+ this.offlineDevices = [];
24
+ this.linkQualityDevices = [];
25
+ this.batteryPowered = [];
26
+ this.batteryLowPowered = [];
27
+ this.listAllDevices = [];
28
+ this.blacklistArr = [];
29
+ this.arrDev = [];
30
+ this.adapterSelected = [];
34
31
 
35
32
  // counts
36
- this.offlineDevicesCount = 0;
37
- this.deviceCounter = 0;
38
- this.linkQualityCount = 0;
39
- this.batteryPoweredCount = 0;
40
- this.lowBatteryPoweredCount = 0;
33
+ this.offlineDevicesCount = 0;
34
+ this.deviceCounter = 0;
35
+ this.linkQualityCount = 0;
36
+ this.batteryPoweredCount = 0;
37
+ this.lowBatteryPoweredCount = 0;
41
38
 
42
- this.deviceReachable = '';
39
+ this.deviceReachable = '';
40
+
41
+ // Interval timer
42
+ this.refreshDataTimeout = null;
43
43
 
44
44
  // arrays of supported adapters
45
45
  this.arrApart = {
46
- alexa2: {
47
- 'Selektor':'alexa2.*.online',
48
- 'adapter':'alexa2',
49
- 'battery':'none',
50
- 'reach':'.online',
51
- 'isLowBat':'none'
46
+ alexa2: {
47
+ 'Selektor': 'alexa2.*.online',
48
+ 'adapter': 'alexa2',
49
+ 'battery': 'none',
50
+ 'reach': '.online',
51
+ 'isLowBat': 'none'
52
+ },
53
+ ble: {
54
+ 'Selektor': 'ble.*.rssi',
55
+ 'adapter': 'ble',
56
+ 'battery': '.battery',
57
+ 'reach': 'none',
58
+ 'isLowBat': 'none'
52
59
  },
53
- ble: {
54
- 'Selektor':'ble.*.rssi',
55
- 'adapter':'ble',
56
- 'battery':'.battery',
57
- 'reach':'none',
58
- 'isLowBat':'none'
60
+ deconz: {
61
+ 'Selektor': 'deconz.*.reachable',
62
+ 'adapter': 'deconz',
63
+ 'battery': '.battery',
64
+ 'reach': '.reachable',
65
+ 'isLowBat': 'none'
59
66
  },
60
- esphome: {
61
- 'Selektor':'esphome.*._online',
62
- 'adapter':'esphome',
63
- 'battery':'none',
64
- 'reach':'._online',
65
- 'isLowBat':'none',
66
- 'id':'.name'
67
+ enocean: {
68
+ 'Selektor': 'enocean.*.rssi',
69
+ 'adapter': 'enocean',
70
+ 'battery': '.BS',
71
+ 'reach': 'none',
72
+ 'isLowBat': 'none'
67
73
  },
68
- zigbee: {
69
- 'Selektor':'zigbee.*.link_quality',
70
- 'adapter':'zigbee',
71
- 'battery':'.battery',
72
- 'reach':'.available',
73
- 'isLowBat':'none'
74
+ esphome: {
75
+ 'Selektor': 'esphome.*._online',
76
+ 'adapter': 'esphome',
77
+ 'battery': 'none',
78
+ 'reach': '._online',
79
+ 'isLowBat': 'none',
80
+ 'id': '.name'
74
81
  },
75
- sonoff: {
76
- 'Selektor':'sonoff.*.Uptime',
77
- 'adapter':'sonoff',
78
- 'rssiState': '.Wifi_RSSI',
79
- 'battery':'.battery',
80
- 'reach':'.alive',
81
- 'isLowBat':'none'
82
+ fritzdect: {
83
+ 'Selektor': 'fritzdect.*.present',
84
+ 'adapter': 'fritzDect',
85
+ 'battery': '.battery',
86
+ 'reach': '.present',
87
+ 'isLowBat': '.batterylow'
82
88
  },
83
- shelly: {
84
- 'Selektor':'shelly.*.rssi',
85
- 'adapter':'shelly',
86
- 'battery':'.sensor.battery',
87
- 'reach':'.online',
88
- 'isLowBat':'none'
89
+ harmony: {
90
+ 'Selektor': 'harmony.*.hubConnected',
91
+ 'adapter': 'harmony',
92
+ 'battery': 'none',
93
+ 'reach': '.hubConnected',
94
+ 'isLowBat': 'none'
89
95
  },
90
- homematic: {
91
- 'Selektor':'hm-rpc.*.UNREACH',
92
- 'adapter':'homematic',
93
- 'rssiState':'.RSSI_DEVICE',
94
- 'battery':'.OPERATING_VOLTAGE',
95
- 'reach':'.UNREACH',
96
- 'isLowBat':'.LOW_BAT',
97
- 'isLowBat2':'.LOWBAT'
96
+ homematic: {
97
+ 'Selektor': 'hm-rpc.*.UNREACH',
98
+ 'adapter': 'homematic',
99
+ 'rssiState': '.RSSI_DEVICE',
100
+ 'battery': '.OPERATING_VOLTAGE',
101
+ 'reach': '.UNREACH',
102
+ 'isLowBat': '.LOW_BAT',
103
+ 'isLowBat2': '.LOWBAT'
98
104
  },
99
- deconz: {
100
- 'Selektor':'deconz.*.reachable',
101
- 'adapter':'deconz',
102
- 'battery':'.battery',
103
- 'reach':'.reachable',
104
- 'isLowBat':'none'
105
+ hue: {
106
+ 'Selektor': 'hue.*.reachable',
107
+ 'adapter': 'hue',
108
+ 'battery': '.battery',
109
+ 'reach': '.reachable',
110
+ 'isLowBat': 'none'
105
111
  },
106
- zwave: {
107
- 'Selektor':'zwave2.*.ready',
108
- 'adapter':'zwave',
109
- 'battery':'.Battery.level',
110
- 'reach':'.ready',
111
- 'isLowBat':'.Battery.isLow'
112
+ hueExt: {
113
+ 'Selektor': 'hue-extended.*.reachable',
114
+ 'adapter': 'hue-extended',
115
+ 'battery': '.config.battery',
116
+ 'reach': '.reachable',
117
+ 'isLowBat': 'none'
112
118
  },
113
- dect: {
114
- 'Selektor':'fritzdect.*.present',
115
- 'adapter':'fritzDect',
116
- 'battery':'.battery',
117
- 'reach':'.present',
118
- 'isLowBat':'.batterylow'
119
+ jeelink: {
120
+ 'Selektor': 'jeelink.*.lowBatt',
121
+ 'adapter': 'jeelink',
122
+ 'battery': 'none',
123
+ 'reach': 'none',
124
+ 'isLowBat': '.lowBatt'
119
125
  },
120
- hue: {
121
- 'Selektor':'hue.*.reachable',
122
- 'adapter':'hue',
123
- 'battery':'.battery',
124
- 'reach':'.reachable',
125
- 'isLowBat':'none'
126
+ mihome: {
127
+ 'Selektor': 'mihome.*.percent',
128
+ 'adapter': 'miHome',
129
+ 'battery': '.percent',
130
+ 'reach': 'none',
131
+ 'isLowBat': 'none'
126
132
  },
127
- hueExt: {
128
- 'Selektor':'hue-extended.*.reachable',
129
- 'adapter':'hue-extended',
130
- 'battery':'.config.battery',
131
- 'reach':'.reachable',
132
- 'isLowBat':'none'
133
+ mihomeGW: {
134
+ 'Selektor': 'mihome.*.connected',
135
+ 'adapter': 'miHome',
136
+ 'battery': 'none',
137
+ 'reach': '.connected',
138
+ 'isLowBat': 'none'
133
139
  },
134
- ping: {
135
- 'Selektor':'ping.*.alive',
136
- 'adapter':'ping',
137
- 'battery':'none',
138
- 'reach':'.alive',
139
- 'isLowBat':'none'
140
+ mihomeVacuum: {
141
+ 'Selektor': 'mihome-vacuum.*.connection',
142
+ 'adapter': 'mihomeVacuum',
143
+ 'rssiState': '.deviceInfo.wifi_signal',
144
+ 'battery': '.info.battery',
145
+ 'battery2': '.control.battary_life',
146
+ 'reach': '.info.connection',
147
+ 'isLowBat': 'none',
148
+ 'id': '.deviceInfo.model'
140
149
  },
141
- switchbotBle: {
142
- 'Selektor':'switchbot-ble.*.rssi',
143
- 'adapter':'switchbotBle',
144
- 'battery':'.battery',
145
- 'reach':'none',
146
- 'isLowBat':'none',
147
- 'id':'.id'
150
+ nukiExt: {
151
+ 'Selektor': 'nuki-extended.*.lastDataUpdate',
152
+ 'adapter': 'nuki-extended',
153
+ 'rssiState': 'none',
154
+ 'battery': '.batteryChargeState',
155
+ 'reach': 'none',
156
+ 'isLowBat': '.batteryCritical'
148
157
  },
149
- sonos: {
150
- 'Selektor':'sonos.*.alive',
151
- 'adapter':'sonos',
152
- 'battery':'none',
153
- 'reach':'.alive',
154
- 'isLowBat':'none'
158
+ ping: {
159
+ 'Selektor': 'ping.*.alive',
160
+ 'adapter': 'ping',
161
+ 'battery': 'none',
162
+ 'reach': '.alive',
163
+ 'isLowBat': 'none'
155
164
  },
156
- mihome: {
157
- 'Selektor':'mihome.*.percent',
158
- 'adapter':'miHome',
159
- 'battery':'.percent',
160
- 'reach':'none',
161
- 'isLowBat':'none'
165
+ shelly: {
166
+ 'Selektor': 'shelly.*.rssi',
167
+ 'adapter': 'shelly',
168
+ 'battery': '.sensor.battery',
169
+ 'reach': '.online',
170
+ 'isLowBat': 'none'
162
171
  },
163
- mihomeGW: {
164
- 'Selektor':'mihome.*.connected',
165
- 'adapter':'miHome',
166
- 'battery':'none',
167
- 'reach':'.connected',
168
- 'isLowBat':'none'
172
+ sonoff: {
173
+ 'Selektor': 'sonoff.*.Uptime',
174
+ 'adapter': 'sonoff',
175
+ 'rssiState': '.Wifi_Signal',
176
+ 'battery': '.battery',
177
+ 'reach': '.alive',
178
+ 'isLowBat': 'none'
169
179
  },
170
- nukiExt: {
171
- 'Selektor':'nuki-extended.*.batteryCritical',
172
- 'adapter':'nuki-extended',
173
- 'battery':'.batteryCharge',
174
- 'reach':'none',
175
- 'isLowBat':'.batteryCritical'
180
+ sonos: {
181
+ 'Selektor': 'sonos.*.alive',
182
+ 'adapter': 'sonos',
183
+ 'battery': 'none',
184
+ 'reach': '.alive',
185
+ 'isLowBat': 'none'
186
+ },
187
+ switchbotBle: {
188
+ 'Selektor': 'switchbot-ble.*.rssi',
189
+ 'adapter': 'switchbotBle',
190
+ 'battery': '.battery',
191
+ 'reach': 'none',
192
+ 'isLowBat': 'none',
193
+ 'id': '.id'
194
+ },
195
+ zigbee: {
196
+ 'Selektor': 'zigbee.*.link_quality',
197
+ 'adapter': 'zigbee',
198
+ 'battery': '.battery',
199
+ 'reach': '.available',
200
+ 'isLowBat': '.battery_low'
201
+ },
202
+ zwave: {
203
+ 'Selektor': 'zwave2.*.ready',
204
+ 'adapter': 'zwave',
205
+ 'battery': '.Battery.level',
206
+ 'reach': '.ready',
207
+ 'isLowBat': '.Battery.isLow'
208
+ },
209
+ test: { // Only for Dev
210
+ 'Selektor': '0_userdata.*.UNREACH',
211
+ 'adapter': 'homematic',
212
+ 'rssiState': '.RSSI_DEVICE',
213
+ 'battery': '.OPERATING_VOLTAGE',
214
+ 'reach': '.UNREACH',
215
+ 'isLowBat': '.LOW_BAT',
216
+ 'isLowBat2': '.LOWBAT'
176
217
  }
177
218
  };
219
+
220
+ this.on('ready', this.onReady.bind(this));
221
+ this.on('stateChange', this.onStateChange.bind(this));
222
+ // this.on('objectChange', this.onObjectChange.bind(this));
223
+ // this.on('message', this.onMessage.bind(this));
224
+ this.on('unload', this.onUnload.bind(this));
225
+
178
226
  }
179
227
 
180
228
  async onReady() {
181
229
  this.log.debug(`Adapter ${adapterName} was started`);
182
230
 
183
231
  try {
232
+ this.supAdapter = {
233
+ alexa2: this.config.alexa2Devices,
234
+ ble: this.config.bleDevices,
235
+ deconz: this.config.deconzDevices,
236
+ enocean: this.config.enoceanDevices,
237
+ esphome: this.config.esphomeDevices,
238
+ fritzdect: this.config.fritzdectDevices,
239
+ harmony: this.config.harmonyDevices,
240
+ homematic: this.config.homematicDevices,
241
+ hue: this.config.hueDevices,
242
+ hueExt: this.config.hueExtDevices,
243
+ jeelink: this.config.jeelinkDevices,
244
+ mihome: this.config.mihomeDevices,
245
+ mihomeGW: this.config.mihomeDevices,
246
+ mihomeVacuum: this.config.mihomeVacuumDevices,
247
+ nukiExt: this.config.nukiExtDevices,
248
+ ping: this.config.pingDevices,
249
+ shelly: this.config.shellyDevices,
250
+ sonoff: this.config.sonoffDevices,
251
+ sonos: this.config.sonosDevices,
252
+ switchbotBle: this.config.switchbotBleDevices,
253
+ zigbee: this.config.zigbeeDevices,
254
+ zwave: this.config.zwaveDevices,
255
+ test: false // Only for Dev
256
+ };
257
+
258
+ for (const [id] of Object.entries(this.arrApart)) {
259
+ if (this.supAdapter[id]) {
260
+ this.arrDev.push(this.arrApart[id]);
261
+ this.adapterSelected.push(await this.capitalize(id));
262
+ }
263
+ }
264
+
265
+ //Check if an Adapter is selected.
266
+ if (this.adapterSelected.length >= 1) {
267
+ // show list in debug log
268
+ this.log.debug(JSON.stringify(this.arrDev));
269
+
270
+ this.log.info(`Number of selected adapters: ${this.adapterSelected.length}. Loading data from: ${(this.adapterSelected).join(', ')} ...`);
271
+ } else {
272
+ this.log.warn(`No adapter selected. Please check the instance configuration!`);
273
+ return; // cancel run if no adapter is selected
274
+ }
275
+
276
+ //create Blacklist
277
+ try {
278
+ await this.createBlacklist();
279
+ } catch (error) {
280
+ this.errorReporting('[onReady - create blacklist]', error);
281
+ }
282
+
283
+ // update data now
184
284
  await this.main();
185
- if (this.config.checkSendOfflineMsg) await this.sendOfflineNotifications();
186
- if (this.config.checkSendBatteryMsg) await this.sendBatteryNotifications();
187
- await this.writeDatapoints();
188
- this.log.debug('all done, exiting');
189
- this.terminate ? this.terminate('Everything done. Going to terminate till next schedule', 11) : process.exit(0);
190
- } catch (e) {
191
- this.log.error(`Error while running Device-Watcher. Error Message: ${e}`);
285
+
286
+ // update data in interval
287
+ await this.refreshData();
288
+
289
+ } catch (error) {
290
+ this.errorReporting('[onReady]', error);
192
291
  this.terminate ? this.terminate(15) : process.exit(15);
193
292
  }
194
293
  }
195
294
 
196
- //Helpfunctions
197
- async capitalize(sentence)
198
- {
295
+ /**
296
+ * Is called if a subscribed state changes
297
+ * @param {string} id
298
+ * @param {ioBroker.State | null | undefined} state
299
+ */
300
+ async onStateChange(id, state) {
301
+ if (state) {
302
+ // The state was changed
303
+ this.log.debug(`state ${id} changed: ${state.val} (ack = ${state.ack})`);
304
+ await this.main();
305
+ } else {
306
+ // The state was deleted
307
+ this.log.debug(`state ${id} deleted`);
308
+ }
309
+ }
310
+
311
+ async refreshData() {
312
+ const nextTimeout = this.config.updateinterval * 1000;
313
+
314
+ await this.main();
315
+
316
+ // Clear existing timeout
317
+ if (this.refreshDataTimeout) {
318
+ this.log.debug('clearing old refresh timeout');
319
+ this.clearTimeout(this.refreshDataTimeout);
320
+ }
321
+
322
+ this.refreshDataTimeout = this.setTimeout(() => {
323
+ this.log.debug('Updating Data');
324
+
325
+ this.refreshDataTimeout = null;
326
+ this.refreshData();
327
+ }, nextTimeout);
328
+ }
329
+
330
+ async main() {
331
+ this.log.debug(`Function started: ${this.main.name}`);
332
+
333
+ try {
334
+
335
+ //create and fill datapoints for each adapter if selected
336
+ try {
337
+ for (const [id] of Object.entries(this.arrApart)) {
338
+ if ((this.supAdapter !== undefined) && (this.supAdapter[id])) {
339
+
340
+ if (this.config.createOwnFolder) {
341
+ await this.createDPsForEachAdapter(id);
342
+ if (this.config.createHtmlList) await this.createHtmlListDatapoints(id);
343
+ this.log.debug(`Created datapoints for ${await this.capitalize(id)}`);
344
+ await this.createDataForEachAdapter(id);
345
+ this.log.debug(`Created and filled data for each adapter`);
346
+ }
347
+ }
348
+ }
349
+ } catch (error) {
350
+ this.errorReporting('[main - create and fill datapoints for each adapter]', error);
351
+ }
352
+
353
+ // creating counts and lists of all selected adapter
354
+ try {
355
+ if (this.config.createHtmlList) await this.createHtmlListDatapoints();
356
+ await this.createDataOfAllAdapter();
357
+ this.log.debug(`Created and filled data for all adapters`);
358
+ } catch (error) {
359
+ this.errorReporting('[main - create data of all adapter]', error);
360
+ }
361
+
362
+ } catch (error) {
363
+ this.errorReporting('[main]', error);
364
+ }
365
+
366
+ this.log.debug(`Function finished: ${this.main.name}`);
367
+ } //<--End of main function
368
+
369
+
370
+ /**
371
+ * @param {string} sentence - Word which should be capitalize
372
+ **/
373
+ async capitalize(sentence) {
199
374
  //make the first letter uppercase
200
375
  return sentence && sentence[0].toUpperCase() + sentence.slice(1);
201
376
  }
202
377
 
378
+ /**
379
+ * @param {object} obj - State of datapoint
380
+ **/
203
381
  async getInitValue(obj) {
204
382
  //state can be null or undefinded
205
383
  const foreignState = await this.getForeignStateAsync(obj);
206
384
  if (foreignState) return foreignState.val;
207
385
  }
208
386
 
387
+ /**
388
+ * @param {object} obj - State of own datapoint
389
+ **/
209
390
  async getOwnInitValue(obj) {
210
391
  //state can be null or undefinded for own states
211
392
  const stateVal = await this.getStateAsync(obj);
212
393
  if (stateVal) return stateVal.val;
213
394
  }
214
395
 
215
- //Notification services
216
- async sendPushover(text) {
217
- await this.sendToAsync(this.config.instancePushover, 'send', {
218
- message: text,
219
- title: this.config.titlePushover,
220
- device: this.config.devicePushover,
221
- priority: this.config.prioPushover
222
- });
223
- }
224
-
225
- async sendTelegram(text) {
226
- await this.sendToAsync(this.config.instanceTelegram, 'send', {
227
- text: text,
228
- user: this.config.deviceTelegram,
229
- chatId: this.config.chatIdTelegram
230
- });
231
- }
232
-
233
- async sendWhatsapp(text) {
234
- await this.sendToAsync(this.config.instanceWhatsapp, 'send', {
235
- text: text,
236
- phone: this.config.phoneWhatsapp
237
- });
238
- }
239
-
240
- async sendEmail(text) {
241
- await this.sendToAsync(this.config.instanceEmail, 'send', {
242
- sendTo: this.config.sendToEmail,
243
- text: text,
244
- subject: this.config.subjectEmail
245
- });
246
- }
247
-
248
- async sendJarvis(text) {
249
- await this.setForeignStateAsync(`${this.config.instanceJarvis}.addNotification`, text);
250
-
251
- }
252
-
253
- async sendLovelace(text) {
254
- await this.setForeignStateAsync(`${this.config.instanceLovelace}.notifications.add`, text);
255
-
256
- }
257
-
258
396
  //create datapoints for each adapter
397
+ /**
398
+ * @param {object} adptName - Adaptername of devices
399
+ **/
259
400
  async createDPsForEachAdapter(adptName) {
260
401
 
261
402
  await this.setObjectNotExistsAsync(`${adptName}`, {
@@ -288,6 +429,7 @@ class DeviceWatcher extends utils.Adapter {
288
429
  },
289
430
  'native': {}
290
431
  });
432
+
291
433
  await this.setObjectNotExistsAsync(`${adptName}.offlineList`, {
292
434
  'type': 'state',
293
435
  'common': {
@@ -310,6 +452,7 @@ class DeviceWatcher extends utils.Adapter {
310
452
  },
311
453
  'native': {}
312
454
  });
455
+
313
456
  await this.setObjectNotExistsAsync(`${adptName}.listAll`, {
314
457
  'type': 'state',
315
458
  'common': {
@@ -332,6 +475,7 @@ class DeviceWatcher extends utils.Adapter {
332
475
  },
333
476
  'native': {}
334
477
  });
478
+
335
479
  await this.setObjectNotExistsAsync(`${adptName}.linkQualityList`, {
336
480
  'type': 'state',
337
481
  'common': {
@@ -354,6 +498,7 @@ class DeviceWatcher extends utils.Adapter {
354
498
  },
355
499
  'native': {}
356
500
  });
501
+
357
502
  await this.setObjectNotExistsAsync(`${adptName}.countAll`, {
358
503
  'type': 'state',
359
504
  'common': {
@@ -376,6 +521,7 @@ class DeviceWatcher extends utils.Adapter {
376
521
  },
377
522
  'native': {}
378
523
  });
524
+
379
525
  await this.setObjectNotExistsAsync(`${adptName}.batteryList`, {
380
526
  'type': 'state',
381
527
  'common': {
@@ -398,6 +544,7 @@ class DeviceWatcher extends utils.Adapter {
398
544
  },
399
545
  'native': {}
400
546
  });
547
+
401
548
  await this.setObjectNotExistsAsync(`${adptName}.lowBatteryList`, {
402
549
  'type': 'state',
403
550
  'common': {
@@ -420,6 +567,7 @@ class DeviceWatcher extends utils.Adapter {
420
567
  },
421
568
  'native': {}
422
569
  });
570
+
423
571
  await this.setObjectNotExistsAsync(`${adptName}.lowBatteryCount`, {
424
572
  'type': 'state',
425
573
  'common': {
@@ -442,6 +590,7 @@ class DeviceWatcher extends utils.Adapter {
442
590
  },
443
591
  'native': {}
444
592
  });
593
+
445
594
  await this.setObjectNotExistsAsync(`${adptName}.batteryCount`, {
446
595
  'type': 'state',
447
596
  'common': {
@@ -466,41 +615,161 @@ class DeviceWatcher extends utils.Adapter {
466
615
  });
467
616
  }
468
617
 
469
- async createData(i) {
470
- const devices = await this.getForeignStatesAsync(this.arrDev[i].Selektor);
471
- const deviceAdapterName = await this.capitalize(this.arrDev[i].adapter);
472
- const myBlacklist = this.config.tableBlacklist;
618
+ /**
619
+ * @param {object} [adptName] - Adaptername of devices
620
+ **/
621
+ async createHtmlListDatapoints(adptName) {
622
+
623
+ let dpSubFolder;
624
+ //write the datapoints in subfolders with the adaptername otherwise write the dP's in the root folder
625
+ if (adptName) {
626
+ dpSubFolder = `${adptName}.`;
627
+ } else {
628
+ dpSubFolder = '';
629
+ }
630
+
631
+ await this.setObjectNotExistsAsync(`${dpSubFolder}offlineListHTML`, {
632
+ 'type': 'state',
633
+ 'common': {
634
+ 'name': {
635
+ 'en': 'HTML List of offline devices',
636
+ 'de': 'HTML Liste der Offline-Geräte',
637
+ 'ru': 'HTML Список оффлайн устройств',
638
+ 'pt': 'HTML Lista de dispositivos off-line',
639
+ 'nl': 'HTML List van offline apparatuur',
640
+ 'fr': 'HTML Liste des dispositifs hors ligne',
641
+ 'it': 'HTML Elenco dei dispositivi offline',
642
+ 'es': 'HTML Lista de dispositivos sin conexión',
643
+ 'pl': 'HTML Lista urządzeń offline',
644
+ 'zh-cn': 'HTML 线装置清单'
645
+ },
646
+ 'type': 'string',
647
+ 'role': 'html',
648
+ 'read': true,
649
+ 'write': false,
650
+ },
651
+ 'native': {}
652
+ });
653
+
654
+ await this.setObjectNotExistsAsync(`${dpSubFolder}linkQualityListHTML`, {
655
+ 'type': 'state',
656
+ 'common': {
657
+ 'name': {
658
+ 'en': 'HTML List of devices with signal strength',
659
+ 'de': 'HTML Liste der Geräte mit Signalstärke',
660
+ 'ru': 'HTML Список устройств с силой сигнала',
661
+ 'pt': 'HTML Lista de dispositivos com força de sinal',
662
+ 'nl': 'HTML List van apparaten met signaalkracht',
663
+ 'fr': 'HTML Liste des dispositifs avec force de signal',
664
+ 'it': 'HTML Elenco dei dispositivi con forza del segnale',
665
+ 'es': 'HTML Lista de dispositivos con fuerza de señal',
666
+ 'pl': 'HTML Lista urządzeń z siłą sygnałową',
667
+ 'zh-cn': 'HTML 具有信号实力的装置清单'
668
+ },
669
+ 'type': 'string',
670
+ 'role': 'value',
671
+ 'read': true,
672
+ 'write': false,
673
+ },
674
+ 'native': {}
675
+ });
676
+
677
+ await this.setObjectNotExistsAsync(`${dpSubFolder}batteryListHTML`, {
678
+ 'type': 'state',
679
+ 'common': {
680
+ 'name': {
681
+ 'en': 'HTML List of devices with battery state',
682
+ 'de': 'HTML Liste der Geräte mit Batteriezustand',
683
+ 'ru': 'HTML Список устройств с состоянием батареи',
684
+ 'pt': 'HTML Lista de dispositivos com estado da bateria',
685
+ 'nl': 'HTML List van apparaten met batterij staat',
686
+ 'fr': 'HTML Liste des appareils avec état de batterie',
687
+ 'it': 'HTML Elenco dei dispositivi con stato della batteria',
688
+ 'es': 'HTML Lista de dispositivos con estado de batería',
689
+ 'pl': 'HTML Lista urządzeń z baterią stanową',
690
+ 'zh-cn': 'HTML 电池国装置清单'
691
+ },
692
+ 'type': 'string',
693
+ 'role': 'html',
694
+ 'read': true,
695
+ 'write': false,
696
+ },
697
+ 'native': {}
698
+ });
473
699
 
474
- /*---------- Loop for blacklist ----------*/
475
- for(const i in myBlacklist){
700
+ await this.setObjectNotExistsAsync(`${dpSubFolder}lowBatteryListHTML`, {
701
+ 'type': 'state',
702
+ 'common': {
703
+ 'name': {
704
+ 'en': 'HTML List of devices with low battery state',
705
+ 'de': 'HTML Liste der Geräte mit niedrigem Batteriezustand',
706
+ 'ru': 'HTML Список устройств с низким состоянием батареи',
707
+ 'pt': 'HTML Lista de dispositivos com baixo estado da bateria',
708
+ 'nl': 'HTML List van apparaten met lage batterij staat',
709
+ 'fr': 'HTML Liste des appareils à faible état de batterie',
710
+ 'it': 'HTML Elenco di dispositivi con stato di batteria basso',
711
+ 'es': 'HTML Lista de dispositivos con estado de batería bajo',
712
+ 'pl': 'HTML Lista urządzeń o niskim stanie baterii',
713
+ 'zh-cn': 'HTML 低电池国家装置清单'
714
+ },
715
+ 'type': 'string',
716
+ 'role': 'html',
717
+ 'read': true,
718
+ 'write': false,
719
+ },
720
+ 'native': {}
721
+ });
722
+ }
723
+
724
+ async createBlacklist() {
725
+ this.log.debug(`Function started: ${this.createBlacklist.name}`);
726
+
727
+ const myBlacklist = this.config.tableBlacklist;
728
+
729
+ for (const i in myBlacklist) {
476
730
  this.blacklistArr.push(myBlacklist[i].device);
477
- this.log.debug(`Found items on the blacklist: ${this.blacklistArr}`);
478
731
  }
732
+ this.log.info(`Found items on the blacklist: ${this.blacklistArr}`);
733
+ this.log.debug(`Function finished: ${this.createBlacklist.name}`);
734
+ }
735
+
736
+ /**
737
+ * @param {object} i - Device Object
738
+ **/
739
+ async createData(i) {
740
+ const devices = await this.getForeignStatesAsync(this.arrDev[i].Selektor);
741
+ const deviceAdapterName = await this.capitalize(this.arrDev[i].adapter);
479
742
 
480
743
  /*---------- Start of second main loop ----------*/
481
- for(const [id] of Object.entries(devices)) {
744
+ for (const [id] of Object.entries(devices)) {
482
745
  if (!this.blacklistArr.includes(id)) {
483
746
 
484
- const currDeviceString = id.slice(0, (id.lastIndexOf('.') + 1) - 1);
747
+ const currDeviceString = id.slice(0, (id.lastIndexOf('.') + 1) - 1);
485
748
  const shortCurrDeviceString = currDeviceString.slice(0, (currDeviceString.lastIndexOf('.') + 1) - 1);
486
749
 
487
- //Get device name
750
+ // Get device name
488
751
  const deviceObject = await this.getForeignObjectAsync(currDeviceString);
489
752
  const shortDeviceObject = await this.getForeignObjectAsync(shortCurrDeviceString);
490
753
  let deviceName;
491
754
 
492
755
  switch (this.arrDev[i].adapter) {
493
- case 'switchbotBle': //Get ID for Switchbot and ESPHome Devices
756
+ case 'switchbotBle': // Get ID for Switchbot and ESPHome Devices
494
757
  case 'esphome':
495
758
  deviceName = await this.getInitValue(currDeviceString + this.arrDev[i].id);
496
759
  break;
497
760
 
498
761
  case 'hue-extended':
499
- if (shortDeviceObject && typeof shortDeviceObject === 'object') {
762
+ case 'homematic':
763
+ case 'nuki-extended':
764
+ if (shortDeviceObject && typeof shortDeviceObject === 'object') {
500
765
  deviceName = shortDeviceObject.common.name;
501
766
  }
502
767
  break;
503
768
 
769
+ case 'mihomeVacuum':
770
+ deviceName = await this.getInitValue(shortCurrDeviceString + this.arrDev[i].id);
771
+ break;
772
+
504
773
  default:
505
774
  if (deviceObject && typeof deviceObject === 'object') {
506
775
  deviceName = deviceObject.common.name;
@@ -508,48 +777,71 @@ class DeviceWatcher extends utils.Adapter {
508
777
  break;
509
778
  }
510
779
 
511
- // 1. Get link quality
780
+ const deviceMainSelector = await this.getForeignStateAsync(id);
781
+
782
+ // Get battery states
783
+ const deviceBatteryState = await this.getInitValue(currDeviceString + this.arrDev[i].battery);
784
+ const shortDeviceBatteryState = await this.getInitValue(shortCurrDeviceString + this.arrDev[i].battery);
785
+ const shortDeviceBatteryState2 = await this.getInitValue(shortCurrDeviceString + this.arrDev[i].battery2);
786
+
787
+ // Get link quality
512
788
  let deviceQualityState;
513
789
  let linkQuality;
514
790
 
515
791
  switch (this.arrDev[i].adapter) {
516
- case 'homematic':
517
792
  case 'sonoff':
793
+ case 'homematic':
518
794
  deviceQualityState = await this.getForeignStateAsync(currDeviceString + this.arrDev[i].rssiState);
519
795
  break;
796
+
797
+ case 'mihomeVacuum':
798
+ deviceQualityState = await this.getForeignStateAsync(shortCurrDeviceString + this.arrDev[i].rssiState);
799
+ break;
800
+
520
801
  default:
521
802
  deviceQualityState = await this.getForeignStateAsync(id);
803
+ break;
522
804
  }
523
805
 
524
- if ((deviceQualityState) && (typeof deviceQualityState.val === 'number')){
806
+ if ((deviceQualityState) && (typeof deviceQualityState.val === 'number')) {
525
807
  if (this.config.trueState) {
526
808
  linkQuality = deviceQualityState.val;
527
809
  } else {
528
810
  if (deviceQualityState.val < 0) {
529
811
  linkQuality = Math.min(Math.max(2 * (deviceQualityState.val + 100), 0), 100) + '%';
530
812
  } else if ((deviceQualityState.val) >= 0) {
531
- linkQuality = parseFloat((100/255 * deviceQualityState.val).toFixed(0)) + '%';
813
+ linkQuality = parseFloat((100 / 255 * deviceQualityState.val).toFixed(0)) + '%';
532
814
  }
533
815
  }
534
- this.linkQualityDevices.push(
535
- {
536
- 'Device': deviceName,
537
- 'Adapter': deviceAdapterName,
538
- 'Signal strength': linkQuality
816
+ if (this.config.listOnlyBattery) {
817
+ if (deviceBatteryState || shortDeviceBatteryState) {
818
+ this.linkQualityDevices.push(
819
+ {
820
+ 'Device': deviceName,
821
+ 'Adapter': deviceAdapterName,
822
+ 'Signal strength': linkQuality
823
+ }
824
+ );
539
825
  }
540
- );
826
+ } else {
827
+ this.linkQualityDevices.push(
828
+ {
829
+ 'Device': deviceName,
830
+ 'Adapter': deviceAdapterName,
831
+ 'Signal strength': linkQuality
832
+ }
833
+ );
834
+ }
541
835
  } else {
542
- // no linkQuality available for powered devices
543
- linkQuality = ' - ';
836
+ linkQuality = ' - '; // no linkQuality available for powered devices
544
837
  }
545
838
 
546
- // 1b. Count how many devices with link Quality
839
+ // Count how many devices with link Quality
547
840
  this.linkQualityCount = this.linkQualityDevices.length;
548
841
 
549
- // 2. When was the last contact to the device?
842
+ // When was the last contact to the device?
550
843
  let lastContactString;
551
844
 
552
- const deviceMainSelector = await this.getForeignStateAsync(id);
553
845
  let deviceState = 'Online';
554
846
  if (deviceMainSelector) {
555
847
  try {
@@ -557,15 +849,15 @@ class DeviceWatcher extends utils.Adapter {
557
849
  const lastContact = Math.round((time.getTime() - deviceMainSelector.ts) / 1000 / 60);
558
850
  const lastStateChange = Math.round((time.getTime() - deviceMainSelector.lc) / 1000 / 60);
559
851
  const deviceUnreachState = await this.getInitValue(currDeviceString + this.arrDev[i].reach);
560
-
852
+ const shortDeviceUnreachState = await this.getInitValue(shortCurrDeviceString + this.arrDev[i].reach);
561
853
 
562
854
  const getLastContact = async () => {
563
855
  lastContactString = this.formatDate(new Date((deviceMainSelector.ts)), 'hh:mm') + ' Uhr';
564
856
  if (Math.round(lastContact) > 100) {
565
- lastContactString = Math.round(lastContact/60) + ' Stunden';
857
+ lastContactString = Math.round(lastContact / 60) + ' Stunden';
566
858
  }
567
- if (Math.round(lastContact/60) > 48) {
568
- lastContactString = Math.round(lastContact/60/24) + ' Tagen';
859
+ if (Math.round(lastContact / 60) > 48) {
860
+ lastContactString = Math.round(lastContact / 60 / 24) + ' Tagen';
569
861
  }
570
862
  return lastContactString;
571
863
  };
@@ -573,47 +865,42 @@ class DeviceWatcher extends utils.Adapter {
573
865
  const getLastStateChange = async () => {
574
866
  lastContactString = this.formatDate(new Date((deviceMainSelector.lc)), 'hh:mm') + ' Uhr';
575
867
  if (Math.round(lastStateChange) > 100) {
576
- lastContactString = Math.round(lastStateChange/60) + ' Stunden';
868
+ lastContactString = Math.round(lastStateChange / 60) + ' Stunden';
577
869
  }
578
- if (Math.round(lastStateChange/60) > 48) {
579
- lastContactString = Math.round(lastStateChange/60/24) + ' Tagen';
870
+ if (Math.round(lastStateChange / 60) > 48) {
871
+ lastContactString = Math.round(lastStateChange / 60 / 24) + ' Tagen';
580
872
  }
581
873
  return lastContactString;
582
874
  };
583
875
 
584
- // 2b. wenn seit X Minuten kein Kontakt mehr besteht, nimm Gerät in Liste auf
585
- //Rechne auf Tage um, wenn mehr als 48 Stunden seit letztem Kontakt vergangen sind
586
- //lastContactString = Math.round(lastContact) + ' Minuten';
587
- switch (this.arrDev[i].adapter) {
588
- case 'ping':
589
- //State changed
590
- if (!deviceUnreachState) {
591
- await getLastStateChange();
592
- } else {
593
- await getLastContact();
594
- }
876
+ // If there is no contact since user sets minutes add device in offline list
877
+ // calculate to days after 48 hours
878
+ switch (this.arrDev[i].reach) {
879
+ case 'none':
880
+ await getLastContact();
595
881
  break;
596
882
 
597
883
  default:
598
- await getLastContact();
599
- break;
884
+ //State changed
885
+ if (this.arrDev[i].adapter == 'homematic') {
886
+ if (deviceUnreachState) {
887
+ await getLastStateChange();
888
+ } else {
889
+ await getLastContact();
890
+ }
891
+ } else {
892
+ if ((!deviceUnreachState)) {
893
+ await getLastStateChange();
894
+ } else {
895
+ await getLastContact();
896
+ }
897
+ break;
898
+ }
600
899
  }
601
900
 
602
- switch (this.arrDev[i].adapter) {
603
- case 'alexa2':
604
- if (this.config.alexa2MaxMinutes === -1) {
605
- if (!deviceUnreachState) {
606
- deviceState = 'Offline'; //set online state to offline
607
- this.offlineDevices.push(
608
- {
609
- 'Device': deviceName,
610
- 'Adapter': deviceAdapterName,
611
- 'Last contact': lastContactString
612
- }
613
- );
614
- }
615
- } else if (lastContact > this.config.alexa2MaxMinutes) {
616
- deviceState = 'Offline'; //set online state to offline
901
+ const pushOfflineDevice = async () => {
902
+ if (this.config.listOnlyBattery) { //if checked, list only battery devices
903
+ if (deviceBatteryState || shortDeviceBatteryState) {
617
904
  this.offlineDevices.push(
618
905
  {
619
906
  'Device': deviceName,
@@ -622,395 +909,304 @@ class DeviceWatcher extends utils.Adapter {
622
909
  }
623
910
  );
624
911
  }
912
+ } else {
913
+ this.offlineDevices.push( //else push all devices
914
+ {
915
+ 'Device': deviceName,
916
+ 'Adapter': deviceAdapterName,
917
+ 'Last contact': lastContactString
918
+ }
919
+ );
920
+ }
921
+ };
922
+
923
+ // await this.subscribeForeignStatesAsync(currDeviceString + this.arrDev[i].reach);
924
+
925
+ switch (this.arrDev[i].adapter) {
926
+ case 'alexa2':
927
+ if (this.config.alexa2MaxMinutes === -1) {
928
+ if (!deviceUnreachState) {
929
+ deviceState = 'Offline'; //set online state to offline
930
+ await pushOfflineDevice();
931
+ }
932
+ } else if ((lastStateChange > this.config.alexa2MaxMinutes) && (!deviceUnreachState)) {
933
+ deviceState = 'Offline'; //set online state to offline
934
+ await pushOfflineDevice();
935
+ }
625
936
  break;
626
937
  case 'ble':
627
938
  if (this.config.bleMaxMinutes === -1) {
628
939
  if (!deviceUnreachState) {
629
940
  deviceState = 'Offline'; //set online state to offline
630
- this.offlineDevices.push(
631
- {
632
- 'Device': deviceName,
633
- 'Adapter': deviceAdapterName,
634
- 'Last contact': lastContactString
635
- }
636
- );
941
+ await pushOfflineDevice();
637
942
  }
638
943
  } else if (lastContact > this.config.bleMaxMinutes) {
639
944
  deviceState = 'Offline'; //set online state to offline
640
- this.offlineDevices.push(
641
- {
642
- 'Device': deviceName,
643
- 'Adapter': deviceAdapterName,
644
- 'Last contact': lastContactString
645
- }
646
- );
945
+ await pushOfflineDevice();
647
946
  }
648
947
  break;
649
948
  case 'deconz':
650
949
  if (this.config.deconzMaxMinutes === -1) {
651
950
  if (!deviceUnreachState) {
652
951
  deviceState = 'Offline'; //set online state to offline
653
- this.offlineDevices.push(
654
- {
655
- 'Device': deviceName,
656
- 'Adapter': deviceAdapterName,
657
- 'Last contact': lastContactString
658
- }
659
- );
952
+ await pushOfflineDevice();
660
953
  }
661
- } else if (lastContact > this.config.deconzMaxMinutes) {
954
+ } else if ((lastStateChange > this.config.deconzMaxMinutes) && (!deviceUnreachState)) {
662
955
  deviceState = 'Offline'; //set online state to offline
663
- this.offlineDevices.push(
664
- {
665
- 'Device': deviceName,
666
- 'Adapter': deviceAdapterName,
667
- 'Last contact': lastContactString
668
- }
669
- );
956
+ await pushOfflineDevice();
957
+ }
958
+ break;
959
+ case 'enocean':
960
+ if (this.config.enoceanMaxMinutes === -1) {
961
+ if (!deviceUnreachState) {
962
+ deviceState = 'Offline'; //set online state to offline
963
+ await pushOfflineDevice();
964
+ }
965
+ } else if (lastContact > this.config.enoceanMaxMinutes) {
966
+ deviceState = 'Offline'; //set online state to offline
967
+ await pushOfflineDevice();
670
968
  }
671
969
  break;
672
970
  case 'esphome':
673
971
  if (this.config.esphomeMaxMinutes === -1) {
674
972
  if (!deviceUnreachState) {
675
973
  deviceState = 'Offline'; //set online state to offline
676
- this.offlineDevices.push(
677
- {
678
- 'Device': deviceName,
679
- 'Adapter': deviceAdapterName,
680
- 'Last contact': lastContactString
681
- }
682
- );
974
+ await pushOfflineDevice();
683
975
  }
684
- } else if (lastContact > this.config.esphomeMaxMinutes) {
976
+ } else if ((lastStateChange > this.config.esphomeMaxMinutes) && (!deviceUnreachState)) {
685
977
  deviceState = 'Offline'; //set online state to offline
686
- this.offlineDevices.push(
687
- {
688
- 'Device': deviceName,
689
- 'Adapter': deviceAdapterName,
690
- 'Last contact': lastContactString
691
- }
692
- );
978
+ await pushOfflineDevice();
693
979
  }
694
980
  break;
695
981
  case 'fritzDect':
696
982
  if (this.config.fritzdectMaxMinutes === -1) {
697
983
  if (!deviceUnreachState) {
698
984
  deviceState = 'Offline'; //set online state to offline
699
- this.offlineDevices.push(
700
- {
701
- 'Device': deviceName,
702
- 'Adapter': deviceAdapterName,
703
- 'Last contact': lastContactString
704
- }
705
- );
985
+ await pushOfflineDevice();
706
986
  }
707
- } else if (lastContact > this.config.fritzdectMaxMinutes) {
987
+ } else if ((lastStateChange > this.config.fritzdectMaxMinutes) && (!deviceUnreachState)) {
708
988
  deviceState = 'Offline'; //set online state to offline
709
- this.offlineDevices.push(
710
- {
711
- 'Device': deviceName,
712
- 'Adapter': deviceAdapterName,
713
- 'Last contact': lastContactString
714
- }
715
- );
989
+ await pushOfflineDevice();
990
+ }
991
+ break;
992
+ case 'harmony':
993
+ if (this.config.harmonyMaxMinutes === -1) {
994
+ if (!deviceUnreachState) {
995
+ deviceState = 'Offline'; //set online state to offline
996
+ await pushOfflineDevice();
997
+ }
998
+ } else if ((lastStateChange > this.config.harmonyMaxMinutes) && (!deviceUnreachState)) {
999
+ deviceState = 'Offline'; //set online state to offline
1000
+ await pushOfflineDevice();
716
1001
  }
717
1002
  break;
718
1003
  case 'homematic':
719
1004
  if (this.config.homematicMaxMinutes === -1) {
720
1005
  if (deviceUnreachState) {
721
1006
  deviceState = 'Offline'; //set online state to offline
722
- this.offlineDevices.push(
723
- {
724
- 'Device': deviceName,
725
- 'Adapter': deviceAdapterName,
726
- 'Last contact': lastContactString
727
- }
728
- );
1007
+ await pushOfflineDevice();
729
1008
  }
730
- } else if (lastContact > this.config.homematicMaxMinutes) {
1009
+ } else if ((lastStateChange > this.config.homematicMaxMinutes) && (deviceUnreachState)) {
731
1010
  deviceState = 'Offline'; //set online state to offline
732
- this.offlineDevices.push(
733
- {
734
- 'Device': deviceName,
735
- 'Adapter': deviceAdapterName,
736
- 'Last contact': lastContactString
737
- }
738
- );
1011
+ await pushOfflineDevice();
739
1012
  }
740
1013
  break;
741
1014
  case 'hue':
742
1015
  if (this.config.hueMaxMinutes === -1) {
743
1016
  if (!deviceUnreachState) {
744
1017
  deviceState = 'Offline'; //set online state to offline
745
- this.offlineDevices.push(
746
- {
747
- 'Device': deviceName,
748
- 'Adapter': deviceAdapterName,
749
- 'Last contact': lastContactString
750
- }
751
- );
1018
+ await pushOfflineDevice();
752
1019
  }
753
- } else if (lastContact > this.config.hueMaxMinutes) {
1020
+ } else if ((lastStateChange > this.config.hueMaxMinutes) && (!deviceUnreachState)) {
754
1021
  deviceState = 'Offline'; //set online state to offline
755
- this.offlineDevices.push(
756
- {
757
- 'Device': deviceName,
758
- 'Adapter': deviceAdapterName,
759
- 'Last contact': lastContactString
760
- }
761
- );
1022
+ await pushOfflineDevice();
762
1023
  }
763
1024
  break;
764
1025
  case 'hue-extended':
765
1026
  if (this.config.hueextMaxMinutes === -1) {
766
1027
  if (!deviceUnreachState) {
767
1028
  deviceState = 'Offline'; //set online state to offline
768
- this.offlineDevices.push(
769
- {
770
- 'Device': deviceName,
771
- 'Adapter': deviceAdapterName,
772
- 'Last contact': lastContactString
773
- }
774
- );
1029
+ await pushOfflineDevice();
775
1030
  }
776
- } else if (lastContact > this.config.hueextMaxMinutes) {
1031
+ } else if ((lastStateChange > this.config.hueextMaxMinutes) && (!deviceUnreachState)) {
777
1032
  deviceState = 'Offline'; //set online state to offline
778
- this.offlineDevices.push(
779
- {
780
- 'Device': deviceName,
781
- 'Adapter': deviceAdapterName,
782
- 'Last contact': lastContactString
783
- }
784
- );
1033
+ await pushOfflineDevice();
1034
+ }
1035
+ break;
1036
+ case 'jeelink':
1037
+ if (this.config.jeelinkMaxMinutes === -1) {
1038
+ if (!deviceUnreachState) {
1039
+ deviceState = 'Offline'; //set online state to offline
1040
+ await pushOfflineDevice();
1041
+ }
1042
+ } else if (lastContact > this.config.jeelinkMaxMinutes) {
1043
+ deviceState = 'Offline'; //set online state to offline
1044
+ await pushOfflineDevice();
785
1045
  }
786
1046
  break;
787
1047
  case 'miHome':
788
1048
  if (this.config.mihomeMaxMinutes === -1) {
789
1049
  if (!deviceUnreachState) {
790
1050
  deviceState = 'Offline'; //set online state to offline
791
- this.offlineDevices.push(
792
- {
793
- 'Device': deviceName,
794
- 'Adapter': deviceAdapterName,
795
- 'Last contact': lastContactString
796
- }
797
- );
1051
+ await pushOfflineDevice();
798
1052
  }
799
- } else if (lastContact > this.config.mihomeMaxMinutes) {
1053
+ } else if ((lastStateChange > this.config.mihomeMaxMinutes) && (!deviceUnreachState)) {
800
1054
  deviceState = 'Offline'; //set online state to offline
801
- this.offlineDevices.push(
802
- {
803
- 'Device': deviceName,
804
- 'Adapter': deviceAdapterName,
805
- 'Last contact': lastContactString
806
- }
807
- );
1055
+ await pushOfflineDevice();
1056
+ }
1057
+ break;
1058
+ case 'mihomeVacuum':
1059
+ if (this.config.mihomeVacuumMaxMinutes === -1) {
1060
+ if (!shortDeviceUnreachState) {
1061
+ deviceState = 'Offline'; //set online state to offline
1062
+ await pushOfflineDevice();
1063
+ }
1064
+ } else if ((lastStateChange > this.config.mihomeVacuumMaxMinutes) && (!shortDeviceUnreachState)) {
1065
+ deviceState = 'Offline'; //set online state to offline
1066
+ await pushOfflineDevice();
808
1067
  }
809
1068
  break;
810
1069
  case 'nuki-extended':
811
1070
  if (this.config.nukiextendMaxMinutes === -1) {
812
1071
  if (!deviceUnreachState) {
813
1072
  deviceState = 'Offline'; //set online state to offline
814
- this.offlineDevices.push(
815
- {
816
- 'Device': deviceName,
817
- 'Adapter': deviceAdapterName,
818
- 'Last contact': lastContactString
819
- }
820
- );
1073
+ await pushOfflineDevice();
821
1074
  }
822
1075
  } else if (lastContact > this.config.nukiextendMaxMinutes) {
823
1076
  deviceState = 'Offline'; //set online state to offline
824
- this.offlineDevices.push(
825
- {
826
- 'Device': deviceName,
827
- 'Adapter': deviceAdapterName,
828
- 'Last contact': lastContactString
829
- }
830
- );
1077
+ await pushOfflineDevice();
831
1078
  }
832
1079
  break;
833
1080
  case 'ping':
834
1081
  if (this.config.pingMaxMinutes === -1) {
835
1082
  if (!deviceUnreachState) {
836
1083
  deviceState = 'Offline'; //set online state to offline
837
- this.offlineDevices.push(
838
- {
839
- 'Device': deviceName,
840
- 'Adapter': deviceAdapterName,
841
- 'Last contact': lastContactString
842
- }
843
- );
1084
+ await pushOfflineDevice();
844
1085
  }
845
1086
  } else if ((lastStateChange > this.config.pingMaxMinutes) && (!deviceUnreachState)) {
846
1087
  deviceState = 'Offline'; //set online state to offline
847
- this.offlineDevices.push(
848
- {
849
- 'Device': deviceName,
850
- 'Adapter': deviceAdapterName,
851
- 'Last contact': lastContactString
852
- }
853
- );
1088
+ await pushOfflineDevice();
854
1089
  }
855
1090
  break;
856
1091
  case 'shelly':
857
1092
  if (this.config.shellyMaxMinutes === -1) {
858
1093
  if (!deviceUnreachState) {
859
1094
  deviceState = 'Offline'; //set online state to offline
860
- this.offlineDevices.push(
861
- {
862
- 'Device': deviceName,
863
- 'Adapter': deviceAdapterName,
864
- 'Last contact': lastContactString
865
- }
866
- );
1095
+ await pushOfflineDevice();
867
1096
  }
868
- } else if (lastContact > this.config.shellyMaxMinutes) {
1097
+ } else if ((lastStateChange > this.config.shellyMaxMinutes) && (!deviceUnreachState)) {
869
1098
  deviceState = 'Offline'; //set online state to offline
870
- this.offlineDevices.push(
871
- {
872
- 'Device': deviceName,
873
- 'Adapter': deviceAdapterName,
874
- 'Last contact': lastContactString
875
- }
876
- );
1099
+ await pushOfflineDevice();
877
1100
  }
878
1101
  break;
879
1102
  case 'sonoff':
880
1103
  if (this.config.sonoffMaxMinutes === -1) {
881
1104
  if (!deviceUnreachState) {
882
1105
  deviceState = 'Offline'; //set online state to offline
883
- this.offlineDevices.push(
884
- {
885
- 'Device': deviceName,
886
- 'Adapter': deviceAdapterName,
887
- 'Last contact': lastContactString
888
- }
889
- );
1106
+ await pushOfflineDevice();
890
1107
  }
891
- } else if (lastContact > this.config.sonoffMaxMinutes) {
1108
+ } else if ((lastStateChange > this.config.sonoffMaxMinutes) && (!deviceUnreachState)) {
892
1109
  deviceState = 'Offline'; //set online state to offline
893
- this.offlineDevices.push(
894
- {
895
- 'Device': deviceName,
896
- 'Adapter': deviceAdapterName,
897
- 'Last contact': lastContactString
898
- }
899
- );
1110
+ await pushOfflineDevice();
900
1111
  }
901
1112
  break;
902
1113
  case 'sonos':
903
1114
  if (this.config.sonosMaxMinutes === -1) {
904
1115
  if (!deviceUnreachState) {
905
1116
  deviceState = 'Offline'; //set online state to offline
906
- this.offlineDevices.push(
907
- {
908
- 'Device': deviceName,
909
- 'Adapter': deviceAdapterName,
910
- 'Last contact': lastContactString
911
- }
912
- );
1117
+ await pushOfflineDevice();
913
1118
  }
914
- } else if (lastContact > this.config.sonosMaxMinutes) {
1119
+ } else if ((lastStateChange > this.config.sonosMaxMinutes) && (!deviceUnreachState)) {
915
1120
  deviceState = 'Offline'; //set online state to offline
916
- this.offlineDevices.push(
917
- {
918
- 'Device': deviceName,
919
- 'Adapter': deviceAdapterName,
920
- 'Last contact': lastContactString
921
- }
922
- );
1121
+ await pushOfflineDevice();
923
1122
  }
924
1123
  break;
925
1124
  case 'switchbotBle':
926
1125
  if (this.config.switchbotMaxMinutes === -1) {
927
1126
  if (!deviceUnreachState) {
928
1127
  deviceState = 'Offline'; //set online state to offline
929
- this.offlineDevices.push(
930
- {
931
- 'Device': deviceName,
932
- 'Adapter': deviceAdapterName,
933
- 'Last contact': lastContactString
934
- }
935
- );
1128
+ await pushOfflineDevice();
936
1129
  }
937
1130
  } else if (lastContact > this.config.switchbotMaxMinutes) {
938
1131
  deviceState = 'Offline'; //set online state to offline
939
- this.offlineDevices.push(
940
- {
941
- 'Device': deviceName,
942
- 'Adapter': deviceAdapterName,
943
- 'Last contact': lastContactString
944
- }
945
- );
1132
+ await pushOfflineDevice();
946
1133
  }
947
1134
  break;
948
1135
  case 'zigbee':
949
1136
  if (this.config.zigbeeMaxMinutes === -1) {
950
1137
  if (!deviceUnreachState) {
951
1138
  deviceState = 'Offline'; //set online state to offline
952
- this.offlineDevices.push(
953
- {
954
- 'Device': deviceName,
955
- 'Adapter': deviceAdapterName,
956
- 'Last contact': lastContactString
957
- }
958
- );
1139
+ await pushOfflineDevice();
959
1140
  }
960
- } else if (lastContact > this.config.zigbeeMaxMinutes) {
1141
+ } else if ((lastStateChange > this.config.zigbeeMaxMinutes) && (!deviceUnreachState)) {
961
1142
  deviceState = 'Offline'; //set online state to offline
962
- this.offlineDevices.push(
963
- {
964
- 'Device': deviceName,
965
- 'Adapter': deviceAdapterName,
966
- 'Last contact': lastContactString
967
- }
968
- );
1143
+ await pushOfflineDevice();
969
1144
  }
970
1145
  break;
971
1146
  case 'zwave':
972
1147
  if (this.config.zwaveMaxMinutes === -1) {
973
1148
  if (!deviceUnreachState) {
974
1149
  deviceState = 'Offline'; //set online state to offline
975
- this.offlineDevices.push(
976
- {
977
- 'Device': deviceName,
978
- 'Adapter': deviceAdapterName,
979
- 'Last contact': lastContactString
980
- }
981
- );
1150
+ await pushOfflineDevice();
982
1151
  }
983
- } else if (lastContact > this.config.zwaveMaxMinutes) {
1152
+ } else if ((lastStateChange > this.config.zwaveMaxMinutes) && (!deviceUnreachState)) {
984
1153
  deviceState = 'Offline'; //set online state to offline
985
- this.offlineDevices.push(
986
- {
987
- 'Device': deviceName,
988
- 'Adapter': deviceAdapterName,
989
- 'Last contact': lastContactString
990
- }
991
- );
1154
+ await pushOfflineDevice();
992
1155
  }
993
1156
  break;
994
1157
  }
995
- } catch (e) {
996
- this.log.error(`(03) Error while getting timestate ${e}`);
1158
+ } catch (error) {
1159
+ this.errorReporting('[getLastContact]', error);
997
1160
  }
998
1161
  }
999
1162
 
1000
1163
 
1001
1164
 
1002
- // 2c. Count how many devcies are offline
1165
+ // Count how many devcies are offline
1003
1166
  this.offlineDevicesCount = this.offlineDevices.length;
1004
1167
 
1005
- // 3. Get battery states
1006
- const deviceBatteryState = await this.getInitValue(currDeviceString + this.arrDev[i].battery);
1007
- const shortDeviceBatteryState = await this.getInitValue(shortCurrDeviceString + this.arrDev[i].battery);
1168
+ // Get battery states
1008
1169
  let batteryHealth;
1170
+ const deviceLowBatState = await this.getInitValue(currDeviceString + this.arrDev[i].isLowBat);
1171
+ const deviceLowBatStateHM = await this.getInitValue(currDeviceString + this.arrDev[i].isLowBat2);
1009
1172
 
1010
- if ((!deviceBatteryState) && (!shortDeviceBatteryState)) {
1011
- batteryHealth = ' - ';
1173
+ if ((!deviceBatteryState) && (!shortDeviceBatteryState) && (!shortDeviceBatteryState2)) {
1174
+ if ((deviceLowBatState !== undefined) || (deviceLowBatState !== undefined) || (deviceLowBatStateHM !== undefined)) {
1175
+ switch (this.arrDev[i].isLowBat) {
1176
+ case 'none':
1177
+ batteryHealth = ' - ';
1178
+ break;
1179
+ default:
1180
+ if (!deviceLowBatState) {
1181
+ batteryHealth = 'ok';
1182
+ } else {
1183
+ batteryHealth = 'low';
1184
+ }
1185
+ break;
1186
+ }
1187
+ switch (this.arrDev[i].isLowBat2) {
1188
+ case 'none':
1189
+ batteryHealth = ' - ';
1190
+ break;
1191
+ default:
1192
+ if (!deviceLowBatState) {
1193
+ batteryHealth = 'ok';
1194
+ } else {
1195
+ batteryHealth = 'low';
1196
+ }
1197
+ break;
1198
+ }
1199
+ this.batteryPowered.push(
1200
+ {
1201
+ 'Device': deviceName,
1202
+ 'Adapter': deviceAdapterName,
1203
+ 'Battery': batteryHealth
1204
+ }
1205
+ );
1206
+ } else {
1207
+ batteryHealth = ' - ';
1208
+ }
1012
1209
  } else {
1013
-
1014
1210
  switch (this.arrDev[i].adapter) {
1015
1211
  case 'homematic':
1016
1212
  if (deviceBatteryState === 0) {
@@ -1039,6 +1235,27 @@ class DeviceWatcher extends utils.Adapter {
1039
1235
  );
1040
1236
  }
1041
1237
  break;
1238
+ case 'mihomeVacuum':
1239
+ if (shortDeviceBatteryState) {
1240
+ batteryHealth = shortDeviceBatteryState + '%';
1241
+ this.batteryPowered.push(
1242
+ {
1243
+ 'Device': deviceName,
1244
+ 'Adapter': deviceAdapterName,
1245
+ 'Battery': batteryHealth
1246
+ }
1247
+ );
1248
+ } else if (shortDeviceBatteryState2) {
1249
+ batteryHealth = shortDeviceBatteryState2 + '%';
1250
+ this.batteryPowered.push(
1251
+ {
1252
+ 'Device': deviceName,
1253
+ 'Adapter': deviceAdapterName,
1254
+ 'Battery': batteryHealth
1255
+ }
1256
+ );
1257
+ }
1258
+ break;
1042
1259
  default:
1043
1260
  batteryHealth = (deviceBatteryState) + '%';
1044
1261
  this.batteryPowered.push(
@@ -1051,43 +1268,51 @@ class DeviceWatcher extends utils.Adapter {
1051
1268
  }
1052
1269
  }
1053
1270
 
1054
- // 3b. Count how many devices are with battery
1271
+ // Count how many devices are with battery
1055
1272
  this.batteryPoweredCount = this.batteryPowered.length;
1056
1273
 
1057
- // 3c. Count how many devices are with low battery
1058
- const batteryWarningMin = this.config.minWarnBatterie;
1059
- const deviceLowBatState = await this.getInitValue(currDeviceString + this.arrDev[i].isLowBat);
1060
- const deviceLowBatStateHM = await this.getInitValue(currDeviceString + this.arrDev[i].isLowBat2);
1274
+ // Count how many devices are with low battery
1275
+ const batteryWarningMin = this.config.minWarnBatterie;
1061
1276
 
1277
+ // fill list with low battery devices
1278
+ switch (this.arrDev[i].adapter) {
1279
+ case 'homematic': // there are differnt low bat states between hm and hmIp devices
1280
+ if (deviceLowBatState || deviceLowBatStateHM) {
1281
+ this.batteryLowPowered.push(
1282
+ {
1283
+ 'Device': deviceName,
1284
+ 'Adapter': deviceAdapterName,
1285
+ 'Battery': batteryHealth
1286
+ }
1287
+ );
1288
+ }
1289
+ break;
1062
1290
 
1063
- if (this.arrDev[i].isLowBat === 'none') {
1064
- if (deviceBatteryState && (deviceBatteryState < batteryWarningMin)) {
1065
- this.batteryLowPowered.push(
1066
- {
1067
- 'Device': deviceName,
1068
- 'Adapter': deviceAdapterName,
1069
- 'Battery': batteryHealth
1070
- }
1071
- );
1072
- }
1073
- } else {
1074
- if (deviceLowBatState || deviceLowBatStateHM) {
1075
- this.batteryLowPowered.push(
1076
- {
1077
- 'Device': deviceName,
1078
- 'Adapter': deviceAdapterName,
1079
- 'Battery': batteryHealth
1080
- }
1081
- );
1082
- }
1291
+ default: // for all other devices with low bat states
1292
+ if (deviceLowBatState) {
1293
+ this.batteryLowPowered.push(
1294
+ {
1295
+ 'Device': deviceName,
1296
+ 'Adapter': deviceAdapterName,
1297
+ 'Battery': batteryHealth
1298
+ }
1299
+ );
1300
+ } else if (deviceBatteryState && (deviceBatteryState < batteryWarningMin)) { // if the battery state is under the set limit
1301
+ this.batteryLowPowered.push(
1302
+ {
1303
+ 'Device': deviceName,
1304
+ 'Adapter': deviceAdapterName,
1305
+ 'Battery': batteryHealth
1306
+ }
1307
+ );
1308
+ }
1083
1309
  }
1084
1310
 
1085
1311
  // 3d. Count how many devices are with low battery
1086
1312
  this.lowBatteryPoweredCount = this.batteryLowPowered.length;
1087
1313
 
1088
- // 4. Add all devices in the list
1089
- if (this.config.listOnlyBattery) {
1090
- if (deviceBatteryState !== null || shortDeviceBatteryState !== null) {
1314
+ if (this.config.listOnlyBattery) { // 4. Add only devices with battery in the list
1315
+ if (deviceBatteryState || shortDeviceBatteryState) {
1091
1316
  this.listAllDevices.push(
1092
1317
  {
1093
1318
  'Device': deviceName,
@@ -1099,7 +1324,7 @@ class DeviceWatcher extends utils.Adapter {
1099
1324
  }
1100
1325
  );
1101
1326
  }
1102
- } else if (!this.config.listOnlyBattery) {
1327
+ } else if (!this.config.listOnlyBattery) { // 4. Add all devices
1103
1328
  this.listAllDevices.push(
1104
1329
  {
1105
1330
  'Device': deviceName,
@@ -1116,129 +1341,183 @@ class DeviceWatcher extends utils.Adapter {
1116
1341
  // 4a. Count how many devices are exists
1117
1342
  this.deviceCounter = this.listAllDevices.length;
1118
1343
  }
1119
- } //<--End of second loop
1120
- }
1344
+ } // <-- end of loop
1345
+ } // <-- end of createData
1121
1346
 
1122
- async createDataForEachAdpt(adptName) {
1123
- this.log.debug(`Function started: ${this.createDataForEachAdpt.name}`);
1124
- await this.resetVars();
1125
1347
 
1126
- for (let i = 0; i < this.arrDev.length; i++) {
1348
+ /**
1349
+ * @param {string} adptName - Adapter name
1350
+ */
1351
+ async createDataForEachAdapter(adptName) {
1352
+ // create Data for each Adapter in own lists
1353
+ this.log.debug(`Function started: ${this.createDataForEachAdapter.name}`);
1127
1354
 
1128
- if (this.arrDev[i].adapter.includes(adptName)) {
1129
- await this.createData(i);
1130
- }
1355
+ try {
1356
+ await this.resetVars(); // reset the arrays and counts
1131
1357
 
1358
+ for (let i = 0; i < this.arrDev.length; i++) {
1359
+
1360
+ if (this.arrDev[i].adapter.includes(adptName)) { // list device only if selected adapter matched with device
1361
+ await this.createData(i);
1362
+ }
1363
+ }
1364
+ await this.writeDatapoints(adptName); // fill the datapoints
1365
+ } catch (error) {
1366
+ this.errorReporting('[createDataForEachAdapter]', error);
1132
1367
  }
1133
- await this.writeDatapoints(adptName);
1134
1368
 
1135
- this.log.debug(`Function finished: ${this.createDataForEachAdpt.name}`);
1136
- }
1369
+ this.log.debug(`Function finished: ${this.createDataForEachAdapter.name}`);
1370
+ } // <-- end of createDataForEachAdapter
1371
+
1372
+ async createDataOfAllAdapter() {
1373
+ // create Data of all selected adapter in one list
1374
+ this.log.debug(`Function started: ${this.createDataOfAllAdapter.name}`);
1375
+
1376
+ try {
1377
+ await this.resetVars(); // reset the arrays and counts
1137
1378
 
1138
- async createDataOfAll() {
1139
- this.log.debug(`Function started: ${this.createDataOfAll.name}`);
1140
- await this.resetVars();
1379
+ for (let i = 0; i < this.arrDev.length; i++) {
1141
1380
 
1142
- for (let i = 0; i < this.arrDev.length; i++) {
1381
+ await this.createData(i);
1143
1382
 
1144
- await this.createData(i);
1383
+ }
1145
1384
 
1385
+ if (this.config.checkSendOfflineMsg) await this.sendOfflineNotifications(); // send message if new devices are offline
1386
+ if (this.config.checkSendOfflineMsgDaily) await this.sendDailyOfflineNotifications(); // send daily overview of offline devices
1387
+ if (this.config.checkSendBatteryMsg) await this.sendBatteryNotifications(); // send message for low battery devices
1388
+ await this.writeDatapoints(); // fill the datapoints
1389
+ } catch (error) {
1390
+ this.errorReporting('[createDataOfAllAdapter]', error);
1146
1391
  }
1147
- await this.writeDatapoints();
1148
1392
 
1149
- this.log.debug(`Function finished: ${this.createDataOfAll.name}`);
1150
- }
1393
+ this.log.debug(`Function finished: ${this.createDataOfAllAdapter.name}`);
1394
+ } // <-- end of createDataOfAllAdapter
1151
1395
 
1152
- async resetVars() {
1153
- // arrays
1154
- this.offlineDevices = [],
1155
- this.linkQualityDevices = [];
1156
- this.batteryPowered = [];
1157
- this.batteryLowPowered = [];
1158
- this.listAllDevices = [];
1159
1396
 
1160
- // counts
1161
- this.offlineDevicesCount = 0;
1162
- this.deviceCounter = 0;
1163
- this.linkQualityCount = 0;
1164
- this.batteryPoweredCount = 0;
1165
- this.lowBatteryPoweredCount = 0;
1397
+ /**
1398
+ * Notification service
1399
+ * @param {string} text - Text which should be send
1400
+ **/
1401
+ async sendNotification(text) {
1166
1402
 
1167
- this.deviceReachable = '';
1168
- }
1403
+ // Pushover
1404
+ try {
1405
+ if (this.config.instancePushover) {
1406
+ //first check if instance is living
1407
+ const pushoverAliveState = await this.getInitValue('system.adapter.' + this.config.instancePushover + '.alive');
1169
1408
 
1170
- async main() {
1171
- this.log.debug(`Function started: ${this.main.name}`);
1409
+ if (!pushoverAliveState) {
1410
+ this.log.warn('Pushover instance is not running. Message could not be sent. Please check your instance configuration.');
1411
+ } else {
1412
+ await this.sendToAsync(this.config.instancePushover, 'send', {
1413
+ message: text,
1414
+ title: this.config.titlePushover,
1415
+ device: this.config.devicePushover,
1416
+ priority: this.config.prioPushover
1417
+ });
1418
+ }
1419
+ }
1420
+ } catch (error) {
1421
+ this.errorReporting('[sendNotification Pushover]', error);
1422
+ }
1172
1423
 
1173
- this.supAdapter = {
1174
- alexa2: this.config.alexa2Devices,
1175
- esphome: this.config.esphomeDevices,
1176
- zigbee: this.config.zigbeeDevices,
1177
- ble: this.config.bleDevices,
1178
- sonoff: this.config.sonoffDevices,
1179
- shelly: this.config.shellyDevices,
1180
- homematic: this.config.homematicDevices,
1181
- deconz: this.config.deconzDevices,
1182
- zwave: this.config.zwaveDevices,
1183
- dect: this.config.dectDevices,
1184
- hue: this.config.hueDevices,
1185
- hueExt: this.config.hueExtDevices,
1186
- nukiExt: this.config.nukiExtDevices,
1187
- ping: this.config.pingDevices,
1188
- switchbotBle: this.config.switchbotBleDevices,
1189
- sonos: this.config.sonosDevices,
1190
- mihome: this.config.mihomeDevices,
1191
- mihomeGW: this.config.mihomeDevices,
1192
- };
1424
+ // Telegram
1425
+ try {
1426
+ if (this.config.instanceTelegram) {
1427
+ //first check if instance is living
1428
+ const telegramAliveState = await this.getInitValue('system.adapter.' + this.config.instanceTelegram + '.alive');
1193
1429
 
1194
- for(const [id] of Object.entries(this.arrApart)) {
1195
- if (this.supAdapter[id]) {
1196
- this.arrDev.push(this.arrApart[id]);
1197
- this.adapterSelected.push(await this.capitalize(id));
1198
- this.log.debug(JSON.stringify(this.arrDev));
1430
+ if (!telegramAliveState) {
1431
+ this.log.warn('Telegram instance is not running. Message could not be sent. Please check your instance configuration.');
1432
+ } else {
1433
+ await this.sendToAsync(this.config.instanceTelegram, 'send', {
1434
+ text: text,
1435
+ user: this.config.deviceTelegram,
1436
+ chatId: this.config.chatIdTelegram
1437
+ });
1438
+ }
1439
+ }
1440
+ } catch (error) {
1441
+ this.errorReporting('[sendNotification Telegram]', error);
1442
+ }
1199
1443
 
1200
- //create and fill datapoints for each adapter if selected
1201
- if (this.config.createOwnFolder) {
1202
- try {
1203
- await this.createDPsForEachAdapter(id);
1204
- this.log.debug(`Created datapoints for ${await this.capitalize(id)}`);
1205
- await this.createDataForEachAdpt(id);
1206
- this.log.debug(`Created and filled data for each adapter`);
1207
- } catch (e) {
1208
- this.log.warn(`Error at creating/filling datapoints for each adapter: ${e}`);
1209
- return;
1210
- }
1444
+ // Whatsapp
1445
+ try {
1446
+ if (this.config.instanceWhatsapp) {
1447
+ //first check if instance is living
1448
+ const whatsappAliveState = await this.getInitValue('system.adapter.' + this.config.instanceWhatsapp + '.alive');
1449
+
1450
+ if (!whatsappAliveState) {
1451
+ this.log.warn('Whatsapp instance is not running. Message could not be sent. Please check your instance configuration.');
1452
+ } else {
1453
+ await this.sendToAsync(this.config.instanceWhatsapp, 'send', {
1454
+ text: text,
1455
+ phone: this.config.phoneWhatsapp
1456
+ });
1211
1457
  }
1212
1458
  }
1459
+ } catch (error) {
1460
+ this.errorReporting('[sendNotification Whatsapp]', error);
1213
1461
  }
1214
1462
 
1215
- //Check if an Adapter is selected.
1216
- if (this.adapterSelected.length >= 1) {
1217
- this.log.info(`Number of selected adapters: ${this.adapterSelected.length}. Loading data from: ${(this.adapterSelected).join(', ')} ...`);
1218
- } else {
1219
- this.log.warn(`No adapter selected. Please check the instance configuration!`);
1220
- return;
1463
+ // Email
1464
+ try {
1465
+ if (this.config.instanceEmail) {
1466
+ //first check if instance is living
1467
+ const eMailAliveState = await this.getInitValue('system.adapter.' + this.config.instanceEmail + '.alive');
1468
+
1469
+ if (!eMailAliveState) {
1470
+ this.log.warn('eMail instance is not running. Message could not be sent. Please check your instance configuration.');
1471
+ } else {
1472
+ await this.sendToAsync(this.config.instanceEmail, 'send', {
1473
+ sendTo: this.config.sendToEmail,
1474
+ text: text,
1475
+ subject: this.config.subjectEmail
1476
+ });
1477
+ }
1478
+ }
1479
+ } catch (error) {
1480
+ this.errorReporting('[sendNotification eMail]', error);
1221
1481
  }
1222
1482
 
1223
- /*=============================================
1224
- = Start of main loop =
1225
- =============================================*/
1483
+ // Jarvis Notification
1226
1484
  try {
1227
- await this.createDataOfAll();
1228
- this.log.debug(`Created and filled data for all adapters`);
1229
- } catch (e) {
1230
- this.log.warn(`Error at creating/filling datapoints for all adapters: ${e}`);
1231
- return;
1485
+ if (this.config.instanceJarvis) {
1486
+ //first check if instance is living
1487
+ const jarvisAliveState = await this.getInitValue('system.adapter.' + this.config.instanceJarvis + '.alive');
1488
+
1489
+ if (!jarvisAliveState) {
1490
+ this.log.warn('Jarvis instance is not running. Message could not be sent. Please check your instance configuration.');
1491
+ } else {
1492
+ const jsonText = JSON.stringify(text);
1493
+ await this.setForeignStateAsync(`${this.config.instanceJarvis}.addNotification`, '{"title":"' + this.config.titleJarvis + ' (' + this.formatDate(new Date(), 'DD.MM.YYYY - hh:mm:ss') + ')","message": ' + jsonText + ',"display": "drawer"}');
1494
+ }
1495
+ }
1496
+ } catch (error) {
1497
+ this.errorReporting('[sendNotification Jarvis]', error);
1232
1498
  }
1233
1499
 
1234
- this.log.debug(`Function finished: ${this.main.name}`);
1235
- } //<--End of main function
1500
+ // Lovelace Notification
1501
+ try {
1502
+ if (this.config.instanceLovelace) {
1503
+ //first check if instance is living
1504
+ const lovelaceAliveState = await this.getInitValue('system.adapter.' + this.config.instanceLovelace + '.alive');
1505
+
1506
+ if (!lovelaceAliveState) {
1507
+ this.log.warn('Lovelace instance is not running. Message could not be sent. Please check your instance configuration.');
1508
+ } else {
1509
+ const jsonText = JSON.stringify(text);
1510
+ await this.setForeignStateAsync(`${this.config.instanceLovelace}.notifications.add`, '{"message":' + jsonText + ', "title":"' + this.config.titleLovelace + ' (' + this.formatDate(new Date(), 'DD.MM.YYYY - hh:mm:ss') + ')"}');
1511
+ }
1512
+ }
1513
+ } catch (error) {
1514
+ this.errorReporting('[sendNotification Lovelace]', error);
1515
+ }
1516
+ } // <-- End of sendNotification function
1236
1517
 
1237
1518
 
1238
1519
  async sendOfflineNotifications() {
1239
- /*=============================================
1240
- = send offline notification =
1241
- =============================================*/
1520
+ // send message if an device is offline
1242
1521
 
1243
1522
  this.log.debug(`Start the function: ${this.sendOfflineNotifications.name}`);
1244
1523
 
@@ -1246,8 +1525,10 @@ class DeviceWatcher extends utils.Adapter {
1246
1525
  let msg = '';
1247
1526
  const offlineDevicesCountOld = await this.getOwnInitValue('offlineCount');
1248
1527
 
1249
- if ((this.offlineDevicesCount != offlineDevicesCountOld)) {
1250
- if (this.offlineDevicesCount == 1) { // make singular if it is only one device
1528
+ if ((this.offlineDevicesCount !== offlineDevicesCountOld)) {
1529
+ if (this.offlineDevicesCount == 0) {
1530
+ msg = 'Alle Geräte sind Online.';
1531
+ } else if (this.offlineDevicesCount == 1) { // make singular if it is only one device
1251
1532
  msg = 'Folgendes Gerät ist seit einiger Zeit nicht erreichbar: \n';
1252
1533
  } else if (this.offlineDevicesCount >= 2) { //make plural if it is more than one device
1253
1534
  msg = `Folgende ${this.offlineDevicesCount} Geräte sind seit einiger Zeit nicht erreichbar: \n`;
@@ -1256,246 +1537,385 @@ class DeviceWatcher extends utils.Adapter {
1256
1537
  for (const id of this.offlineDevices) {
1257
1538
  msg = `${msg} \n ${id['Device']} (${id['Last contact']})`;
1258
1539
  }
1540
+
1259
1541
  this.log.info(msg);
1260
1542
  await this.setStateAsync('lastNotification', msg, true);
1261
- if (this.config.instancePushover) {
1262
- try {
1263
- await this.sendPushover(msg);
1264
- } catch (e) {
1265
- this.log.warn (`Getting error at sending pushover notification ${e}`);
1266
- }
1267
- }
1268
- if (this.config.instanceTelegram) {
1269
- try {
1270
- await this.sendTelegram(msg);
1271
- } catch (e) {
1272
- this.log.warn (`Getting error at sending telegram notification ${e}`);
1273
- }
1274
- }
1275
- if (this.config.instanceWhatsapp) {
1276
- try {
1277
- await this.sendWhatsapp(msg);
1278
- } catch (e) {
1279
- this.log.warn (`Getting error at sending whatsapp notification ${e}`);
1280
- }
1281
- }
1282
- if (this.config.instanceEmail) {
1283
- try {
1284
- await this.sendEmail(msg);
1285
- } catch (e) {
1286
- this.log.warn (`Getting error at sending email notification ${e}`);
1287
- }
1288
- }
1289
- if (this.config.instanceJarvis) {
1290
- try {
1291
- await this.sendJarvis('{"title":"'+ this.config.titleJarvis +' (' + this.formatDate(new Date(), 'DD.MM.YYYY - hh:mm:ss') + ')","message":" ' + this.offlineDevicesCount + ' Geräte sind nicht erreichbar","display": "drawer"}');
1292
- } catch (e) {
1293
- this.log.warn (`Getting error at sending jarvis notification ${e}`);
1543
+ await this.sendNotification(msg);
1544
+ }
1545
+ } catch (error) {
1546
+ this.errorReporting('[sendOfflineMessage]', error);
1547
+ }
1548
+
1549
+ try {
1550
+ // send daily an overview with offline devices
1551
+ if (this.config.checkSendOfflineMsgDaily) {
1552
+ // Check if the daily message for offline devices was already sent today
1553
+ const lastOfflineNotifyIndicator = await this.getOwnInitValue('info.lastOfflineNotification');
1554
+ const now = new Date(); // get date
1555
+
1556
+ // set indicator for send message first to 'false', after sending to 'true'
1557
+ if (now.getHours() < 11) await this.setStateAsync('info.lastOfflineNotification', false, true);
1558
+
1559
+ // if time is > 11 (12:00 pm create message for offline devices devices)
1560
+ if ((now.getHours() > 11) && (!lastOfflineNotifyIndicator)) {
1561
+ let msg = '';
1562
+
1563
+ for (const id of this.offlineDevices) {
1564
+ msg = `${msg} \n ${id['Device']} (${id['Last contact']})`;
1294
1565
  }
1295
- }
1296
- if (this.config.instanceLovelace) {
1297
- try {
1298
- await this.sendLovelace('{"message":" ' + this.offlineDevicesCount + ' Geräte sind nicht erreichbar", "title":"'+ this.config.titleLovelace +' (' + this.formatDate(new Date(), 'DD.MM.YYYY - hh:mm:ss') + ')"}');
1299
- } catch (e) {
1300
- this.log.warn (`Getting error at sending lovelace notification ${e}`);
1566
+
1567
+ if (this.offlineDevicesCount > 0) {
1568
+ this.log.info(`Geräte Offline: ${msg}`);
1569
+ await this.setStateAsync('lastNotification', `Geräte Offline: ${msg}`, true);
1570
+
1571
+ await this.sendNotification(`Geräte Offline: ${msg}`);
1572
+
1573
+ await this.setStateAsync('info.lastOfflineNotification', true, true);
1301
1574
  }
1302
1575
  }
1303
1576
  }
1304
- } catch (e) {
1305
- this.log.debug(`Getting error at sending offline notification ${e}`);
1577
+ } catch (error) {
1578
+ this.errorReporting('[sendOfflineMessage - daily message]', error);
1306
1579
  }
1307
1580
  this.log.debug(`Finished the function: ${this.sendOfflineNotifications.name}`);
1308
1581
  }//<--End of offline notification
1309
1582
 
1583
+ async sendDailyOfflineNotifications() {
1584
+ // send daily an overview with offline devices
1310
1585
 
1311
- async sendBatteryNotifications() {
1312
- /*=============================================
1313
- = send low battery notification =
1314
- =============================================*/
1586
+ this.log.debug(`Start the function: ${this.sendDailyOfflineNotifications.name}`);
1315
1587
 
1316
- this.log.debug(`Start the function: ${this.sendBatteryNotifications.name}`);
1317
- const now = new Date();
1318
- const today = now.getDay();
1319
- const checkDays = [];
1320
- let checkToday;
1321
-
1322
- const choosedDays = {
1323
- monday: this.config.checkMonday,
1324
- tuesday: this.config.checkTuesday,
1325
- wednesday: this.config.checkWednesday,
1326
- thursday: this.config.checkThursday,
1327
- friday: this.config.checkFriday,
1328
- saturday: this.config.checkSaturday,
1329
- sunday: this.config.checkSunday,
1330
- };
1588
+ try {
1589
+ // Check if the daily message for offline devices was already sent today
1590
+ const lastOfflineNotifyIndicator = await this.getOwnInitValue('info.lastOfflineNotification');
1591
+ const now = new Date(); // get date
1331
1592
 
1332
- if (choosedDays.monday) checkDays.push(1);
1333
- if (choosedDays.tuesday) checkDays.push(2);
1334
- if (choosedDays.wednesday) checkDays.push(3);
1335
- if (choosedDays.thursday) checkDays.push(4);
1336
- if (choosedDays.friday) checkDays.push(5);
1337
- if (choosedDays.saturday) checkDays.push(6);
1338
- if (choosedDays.sunday) checkDays.push(0);
1339
-
1340
- //Check if the message should be send today
1341
- checkDays.forEach(object => {
1342
- if((object >= 0) && today == object){
1343
- checkToday = true;
1344
- }
1345
- });
1593
+ // set indicator for send message first to 'false', after sending to 'true'
1594
+ if (now.getHours() < 11) await this.setStateAsync('info.lastOfflineNotification', false, true);
1346
1595
 
1347
- //Check first if a day is selected
1348
- if (checkDays.length >= 1) {
1349
- this.log.debug(`Number of selected days: ${checkDays.length}. Send Message on: ${(checkDays).join(', ')} ...`);
1350
- } else {
1351
- this.log.warn(`No days selected. Please check the instance configuration!`);
1352
- return;
1596
+ // if time is > 11 (12:00 pm create message for offline devices devices)
1597
+ if ((now.getHours() > 11) && (!lastOfflineNotifyIndicator)) {
1598
+ let msg = '';
1599
+
1600
+ for (const id of this.offlineDevices) {
1601
+ msg = `${msg} \n ${id['Device']} (${id['Last contact']})`;
1602
+ }
1603
+
1604
+ if (this.offlineDevicesCount > 0) {
1605
+ this.log.info(`Geräte Offline: ${msg}`);
1606
+ await this.setStateAsync('lastNotification', `Geräte Offline: ${msg}`, true);
1607
+
1608
+ await this.sendNotification(`Geräte Offline: ${msg}`);
1609
+
1610
+ await this.setStateAsync('info.lastOfflineNotification', true, true);
1611
+ }
1612
+ }
1613
+ } catch (error) {
1614
+ this.errorReporting('[sendDailyOfflineNotifications]', error);
1353
1615
  }
1616
+ this.log.debug(`Finished the function: ${this.sendDailyOfflineNotifications.name}`);
1617
+ }//<--End of daily offline notification
1618
+
1619
+ async sendBatteryNotifications() {
1620
+ // send message for low battery devices
1621
+
1622
+ this.log.debug(`Start the function: ${this.sendBatteryNotifications.name}`);
1354
1623
 
1355
1624
  try {
1356
- //Check if the message for low battery was already sent today
1625
+
1626
+ const now = new Date(); // get date
1627
+ const today = now.getDay();
1628
+ const checkDays = []; // list of selected days
1629
+ let checkToday; // indicator if selected day is today
1630
+
1631
+ // push the selected days in list
1632
+ if (this.config.checkMonday) checkDays.push(1);
1633
+ if (this.config.checkTuesday) checkDays.push(2);
1634
+ if (this.config.checkWednesday) checkDays.push(3);
1635
+ if (this.config.checkThursday) checkDays.push(4);
1636
+ if (this.config.checkFriday) checkDays.push(5);
1637
+ if (this.config.checkSaturday) checkDays.push(6);
1638
+ if (this.config.checkSunday) checkDays.push(0);
1639
+
1640
+ //Check if the message should be send today
1641
+ checkDays.forEach(object => {
1642
+ if ((object >= 0) && today == object) {
1643
+ checkToday = true;
1644
+ }
1645
+ });
1646
+
1647
+ if (checkDays.length >= 1) { // check if an day is selected
1648
+ this.log.debug(`Number of selected days: ${checkDays.length}. Send Message on: ${(checkDays).join(', ')} ...`);
1649
+ } else {
1650
+ this.log.warn(`No days selected. Please check the instance configuration!`);
1651
+ return; // break off function if no day is selected
1652
+ }
1653
+
1654
+ // Check if the message for low battery was already sent today
1357
1655
  const lastBatteryNotifyIndicator = await this.getOwnInitValue('info.lastBatteryNotification');
1358
1656
 
1359
- if (now.getHours() < 11) {await this.setStateAsync('info.lastBatteryNotification', false, true);} //set indicator for send message first to 'false' later after sending to 'true'
1360
- if ((now.getHours() > 11) && (!lastBatteryNotifyIndicator) && (checkToday != undefined)){
1361
- let infotext = '';
1657
+ // set indicator for send message first to 'false', after sending to 'true'
1658
+ if (now.getHours() < 11) await this.setStateAsync('info.lastBatteryNotification', false, true);
1659
+
1660
+ // if time is > 11 (12:00 pm create message for low battery devices)
1661
+ if ((now.getHours() > 11) && (!lastBatteryNotifyIndicator) && (checkToday != undefined)) {
1662
+ let msg = '';
1362
1663
 
1363
1664
  for (const id of this.batteryLowPowered) {
1364
- infotext = '\n' + id['Device'] + ' (' + id['Battery'] + ')'.split(', ');
1665
+ msg = '\n' + id['Device'] + ' (' + id['Battery'] + ')'.split(', ');
1365
1666
  }
1366
1667
 
1367
1668
  if (this.lowBatteryPoweredCount > 0) {
1368
- this.log.info(`Niedrige Batteriezustände: ${infotext}`);
1369
- await this.setStateAsync('lastNotification', `Niedrige Batteriezustände: ${infotext}`, true);
1370
-
1371
- if (this.config.instancePushover) {
1372
- try {
1373
- await this.sendPushover(`Niedrige Batteriezustände: ${infotext}`);
1374
- } catch (e) {
1375
- this.log.warn (`Getting error at sending pushover notification ${e}`);
1376
- }
1377
- }
1378
- if (this.config.instanceTelegram) {
1379
- try {
1380
- await this.sendTelegram(`Niedrige Batteriezustände: ${infotext}`);
1381
- } catch (e) {
1382
- this.log.warn (`Getting error at sending telegram notification ${e}`);
1383
- }
1384
- }
1385
- if (this.config.instanceWhatsapp) {
1386
- try {
1387
- await this.sendWhatsapp(`Niedrige Batteriezustände: ${infotext}`);
1388
- } catch (e) {
1389
- this.log.warn (`Getting error at sending whatsapp notification ${e}`);
1390
- }
1391
- }
1392
- if (this.config.instanceEmail) {
1393
- try {
1394
- await this.sendEmail(`Niedrige Batteriezustände: ${infotext}`);
1395
- } catch (e) {
1396
- this.log.warn (`Getting error at sending email notification ${e}`);
1397
- }
1398
- }
1399
- if (this.config.instanceJarvis) {
1400
- try {
1401
- await this.sendJarvis('{"title":"'+ this.config.titleJarvis +' (' + this.formatDate(new Date(), 'DD.MM.YYYY - hh:mm:ss') + ')","message":" ' + this.lowBatteryPoweredCount + ' Geräte mit schwacher Batterie","display": "drawer"}');
1402
- } catch (e) {
1403
- this.log.warn (`Getting error at sending jarvis notification ${e}`);
1404
- }
1405
- }
1406
- if (this.config.instanceLovelace) {
1407
- try {
1408
- await this.sendLovelace('{"message":" ' + this.lowBatteryPoweredCount + ' Geräte mit schwacher Batterie", "title":"'+ this.config.titleLovelace +' (' + this.formatDate(new Date(), 'DD.MM.YYYY - hh:mm:ss') + ')"}');
1409
- } catch (e) {
1410
- this.log.warn (`Getting error at sending lovelace notification ${e}`);
1411
- }
1412
- }
1669
+ this.log.info(`Niedrige Batteriezustände: ${msg}`);
1670
+ await this.setStateAsync('lastNotification', `Niedrige Batteriezustände: ${msg}`, true);
1671
+
1672
+ await this.sendNotification(`Niedriege Batteriezustände: ${msg}`);
1413
1673
 
1414
1674
  await this.setStateAsync('info.lastBatteryNotification', true, true);
1415
1675
  }
1416
1676
  }
1417
- } catch (e) {
1418
- this.log.debug(`Getting error at sending battery notification ${e}`);
1677
+ } catch (error) {
1678
+ this.errorReporting('[sendOfflineMessage]', error);
1419
1679
  }
1680
+
1420
1681
  this.log.debug(`Finished the function: ${this.sendBatteryNotifications.name}`);
1421
1682
  }//<--End of battery notification
1422
1683
 
1684
+
1685
+ async resetVars() {
1686
+ //Reset all arrays and counts
1687
+ this.log.debug(`Function started: ${this.resetVars.name}`);
1688
+
1689
+ // arrays
1690
+ this.offlineDevices = [];
1691
+ this.linkQualityDevices = [];
1692
+ this.batteryPowered = [];
1693
+ this.batteryLowPowered = [];
1694
+ this.listAllDevices = [];
1695
+
1696
+ // counts
1697
+ this.offlineDevicesCount = 0;
1698
+ this.deviceCounter = 0;
1699
+ this.linkQualityCount = 0;
1700
+ this.batteryPoweredCount = 0;
1701
+ this.lowBatteryPoweredCount = 0;
1702
+
1703
+ this.deviceReachable = '';
1704
+
1705
+ this.log.debug(`Function finished: ${this.resetVars.name}`);
1706
+ } // <-- end of resetVars
1707
+
1708
+
1709
+ /**
1710
+ * @param {string} [adptName] - Adaptername
1711
+ */
1423
1712
  async writeDatapoints(adptName) {
1424
- /*=============================================
1425
- = Write Datapoints =
1426
- =============================================*/
1713
+ // fill the datapoints
1427
1714
 
1428
1715
  this.log.debug(`Start the function: ${this.writeDatapoints.name}`);
1429
1716
 
1430
1717
  try {
1431
1718
 
1432
1719
  let dpSubFolder;
1433
- if (adptName) { //write the datapoints in subfolders with the adaptername otherwise write the dP's in the root folder
1720
+ //write the datapoints in subfolders with the adaptername otherwise write the dP's in the root folder
1721
+ if (adptName) {
1434
1722
  dpSubFolder = adptName + '.';
1435
1723
  } else {
1436
- dpSubFolder = '';}
1724
+ dpSubFolder = '';
1725
+ }
1437
1726
 
1438
- await this.setStateAsync(`${dpSubFolder}offlineCount`, {val: this.offlineDevicesCount, ack: true});
1439
- await this.setStateAsync(`${dpSubFolder}countAll`, {val: this.deviceCounter, ack: true});
1440
- await this.setStateAsync(`${dpSubFolder}batteryCount`, {val: this.batteryPoweredCount, ack: true});
1441
- await this.setStateAsync(`${dpSubFolder}lowBatteryCount`, {val: this.lowBatteryPoweredCount, ack: true});
1727
+ await this.setStateAsync(`${dpSubFolder}offlineCount`, { val: this.offlineDevicesCount, ack: true });
1728
+ await this.setStateAsync(`${dpSubFolder}countAll`, { val: this.deviceCounter, ack: true });
1729
+ await this.setStateAsync(`${dpSubFolder}batteryCount`, { val: this.batteryPoweredCount, ack: true });
1730
+ await this.setStateAsync(`${dpSubFolder}lowBatteryCount`, { val: this.lowBatteryPoweredCount, ack: true });
1442
1731
 
1443
1732
  if (this.deviceCounter == 0) {
1444
- this.listAllDevices = [{'Device': '--none--', 'Adapter': '', 'Battery': '', 'Last contact': '', 'Signal strength': ''}]; //JSON-Info Gesamtliste mit Info je Gerät
1445
-
1446
- await this.setStateAsync(`${dpSubFolder}listAll`, {val: JSON.stringify(this.listAllDevices), ack: true});
1447
- } else {
1448
- await this.setStateAsync(`${dpSubFolder}listAll`, {val: JSON.stringify(this.listAllDevices), ack: true});
1733
+ // if no device is count, write the JSON List with default value
1734
+ this.listAllDevices = [{ 'Device': '--none--', 'Adapter': '', 'Battery': '', 'Last contact': '', 'Signal strength': '' }];
1449
1735
  }
1736
+ await this.setStateAsync(`${dpSubFolder}listAll`, { val: JSON.stringify(this.listAllDevices), ack: true });
1450
1737
 
1451
1738
  if (this.linkQualityCount == 0) {
1452
- this.linkQualityDevices = [{'Device': '--none--', 'Adapter': '', 'Signal strength': ''}]; //JSON-Info alle mit LinkQuality
1453
-
1454
- await this.setStateAsync(`${dpSubFolder}linkQualityList`, {val: JSON.stringify(this.linkQualityDevices), ack: true});
1455
- } else {
1456
- await this.setStateAsync(`${dpSubFolder}linkQualityList`, {val: JSON.stringify(this.linkQualityDevices), ack: true});
1739
+ // if no device is count, write the JSON List with default value
1740
+ this.linkQualityDevices = [{ 'Device': '--none--', 'Adapter': '', 'Signal strength': '' }];
1457
1741
  }
1458
-
1742
+ //write JSON list
1743
+ await this.setStateAsync(`${dpSubFolder}linkQualityList`, { val: JSON.stringify(this.linkQualityDevices), ack: true });
1744
+ //write HTML list
1745
+ if (this.config.createHtmlList) await this.setStateAsync(`${dpSubFolder}linkQualityListHTML`, { val: await this.creatLinkQualityListHTML(this.linkQualityDevices, this.linkQualityCount), ack: true });
1459
1746
 
1460
1747
  if (this.offlineDevicesCount == 0) {
1461
- this.offlineDevices = [{'Device': '--none--', 'Adapter': '', 'Last contact': ''}]; //JSON-Info alle offline-Geräte = 0
1462
-
1463
- await this.setStateAsync(`${dpSubFolder}offlineList`, {val: JSON.stringify(this.offlineDevices), ack: true});
1464
- } else {
1465
- await this.setStateAsync(`${dpSubFolder}offlineList`, {val: JSON.stringify(this.offlineDevices), ack: true});
1748
+ // if no device is count, write the JSON List with default value
1749
+ this.offlineDevices = [{ 'Device': '--none--', 'Adapter': '', 'Last contact': '' }];
1466
1750
  }
1751
+ //write JSON list
1752
+ await this.setStateAsync(`${dpSubFolder}offlineList`, { val: JSON.stringify(this.offlineDevices), ack: true });
1753
+ //write HTML list
1754
+ if (this.config.createHtmlList) await this.setStateAsync(`${dpSubFolder}offlineListHTML`, { val: await this.createOfflineListHTML(this.offlineDevices, this.offlineDevicesCount), ack: true });
1467
1755
 
1468
1756
  if (this.batteryPoweredCount == 0) {
1469
- this.batteryPowered = [{'Device': '--none--', 'Adapter': '', 'Battery': ''}]; //JSON-Info alle batteriebetriebenen Geräte
1470
-
1471
- await this.setStateAsync(`${dpSubFolder}batteryList`, {val: JSON.stringify(this.batteryPowered), ack: true});
1472
- } else {
1473
- await this.setStateAsync(`${dpSubFolder}batteryList`, {val: JSON.stringify(this.batteryPowered), ack: true});
1757
+ // if no device is count, write the JSON List with default value
1758
+ this.batteryPowered = [{ 'Device': '--none--', 'Adapter': '', 'Battery': '' }];
1474
1759
  }
1760
+ //write JSON list
1761
+ await this.setStateAsync(`${dpSubFolder}batteryList`, { val: JSON.stringify(this.batteryPowered), ack: true });
1762
+ //write HTML list
1763
+ if (this.config.createHtmlList) await this.setStateAsync(`${dpSubFolder}batteryListHTML`, { val: await this.createBatteryListHTML(this.batteryPowered, this.batteryPoweredCount, false), ack: true });
1475
1764
 
1476
1765
  if (this.lowBatteryPoweredCount == 0) {
1477
- this.batteryLowPowered = [{'Device': '--none--', 'Adapter': '', 'Battery': ''}]; //JSON-Info alle batteriebetriebenen Geräte
1478
-
1479
- await this.setStateAsync(`${dpSubFolder}lowBatteryList`, {val: JSON.stringify(this.batteryLowPowered), ack: true});
1480
- } else {
1481
- await this.setStateAsync(`${dpSubFolder}lowBatteryList`, {val: JSON.stringify(this.batteryLowPowered), ack: true});
1766
+ // if no device is count, write the JSON List with default value
1767
+ this.batteryLowPowered = [{ 'Device': '--none--', 'Adapter': '', 'Battery': '' }];
1482
1768
  }
1769
+ //write JSON list
1770
+ await this.setStateAsync(`${dpSubFolder}lowBatteryList`, { val: JSON.stringify(this.batteryLowPowered), ack: true });
1771
+ //write HTML list
1772
+ if (this.config.createHtmlList) await this.setStateAsync(`${dpSubFolder}lowBatteryListHTML`, { val: await this.createBatteryListHTML(this.batteryLowPowered, this.lowBatteryPoweredCount, true), ack: true });
1483
1773
 
1484
- //Zeitstempel wann die Datenpunkte zuletzt gecheckt wurden
1774
+ // create timestamp of last run
1485
1775
  const lastCheck = this.formatDate(new Date(), 'DD.MM.YYYY') + ' - ' + this.formatDate(new Date(), 'hh:mm:ss');
1486
1776
  await this.setStateAsync('lastCheck', lastCheck, true);
1487
1777
  }
1488
- catch (e) {
1489
- this.log.error(`(05) Error while writing the states ${e}`);
1778
+ catch (error) {
1779
+ this.errorReporting('[writeDatapoints]', error);
1490
1780
  }
1491
1781
  this.log.debug(`Function finished: ${this.writeDatapoints.name}`);
1492
1782
  }//<--End of writing Datapoints
1493
1783
 
1784
+ /**
1785
+ * @param {object} devices - Device
1786
+ * @param {number} deviceCount - Counted devices
1787
+ */
1788
+ async creatLinkQualityListHTML(devices, deviceCount) {
1789
+ devices = devices.sort((a, b) => { return a.Device.localeCompare(b.Device); });
1790
+ let html = `<center>
1791
+ <b>Link Quality Devices:<font> ${deviceCount}</b><small></small></font>
1792
+ <p></p>
1793
+ </center>
1794
+ <table width=100%>
1795
+ <tr>
1796
+ <th align=left>Device</th>
1797
+ <th align=center width=120>Adapter</th>
1798
+ <th align=right>Link Quality</th>
1799
+ </tr>
1800
+ <tr>
1801
+ <td colspan="5"><hr></td>
1802
+ </tr>`;
1803
+
1804
+ for (const device of devices) {
1805
+ html += `<tr>
1806
+ <td><font>${device.Device}</font></td>
1807
+ <td align=center><font>${device.Adapter}</font></td>
1808
+ <td align=right><font>${device['Signal strength']}</font></td>
1809
+ </tr>`;
1810
+ }
1811
+
1812
+ html += '</table>';
1813
+ return html;
1814
+ }
1815
+
1816
+ /**
1817
+ * @param {object} devices - Device
1818
+ * @param {number} deviceCount - Counted devices
1819
+ */
1820
+ async createOfflineListHTML(devices, deviceCount) {
1821
+ devices = devices.sort((a, b) => { return a.Device.localeCompare(b.Device); });
1822
+ let html = `<center>
1823
+ <b>Offline Devices: <font color=${deviceCount == 0 ? '#3bcf0e' : 'orange'}>${deviceCount}</b><small></small></font>
1824
+ <p></p>
1825
+ </center>
1826
+ <table width=100%>
1827
+ <tr>
1828
+ <th align=left>Device</th>
1829
+ <th align=center width=120>Adapter</th>
1830
+ <th align=center>Letzter Kontakt</th>
1831
+ </tr>
1832
+ <tr>
1833
+ <td colspan="5"><hr></td>
1834
+ </tr>`;
1835
+
1836
+ for (const device of devices) {
1837
+ html += `<tr>
1838
+ <td><font>${device.Device}</font></td>
1839
+ <td align=center><font>${device.Adapter}</font></td>
1840
+ <td align=center><font color=orange>${device['Last contact']}</font></td>
1841
+ </tr>`;
1842
+ }
1843
+
1844
+ html += '</table>';
1845
+ return html;
1846
+ }
1847
+
1848
+ /**
1849
+ * @param {object} [devices] - Device
1850
+ * @param {object} [deviceCount] - Counted devices
1851
+ * @param {object} [isLowBatteryList] - list Low Battery Devices
1852
+ */
1853
+ async createBatteryListHTML(devices, deviceCount, isLowBatteryList) {
1854
+ devices = devices.sort((a, b) => { return a.Device.localeCompare(b.Device); });
1855
+ let html = `<center>
1856
+ <b>${isLowBatteryList == true ? 'Schwache ' : ''}Batterie Devices: <font color=${isLowBatteryList == true ? (deviceCount > 0 ? 'orange' : '#3bcf0e') : ''}>${deviceCount}</b></font>
1857
+ <p></p>
1858
+ </center>
1859
+ <table width=100%>
1860
+ <tr>
1861
+ <th align=left>Device</th>
1862
+ <th align=center width=120>Adapter</th>
1863
+ <th align=${isLowBatteryList ? 'center' : 'right'}>Batterie</th>
1864
+ </tr>
1865
+ <tr>
1866
+ <td colspan="5"><hr></td>
1867
+ </tr>`;
1868
+
1869
+ for (const device of devices) {
1870
+ html += `<tr>
1871
+ <td><font>${device.Device}</font></td>
1872
+ <td align=center><font>${device.Adapter}</font></td>`;
1873
+
1874
+ if (isLowBatteryList) {
1875
+ html += `<td align=center><font color=orange>${device.Battery}</font></td>`;
1876
+ } else {
1877
+ html += `<td align=right><font color=#3bcf0e>${device.Battery}</font></td>`;
1878
+ }
1879
+
1880
+ html += `</tr>`;
1881
+ }
1882
+
1883
+ html += '</table>';
1884
+ return html;
1885
+ }
1886
+
1887
+ /**
1888
+ * @param {string} codePart - Message Prefix
1889
+ * @param {object} error - Sentry message
1890
+ */
1891
+ errorReporting(codePart, error) {
1892
+ const msg = `[${codePart}] error: ${error.message}`;
1893
+ if (enableSendSentry) {
1894
+ if (this.supportsFeature && this.supportsFeature('PLUGINS')) {
1895
+ const sentryInstance = this.getPluginInstance('sentry');
1896
+ if (sentryInstance) {
1897
+ this.log.warn(`Error catched and sent to Sentry, error: ${msg}`);
1898
+ sentryInstance.getSentryObject().captureException(msg);
1899
+ }
1900
+ }
1901
+ } else {
1902
+ this.log.error(`Sentry disabled, error catched : ${msg}`);
1903
+ }
1904
+ } // <-- end of errorReporting
1494
1905
 
1495
1906
 
1907
+ /**
1908
+ * @param {() => void} callback
1909
+ */
1496
1910
  onUnload(callback) {
1497
1911
  try {
1498
1912
  this.log.info('cleaned everything up...');
1913
+
1914
+ if (this.refreshDataTimeout) {
1915
+ this.log.debug('clearing refresh timeout');
1916
+ this.clearTimeout(this.refreshDataTimeout);
1917
+ }
1918
+
1499
1919
  callback();
1500
1920
  } catch (e) {
1501
1921
  callback();
@@ -1503,6 +1923,7 @@ class DeviceWatcher extends utils.Adapter {
1503
1923
  }
1504
1924
  }
1505
1925
 
1926
+ // @ts-ignore parent is a valid property on module
1506
1927
  if (require.main !== module) {
1507
1928
  // Export the constructor in compact mode
1508
1929
  /**