iobroker.device-watcher 2.4.0 → 2.5.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
@@ -8,6 +8,7 @@ const utils = require('@iobroker/adapter-core');
8
8
  const adapterName = require('./package.json').name.split('.').pop();
9
9
  const schedule = require('node-schedule');
10
10
  const arrApart = require('./lib/arrApart.js'); // list of supported adapters
11
+ const cronParser = require('cron-parser');
11
12
 
12
13
  // Sentry error reporting, disable when testing code!
13
14
  const enableSendSentry = true;
@@ -23,6 +24,27 @@ class DeviceWatcher extends utils.Adapter {
23
24
  useFormatDate: true,
24
25
  });
25
26
 
27
+ // instances and adapters
28
+ // raw arrays
29
+ this.adapterUpdatesJsonRaw = [];
30
+ this.listInstanceRaw = [];
31
+ this.listErrorInstanceRaw = [];
32
+
33
+ // user arrays
34
+ this.blacklistInstancesLists = [];
35
+ this.blacklistInstancesNotify = [];
36
+ this.listAllInstances = [];
37
+ this.listDeactivatedInstances = [];
38
+ this.listAdapterUpdates = [];
39
+ this.listErrorInstance = [];
40
+
41
+ //counts
42
+ this.countAllInstances = 0;
43
+ this.countDeactivatedInstances = 0;
44
+ this.countAdapterUpdates = 0;
45
+ this.countErrorInstance = 0;
46
+
47
+ // devices
26
48
  // arrays
27
49
  this.offlineDevices = [];
28
50
  this.linkQualityDevices = [];
@@ -219,6 +241,11 @@ class DeviceWatcher extends utils.Adapter {
219
241
  //read data first at start
220
242
  await this.main();
221
243
 
244
+ if (this.config.checkAdapterInstances) {
245
+ await this.getInstanceData();
246
+ await this.createAdapterUpdateData();
247
+ }
248
+
222
249
  // update last contact data in interval
223
250
  await this.refreshData();
224
251
 
@@ -229,7 +256,10 @@ class DeviceWatcher extends utils.Adapter {
229
256
  if (this.config.checkSendOfflineMsgDaily) await this.sendOfflineNotificationsShedule();
230
257
 
231
258
  // send overview of upgradeable devices
232
- if (this.config.checkSendUpgradeMsgDaily) await this.sendUpgradeNotificationsShedule();
259
+ if (this.config.checkSendUpgradeMsgDaily) await this.sendDeviceUpdateNotificationsShedule();
260
+
261
+ // send overview of instances with error
262
+ if (this.config.checkSendInstanceFailedDaily) await this.sendInstanceErrorNotificationShedule();
233
263
  } catch (error) {
234
264
  this.errorReporting('[onReady]', error);
235
265
  this.terminate ? this.terminate(15) : process.exit(15);
@@ -250,19 +280,95 @@ class DeviceWatcher extends utils.Adapter {
250
280
  let contactData;
251
281
  let oldStatus;
252
282
  let isLowBatValue;
283
+ let instanceStatusRaw;
284
+ let instanceDeviceConnectionDpTS;
285
+ const instanceDeviceConnectionDpTSminTime = 10;
286
+
287
+ /*
288
+ if (this.config.checkAdapterInstances) {
289
+ for (const adapter of this.adapterUpdatesJsonRaw) {
290
+ switch (id) {
291
+ case adapter.Path:
292
+
293
+ this.sendAdapterUpdatesNotification(id, state);
294
+ }
295
+ }
296
+ }*/
297
+
298
+ for (const instance of this.listInstanceRaw) {
299
+ switch (id) {
300
+ case instance.instanceAlivePath:
301
+ if (state.val !== instance.isAlive) {
302
+ instanceStatusRaw = await this.setInstanceStatus(
303
+ instance.instanceMode,
304
+ instance.schedule,
305
+ instance.instanceAlivePath,
306
+ state.val,
307
+ instance.isConnectedHost,
308
+ instance.isConnectedDevice,
309
+ );
310
+ instance.isAlive = instanceStatusRaw[1];
311
+ instance.status = instanceStatusRaw[0];
312
+ instance.isHealthy = instanceStatusRaw[2];
313
+ }
314
+ break;
315
+ case instance.connectedHostPath:
316
+ if (instance.isAlive && state.val !== instance.isConnectedHost) {
317
+ instance.isConnectedHost = state.val;
318
+ instanceStatusRaw = await this.setInstanceStatus(
319
+ instance.instanceMode,
320
+ instance.schedule,
321
+ instance.instanceAlivePath,
322
+ instance.isAlive,
323
+ state.val,
324
+ instance.isConnectedDevice,
325
+ );
326
+ instance.isAlive = instanceStatusRaw[1];
327
+ instance.status = instanceStatusRaw[0];
328
+ instance.isHealthy = instanceStatusRaw[2];
329
+
330
+ if (this.config.checkSendInstanceFailedMsg && !instance.isHealthy && !this.blacklistNotify.includes(instance.instanceAlivePath)) {
331
+ await this.sendInstanceErrorNotification(instance.InstanceName, instance.status);
332
+ }
333
+ }
334
+ break;
335
+ case instance.connectedDevicePath:
336
+ if (instance.isAlive && state.val !== instance.isConnectedDevice) {
337
+ instance.isConnectedDevice = state.val;
338
+ instanceStatusRaw = await this.setInstanceStatus(
339
+ instance.instanceMode,
340
+ instance.schedule,
341
+ instance.instanceAlivePath,
342
+ instance.isAlive,
343
+ instance.isConnectedHost,
344
+ state.val,
345
+ );
346
+ instance.isAlive = instanceStatusRaw[1];
347
+ instance.status = instanceStatusRaw[0];
348
+ instance.isHealthy = instanceStatusRaw[2];
349
+
350
+ if (this.config.checkSendInstanceFailedMsg && !instance.isHealthy && !this.blacklistNotify.includes(instance.instanceAlivePath)) {
351
+ await this.sendInstanceErrorNotification(instance.InstanceName, instance.status);
352
+ }
353
+ }
354
+ break;
355
+ }
356
+ }
253
357
 
254
358
  for (const device of this.listAllDevicesRaw) {
255
359
  // On statechange update available datapoint
256
360
  switch (id) {
257
- case device.instanceAliveDP:
258
- device.instanceAlive = state.val;
361
+ case device.instanceDeviceConnectionDP:
362
+ if (state.val !== device.instancedeviceConnected) {
363
+ device.instancedeviceConnected = state.val;
364
+ }
259
365
  break;
260
366
 
261
367
  case device.UpdateDP:
262
- if (device.instanceAlive) {
368
+ if (state.val !== device.Upgradable) {
263
369
  device.Upgradable = state.val;
264
370
  if (state.val) {
265
- if (!this.blacklistNotify.includes(device.Path)) {
371
+ if (this.config.checkSendDeviceUpgrade && !this.blacklistNotify.includes(device.Path)) {
266
372
  await this.sendDeviceUpdatesNotification(device.Device, device.Adapter);
267
373
  }
268
374
  }
@@ -270,91 +376,89 @@ class DeviceWatcher extends utils.Adapter {
270
376
  break;
271
377
 
272
378
  case device.SignalStrengthDP:
273
- if (device.instanceAlive) {
274
- device.SignalStrength = await this.calculateSignalStrength(state, device.adapterID);
275
- }
379
+ device.SignalStrength = await this.calculateSignalStrength(state, device.adapterID);
276
380
  break;
277
381
 
278
382
  case device.batteryDP:
279
- if (device.instanceAlive) {
280
- if (device.isBatteryDevice) {
281
- oldLowBatState = device.LowBat;
282
- batteryData = await this.getBatteryData(state.val, oldLowBatState, device.adapterID);
283
-
284
- device.Battery = batteryData[0];
285
- device.BatteryRaw = batteryData[2];
286
- if (device.LowBatDP !== 'none') {
287
- isLowBatValue = await this.getInitValue(device.LowBatDP);
288
- } else {
289
- isLowBatValue = undefined;
290
- }
291
- device.LowBat = await this.setLowbatIndicator(state.val, isLowBatValue, device.faultReport, device.adapterID);
383
+ if (device.isBatteryDevice) {
384
+ oldLowBatState = device.LowBat;
385
+ batteryData = await this.getBatteryData(state.val, oldLowBatState, device.adapterID);
386
+
387
+ device.Battery = batteryData[0];
388
+ device.BatteryRaw = batteryData[2];
389
+ if (device.LowBatDP !== 'none') {
390
+ isLowBatValue = await this.getInitValue(device.LowBatDP);
391
+ } else {
392
+ isLowBatValue = undefined;
393
+ }
394
+ device.LowBat = await this.setLowbatIndicator(state.val, isLowBatValue, device.faultReport, device.adapterID);
292
395
 
293
- if (device.LowBat && oldLowBatState !== device.LowBat) {
294
- if (this.config.checkSendBatteryMsg && !this.blacklistNotify.includes(device.Path)) {
295
- await this.sendLowBatNoticiation(device.Device, device.Adapter, device.Battery);
296
- }
396
+ if (device.LowBat && oldLowBatState !== device.LowBat) {
397
+ if (this.config.checkSendBatteryMsg && !this.blacklistNotify.includes(device.Path)) {
398
+ await this.sendLowBatNoticiation(device.Device, device.Adapter, device.Battery);
297
399
  }
298
400
  }
299
401
  }
300
402
  break;
301
403
 
302
404
  case device.LowBatDP:
303
- if (device.instanceAlive) {
304
- if (device.isBatteryDevice) {
305
- oldLowBatState = device.LowBat;
306
- batteryData = await this.getBatteryData(device.BatteryRaw, state.val, device.adapterID);
307
- device.Battery = batteryData[0];
308
- device.BatteryRaw = batteryData[2];
309
- device.LowBat = await this.setLowbatIndicator(device.BatteryRaw, state.val, device.faultReport, device.adapterID);
310
-
311
- if (device.LowBat && oldLowBatState !== device.LowBat) {
312
- if (this.config.checkSendBatteryMsg && !this.blacklistNotify.includes(device.Path)) {
313
- await this.sendLowBatNoticiation(device.Device, device.Adapter, device.Battery);
314
- }
405
+ if (device.isBatteryDevice) {
406
+ oldLowBatState = device.LowBat;
407
+ batteryData = await this.getBatteryData(device.BatteryRaw, state.val, device.adapterID);
408
+ device.Battery = batteryData[0];
409
+ device.BatteryRaw = batteryData[2];
410
+ device.LowBat = await this.setLowbatIndicator(device.BatteryRaw, state.val, device.faultReport, device.adapterID);
411
+
412
+ if (device.LowBat && oldLowBatState !== device.LowBat) {
413
+ if (this.config.checkSendBatteryMsg && !this.blacklistNotify.includes(device.Path)) {
414
+ await this.sendLowBatNoticiation(device.Device, device.Adapter, device.Battery);
315
415
  }
316
416
  }
317
417
  }
318
418
  break;
319
419
 
320
420
  case device.faultReportDP:
321
- if (device.instanceAlive) {
322
- if (device.isBatteryDevice) {
323
- oldLowBatState = device.LowBat;
324
- batteryData = await this.getBatteryData(device.BatteryRaw, oldLowBatState, device.adapterID);
325
-
326
- device.Battery = batteryData[0];
327
- device.BatteryRaw = batteryData[2];
328
- device.LowBat = await this.setLowbatIndicator(device.BatteryRaw, undefined, state.val, device.adapterID);
329
-
330
- if (device.LowBat && oldLowBatState !== device.LowBat) {
331
- if (this.config.checkSendBatteryMsg && !this.blacklistNotify.includes(device.Path)) {
332
- await this.sendLowBatNoticiation(device.Device, device.Adapter, device.Battery);
333
- }
421
+ if (device.isBatteryDevice) {
422
+ oldLowBatState = device.LowBat;
423
+ batteryData = await this.getBatteryData(device.BatteryRaw, oldLowBatState, device.adapterID);
424
+
425
+ device.Battery = batteryData[0];
426
+ device.BatteryRaw = batteryData[2];
427
+ device.LowBat = await this.setLowbatIndicator(device.BatteryRaw, undefined, state.val, device.adapterID);
428
+
429
+ if (device.LowBat && oldLowBatState !== device.LowBat) {
430
+ if (this.config.checkSendBatteryMsg && !this.blacklistNotify.includes(device.Path)) {
431
+ await this.sendLowBatNoticiation(device.Device, device.Adapter, device.Battery);
334
432
  }
335
433
  }
336
434
  }
337
435
  break;
338
436
 
339
437
  case device.UnreachDP:
340
- if (device.instanceAlive) {
341
- oldStatus = device.Status;
342
- device.UnreachState = await this.getInitValue(device.UnreachDP);
343
- contactData = await this.getOnlineState(
344
- device.timeSelector,
345
- device.adapterID,
346
- device.UnreachDP,
347
- device.SignalStrength,
348
- device.UnreachState,
349
- device.DeviceStateSelectorDP,
350
- device.rssiPeerSelectorDP,
351
- );
352
- if (contactData !== undefined) {
353
- device.LastContact = contactData[0];
354
- device.Status = contactData[1];
355
- device.SignalStrength = contactData[2];
438
+ oldStatus = device.Status;
439
+ device.UnreachState = await this.getInitValue(device.UnreachDP);
440
+ contactData = await this.getOnlineState(
441
+ device.timeSelector,
442
+ device.adapterID,
443
+ device.UnreachDP,
444
+ device.SignalStrength,
445
+ device.UnreachState,
446
+ device.DeviceStateSelectorDP,
447
+ device.rssiPeerSelectorDP,
448
+ );
449
+ if (contactData !== undefined) {
450
+ device.LastContact = contactData[0];
451
+ device.Status = contactData[1];
452
+ device.SignalStrength = contactData[2];
453
+ }
454
+ if (device.instanceDeviceConnectionDP !== undefined) {
455
+ instanceDeviceConnectionDpTS = await this.getTimestampConnectionDP(device.instanceDeviceConnectionDP);
456
+ if (device.instancedeviceConnected !== false && instanceDeviceConnectionDpTS && instanceDeviceConnectionDpTS >= instanceDeviceConnectionDpTSminTime) {
457
+ if (this.config.checkSendOfflineMsg && oldStatus !== device.Status && !this.blacklistNotify.includes(device.Path)) {
458
+ await this.sendOfflineNotifications(device.Device, device.Adapter, device.Status, device.LastContact);
459
+ }
356
460
  }
357
-
461
+ } else {
358
462
  if (this.config.checkSendOfflineMsg && oldStatus !== device.Status && !this.blacklistNotify.includes(device.Path)) {
359
463
  await this.sendOfflineNotifications(device.Device, device.Adapter, device.Status, device.LastContact);
360
464
  }
@@ -372,6 +476,9 @@ class DeviceWatcher extends utils.Adapter {
372
476
  const devices = [];
373
477
  let myCount = 0;
374
478
  let result;
479
+ const instances = [];
480
+ let myCountInstances = 0;
481
+ let resultInstances;
375
482
 
376
483
  switch (obj.command) {
377
484
  case 'devicesList':
@@ -402,6 +509,35 @@ class DeviceWatcher extends utils.Adapter {
402
509
  this.sendTo(obj.from, obj.command, obj.callback);
403
510
  }
404
511
  break;
512
+
513
+ case 'instancesList':
514
+ if (obj.message) {
515
+ try {
516
+ resultInstances = this.listInstanceRaw;
517
+ for (const element in resultInstances) {
518
+ const label = `${resultInstances[element].Adapter}: ${resultInstances[element].InstanceName}`;
519
+ const myValueObject = {
520
+ adapter: resultInstances[element].Adapter,
521
+ instanceName: resultInstances[element].InstanceName,
522
+ path: resultInstances[element].instanceAlivePath,
523
+ };
524
+ instances[myCountInstances] = { label: label, value: JSON.stringify(myValueObject) };
525
+ myCountInstances++;
526
+ }
527
+ const sortInstances = instances.slice(0);
528
+ sortInstances.sort(function (a, b) {
529
+ const x = a.label;
530
+ const y = b.label;
531
+ return x < y ? -1 : x > y ? 1 : 0;
532
+ });
533
+ this.sendTo(obj.from, obj.command, sortInstances, obj.callback);
534
+ } catch (error) {
535
+ this.sendTo(obj.from, obj.command, obj.callback);
536
+ }
537
+ } else {
538
+ this.sendTo(obj.from, obj.command, obj.callback);
539
+ }
540
+ break;
405
541
  }
406
542
  }
407
543
 
@@ -457,6 +593,11 @@ class DeviceWatcher extends utils.Adapter {
457
593
  }
458
594
  }
459
595
 
596
+ if (this.config.checkAdapterInstances) {
597
+ await this.createInstanceList();
598
+ await this.writeInstanceDPs();
599
+ }
600
+
460
601
  // Clear existing timeout
461
602
  if (this.refreshDataTimeout) {
462
603
  this.log.debug('clearing old refresh timeout');
@@ -480,6 +621,7 @@ class DeviceWatcher extends utils.Adapter {
480
621
  async createBlacklist() {
481
622
  this.log.debug(`Function started: ${this.createBlacklist.name}`);
482
623
 
624
+ // DEVICES
483
625
  const myBlacklist = this.config.tableBlacklist;
484
626
 
485
627
  for (const i in myBlacklist) {
@@ -505,6 +647,28 @@ class DeviceWatcher extends utils.Adapter {
505
647
  if (this.blacklistAdapterLists.length >= 1) this.log.info(`Found items on blacklist for lists: ${this.blacklistAdapterLists}`);
506
648
  if (this.blacklistNotify.length >= 1) this.log.info(`Found items on blacklist for notificatioons: ${this.blacklistNotify}`);
507
649
 
650
+ // INSTANCES
651
+ const myBlacklistInstances = this.config.tableBlacklistInstances;
652
+
653
+ for (const i in myBlacklistInstances) {
654
+ try {
655
+ const blacklistParse = await this.parseData(myBlacklistInstances[i].instances);
656
+ // push devices in list to ignor device in lists
657
+ if (myBlacklistInstances[i].checkIgnorLists) {
658
+ this.blacklistInstancesLists.push(blacklistParse.path);
659
+ }
660
+ // push devices in list to ignor device in notifications
661
+ if (myBlacklistInstances[i].checkIgnorNotify) {
662
+ this.blacklistInstancesNotify.push(blacklistParse.path);
663
+ }
664
+ } catch (error) {
665
+ this.errorReporting('[createBlacklist]', error);
666
+ }
667
+ }
668
+
669
+ if (this.blacklistInstancesLists.length >= 1) this.log.info(`Found items on blacklist for lists: ${this.blacklistInstancesLists}`);
670
+ if (this.blacklistInstancesNotify.length >= 1) this.log.info(`Found items on blacklist for notificatioons: ${this.blacklistInstancesNotify}`);
671
+
508
672
  this.log.debug(`Function finished: ${this.createBlacklist.name}`);
509
673
  }
510
674
 
@@ -521,9 +685,9 @@ class DeviceWatcher extends utils.Adapter {
521
685
  = get Instanz =
522
686
  =============================================*/
523
687
  const instance = id.slice(0, id.indexOf('.') + 2);
524
- const instanceAliveDP = `system.adapter.${instance}.alive`;
525
- const instanceAlive = await this.getInitValue(instanceAliveDP);
526
- this.subscribeForeignStates(instanceAliveDP);
688
+ const instanceDeviceConnectionDP = `${instance}.info.connection`;
689
+ const instancedeviceConnected = await this.getInitValue(instanceDeviceConnectionDP);
690
+ this.subscribeForeignStates(instanceDeviceConnectionDP);
527
691
 
528
692
  /*=============================================
529
693
  = Get device name =
@@ -689,40 +853,42 @@ class DeviceWatcher extends utils.Adapter {
689
853
  =============================================*/
690
854
 
691
855
  /* Add only devices with battery in the rawlist */
692
- if (this.listOnlyBattery && isBatteryDevice) {
693
- this.listAllDevicesRaw.push({
694
- Path: id,
695
- instanceAliveDP: instanceAliveDP,
696
- instanceAlive: instanceAlive,
697
- Device: deviceName,
698
- adapterID: adapterID,
699
- Adapter: adapter,
700
- timeSelector: timeSelector,
701
- isBatteryDevice: isBatteryDevice,
702
- Battery: batteryHealth,
703
- BatteryRaw: batteryHealthRaw,
704
- batteryDP: deviceBatteryStateDP,
705
- LowBat: lowBatIndicator,
706
- LowBatDP: isLowBatDP,
707
- faultReport: faultReportingState,
708
- faultReportDP: faultReportingDP,
709
- SignalStrengthDP: deviceQualityDP,
710
- SignalStrength: linkQuality,
711
- UnreachState: deviceUnreachState,
712
- UnreachDP: unreachDP,
713
- DeviceStateSelectorDP: deviceStateSelectorDP,
714
- rssiPeerSelectorDP: rssiPeerSelectorDP,
715
- LastContact: lastContactString,
716
- Status: deviceState,
717
- UpdateDP: deviceUpdateDP,
718
- Upgradable: isUpgradable,
719
- });
856
+ if (this.listOnlyBattery) {
857
+ if (isBatteryDevice) {
858
+ this.listAllDevicesRaw.push({
859
+ Path: id,
860
+ instanceDeviceConnectionDP: instanceDeviceConnectionDP,
861
+ instancedeviceConnected: instancedeviceConnected,
862
+ Device: deviceName,
863
+ adapterID: adapterID,
864
+ Adapter: adapter,
865
+ timeSelector: timeSelector,
866
+ isBatteryDevice: isBatteryDevice,
867
+ Battery: batteryHealth,
868
+ BatteryRaw: batteryHealthRaw,
869
+ batteryDP: deviceBatteryStateDP,
870
+ LowBat: lowBatIndicator,
871
+ LowBatDP: isLowBatDP,
872
+ faultReport: faultReportingState,
873
+ faultReportDP: faultReportingDP,
874
+ SignalStrengthDP: deviceQualityDP,
875
+ SignalStrength: linkQuality,
876
+ UnreachState: deviceUnreachState,
877
+ UnreachDP: unreachDP,
878
+ DeviceStateSelectorDP: deviceStateSelectorDP,
879
+ rssiPeerSelectorDP: rssiPeerSelectorDP,
880
+ LastContact: lastContactString,
881
+ Status: deviceState,
882
+ UpdateDP: deviceUpdateDP,
883
+ Upgradable: isUpgradable,
884
+ });
885
+ }
720
886
  } else {
721
887
  /* Add all devices */
722
888
  this.listAllDevicesRaw.push({
723
889
  Path: id,
724
- instanceAliveDP: instanceAliveDP,
725
- instanceAlive: instanceAlive,
890
+ instanceDeviceConnectionDP: instanceDeviceConnectionDP,
891
+ instancedeviceConnected: instancedeviceConnected,
726
892
  Device: deviceName,
727
893
  adapterID: adapterID,
728
894
  Adapter: adapter,
@@ -1209,24 +1375,26 @@ class DeviceWatcher extends utils.Adapter {
1209
1375
  */
1210
1376
  async checkLastContact() {
1211
1377
  for (const device of this.listAllDevicesRaw) {
1212
- const oldContactState = device.Status;
1213
- device.UnreachState = await this.getInitValue(device.UnreachDP);
1214
- const contactData = await this.getOnlineState(
1215
- device.timeSelector,
1216
- device.adapterID,
1217
- device.UnreachDP,
1218
- device.SignalStrength,
1219
- device.UnreachState,
1220
- device.DeviceStateSelectorDP,
1221
- device.rssiPeerSelectorDP,
1222
- );
1223
- if (contactData !== undefined) {
1224
- device.LastContact = contactData[0];
1225
- device.Status = contactData[1];
1226
- device.linkQuality = contactData[2];
1227
- }
1228
- if (this.config.checkSendOfflineMsg && oldContactState !== device.Status && !this.blacklistNotify.includes(device.Path)) {
1229
- await this.sendOfflineNotifications(device.Device, device.Adapter, device.Status, device.LastContact);
1378
+ if (device.instancedeviceConnected !== false) {
1379
+ const oldContactState = device.Status;
1380
+ device.UnreachState = await this.getInitValue(device.UnreachDP);
1381
+ const contactData = await this.getOnlineState(
1382
+ device.timeSelector,
1383
+ device.adapterID,
1384
+ device.UnreachDP,
1385
+ device.SignalStrength,
1386
+ device.UnreachState,
1387
+ device.DeviceStateSelectorDP,
1388
+ device.rssiPeerSelectorDP,
1389
+ );
1390
+ if (contactData !== undefined) {
1391
+ device.LastContact = contactData[0];
1392
+ device.Status = contactData[1];
1393
+ device.linkQuality = contactData[2];
1394
+ }
1395
+ if (this.config.checkSendOfflineMsg && oldContactState !== device.Status && !this.blacklistNotify.includes(device.Path)) {
1396
+ await this.sendOfflineNotifications(device.Device, device.Adapter, device.Status, device.LastContact);
1397
+ }
1230
1398
  }
1231
1399
  }
1232
1400
  }
@@ -1438,18 +1606,18 @@ class DeviceWatcher extends utils.Adapter {
1438
1606
  }
1439
1607
 
1440
1608
  // Write Datapoints for counts
1441
- await this.setStateAsync(`${dpSubFolder}offlineCount`, { val: this.offlineDevicesCount, ack: true });
1442
- await this.setStateAsync(`${dpSubFolder}countAll`, { val: this.deviceCounter, ack: true });
1443
- await this.setStateAsync(`${dpSubFolder}batteryCount`, { val: this.batteryPoweredCount, ack: true });
1444
- await this.setStateAsync(`${dpSubFolder}lowBatteryCount`, { val: this.lowBatteryPoweredCount, ack: true });
1445
- await this.setStateAsync(`${dpSubFolder}upgradableCount`, { val: this.upgradableDevicesCount, ack: true });
1609
+ await this.setStateAsync(`devices.${dpSubFolder}offlineCount`, { val: this.offlineDevicesCount, ack: true });
1610
+ await this.setStateAsync(`devices.${dpSubFolder}countAll`, { val: this.deviceCounter, ack: true });
1611
+ await this.setStateAsync(`devices.${dpSubFolder}batteryCount`, { val: this.batteryPoweredCount, ack: true });
1612
+ await this.setStateAsync(`devices.${dpSubFolder}lowBatteryCount`, { val: this.lowBatteryPoweredCount, ack: true });
1613
+ await this.setStateAsync(`devices.${dpSubFolder}upgradableCount`, { val: this.upgradableDevicesCount, ack: true });
1446
1614
 
1447
1615
  // List all devices
1448
1616
  if (this.deviceCounter === 0) {
1449
1617
  // if no device is count, write the JSON List with default value
1450
1618
  this.listAllDevices = [{ Device: '--none--', Adapter: '', Battery: '', 'Last contact': '', 'Signal strength': '' }];
1451
1619
  }
1452
- await this.setStateAsync(`${dpSubFolder}listAll`, { val: JSON.stringify(this.listAllDevices), ack: true });
1620
+ await this.setStateAsync(`devices.${dpSubFolder}listAll`, { val: JSON.stringify(this.listAllDevices), ack: true });
1453
1621
 
1454
1622
  // List link quality
1455
1623
  if (this.linkQualityCount === 0) {
@@ -1457,7 +1625,7 @@ class DeviceWatcher extends utils.Adapter {
1457
1625
  this.linkQualityDevices = [{ Device: '--none--', Adapter: '', 'Signal strength': '' }];
1458
1626
  }
1459
1627
  //write JSON list
1460
- await this.setStateAsync(`${dpSubFolder}linkQualityList`, {
1628
+ await this.setStateAsync(`devices.${dpSubFolder}linkQualityList`, {
1461
1629
  val: JSON.stringify(this.linkQualityDevices),
1462
1630
  ack: true,
1463
1631
  });
@@ -1468,7 +1636,7 @@ class DeviceWatcher extends utils.Adapter {
1468
1636
  this.offlineDevices = [{ Device: '--none--', Adapter: '', 'Last contact': '' }];
1469
1637
  }
1470
1638
  //write JSON list
1471
- await this.setStateAsync(`${dpSubFolder}offlineList`, {
1639
+ await this.setStateAsync(`devices.${dpSubFolder}offlineList`, {
1472
1640
  val: JSON.stringify(this.offlineDevices),
1473
1641
  ack: true,
1474
1642
  });
@@ -1479,7 +1647,7 @@ class DeviceWatcher extends utils.Adapter {
1479
1647
  this.upgradableList = [{ Device: '--none--', Adapter: '', 'Last contact': '' }];
1480
1648
  }
1481
1649
  //write JSON list
1482
- await this.setStateAsync(`${dpSubFolder}upgradableList`, {
1650
+ await this.setStateAsync(`devices.${dpSubFolder}upgradableList`, {
1483
1651
  val: JSON.stringify(this.upgradableList),
1484
1652
  ack: true,
1485
1653
  });
@@ -1490,7 +1658,7 @@ class DeviceWatcher extends utils.Adapter {
1490
1658
  this.batteryPowered = [{ Device: '--none--', Adapter: '', Battery: '' }];
1491
1659
  }
1492
1660
  //write JSON list
1493
- await this.setStateAsync(`${dpSubFolder}batteryList`, {
1661
+ await this.setStateAsync(`devices.${dpSubFolder}batteryList`, {
1494
1662
  val: JSON.stringify(this.batteryPowered),
1495
1663
  ack: true,
1496
1664
  });
@@ -1501,43 +1669,43 @@ class DeviceWatcher extends utils.Adapter {
1501
1669
  this.batteryLowPowered = [{ Device: '--none--', Adapter: '', Battery: '' }];
1502
1670
  }
1503
1671
  //write JSON list
1504
- await this.setStateAsync(`${dpSubFolder}lowBatteryList`, {
1672
+ await this.setStateAsync(`devices.${dpSubFolder}lowBatteryList`, {
1505
1673
  val: JSON.stringify(this.batteryLowPowered),
1506
1674
  ack: true,
1507
1675
  });
1508
1676
 
1509
1677
  // set booleans datapoints
1510
1678
  if (this.offlineDevicesCount === 0) {
1511
- await this.setStateAsync(`${dpSubFolder}oneDeviceOffline`, {
1679
+ await this.setStateAsync(`devices.${dpSubFolder}oneDeviceOffline`, {
1512
1680
  val: false,
1513
1681
  ack: true,
1514
1682
  });
1515
1683
  } else {
1516
- await this.setStateAsync(`${dpSubFolder}oneDeviceOffline`, {
1684
+ await this.setStateAsync(`devices.${dpSubFolder}oneDeviceOffline`, {
1517
1685
  val: true,
1518
1686
  ack: true,
1519
1687
  });
1520
1688
  }
1521
1689
 
1522
1690
  if (this.lowBatteryPoweredCount === 0) {
1523
- await this.setStateAsync(`${dpSubFolder}oneDeviceLowBat`, {
1691
+ await this.setStateAsync(`devices.${dpSubFolder}oneDeviceLowBat`, {
1524
1692
  val: false,
1525
1693
  ack: true,
1526
1694
  });
1527
1695
  } else {
1528
- await this.setStateAsync(`${dpSubFolder}oneDeviceLowBat`, {
1696
+ await this.setStateAsync(`devices.${dpSubFolder}oneDeviceLowBat`, {
1529
1697
  val: true,
1530
1698
  ack: true,
1531
1699
  });
1532
1700
  }
1533
1701
 
1534
1702
  if (this.upgradableDevicesCount === 0) {
1535
- await this.setStateAsync(`${dpSubFolder}oneDeviceUpdatable`, {
1703
+ await this.setStateAsync(`devices.${dpSubFolder}oneDeviceUpdatable`, {
1536
1704
  val: false,
1537
1705
  ack: true,
1538
1706
  });
1539
1707
  } else {
1540
- await this.setStateAsync(`${dpSubFolder}oneDeviceUpdatable`, {
1708
+ await this.setStateAsync(`devices.${dpSubFolder}oneDeviceUpdatable`, {
1541
1709
  val: true,
1542
1710
  ack: true,
1543
1711
  });
@@ -1545,19 +1713,19 @@ class DeviceWatcher extends utils.Adapter {
1545
1713
 
1546
1714
  //write HTML list
1547
1715
  if (this.createHtmlList) {
1548
- await this.setStateAsync(`${dpSubFolder}linkQualityListHTML`, {
1716
+ await this.setStateAsync(`devices.${dpSubFolder}linkQualityListHTML`, {
1549
1717
  val: await this.creatLinkQualityListHTML(this.linkQualityDevices, this.linkQualityCount),
1550
1718
  ack: true,
1551
1719
  });
1552
- await this.setStateAsync(`${dpSubFolder}offlineListHTML`, {
1720
+ await this.setStateAsync(`devices.${dpSubFolder}offlineListHTML`, {
1553
1721
  val: await this.createOfflineListHTML(this.offlineDevices, this.offlineDevicesCount),
1554
1722
  ack: true,
1555
1723
  });
1556
- await this.setStateAsync(`${dpSubFolder}batteryListHTML`, {
1724
+ await this.setStateAsync(`devices.${dpSubFolder}batteryListHTML`, {
1557
1725
  val: await this.createBatteryListHTML(this.batteryPowered, this.batteryPoweredCount, false),
1558
1726
  ack: true,
1559
1727
  });
1560
- await this.setStateAsync(`${dpSubFolder}lowBatteryListHTML`, {
1728
+ await this.setStateAsync(`devices.${dpSubFolder}lowBatteryListHTML`, {
1561
1729
  val: await this.createBatteryListHTML(this.batteryLowPowered, this.lowBatteryPoweredCount, true),
1562
1730
  ack: true,
1563
1731
  });
@@ -1572,6 +1740,534 @@ class DeviceWatcher extends utils.Adapter {
1572
1740
  this.log.debug(`Function finished: ${this.writeDatapoints.name}`);
1573
1741
  } //<--End of writing Datapoints
1574
1742
 
1743
+ /**
1744
+ * get Instances
1745
+ */
1746
+ async getInstanceData() {
1747
+ try {
1748
+ await this.createDPsForInstances();
1749
+
1750
+ const instanceAliveDP = await this.getForeignStatesAsync(`system.adapter.*.alive`);
1751
+ for (const [id] of Object.entries(instanceAliveDP)) {
1752
+ if (!(typeof id === 'string' && id.startsWith(`system.adapter.`))) continue;
1753
+
1754
+ // get instance name
1755
+ const instanceName = await this.getInstanceName(id);
1756
+
1757
+ // get instance connected to host data
1758
+ const instanceConnectedHostDP = `system.adapter.${instanceName}.connected`;
1759
+ const instanceConnectedHostVal = await this.getInitValue(instanceConnectedHostDP);
1760
+
1761
+ // get instance connected to device data
1762
+ const instanceConnectedDeviceDP = `${instanceName}.info.connection`;
1763
+ let instanceConnectedDeviceVal;
1764
+ if (instanceConnectedDeviceDP !== undefined && typeof instanceConnectedDeviceDP === 'boolean') {
1765
+ instanceConnectedDeviceVal = await this.getInitValue(instanceConnectedDeviceDP);
1766
+ } else {
1767
+ instanceConnectedDeviceVal = 'N/A';
1768
+ }
1769
+
1770
+ // get adapter version
1771
+ const instanceObjectPath = `system.adapter.${instanceName}`;
1772
+ let adapterName;
1773
+ let adapterVersion;
1774
+ let instanceMode;
1775
+ let scheduleTime = 'N/A';
1776
+ const instanceObjectData = await this.getForeignObjectAsync(instanceObjectPath);
1777
+ if (instanceObjectData) {
1778
+ // @ts-ignore
1779
+ adapterName = this.capitalize(instanceObjectData.common.name);
1780
+ adapterVersion = instanceObjectData.common.version;
1781
+ instanceMode = instanceObjectData.common.mode;
1782
+
1783
+ if (instanceMode === 'schedule') {
1784
+ scheduleTime = instanceObjectData.common.schedule;
1785
+ }
1786
+ }
1787
+
1788
+ //const adapterVersionVal = await this.getInitValue(adapterVersionDP);
1789
+ const instanceStatusRaw = await this.setInstanceStatus(instanceMode, scheduleTime, id, instanceAliveDP[id].val, instanceConnectedHostVal, instanceConnectedDeviceVal);
1790
+ const isAlive = instanceStatusRaw[1];
1791
+ const instanceStatus = instanceStatusRaw[0];
1792
+ const isHealthy = instanceStatusRaw[2];
1793
+
1794
+ //subscribe to statechanges
1795
+ this.subscribeForeignStatesAsync(id);
1796
+ this.subscribeForeignStatesAsync(instanceConnectedHostDP);
1797
+ this.subscribeForeignStatesAsync(instanceConnectedDeviceDP);
1798
+ //this.subscribeForeignObjectsAsync(instanceObjectPath);
1799
+
1800
+ // create raw list
1801
+ this.listInstanceRaw.push({
1802
+ Adapter: adapterName,
1803
+ InstanceName: instanceName,
1804
+ instanceObjectPath: instanceObjectPath,
1805
+ instanceAlivePath: id,
1806
+ instanceMode: instanceMode,
1807
+ schedule: scheduleTime,
1808
+ adapterVersion: adapterVersion,
1809
+ isAlive: isAlive,
1810
+ isHealthy: isHealthy,
1811
+ connectedHostPath: instanceConnectedHostDP,
1812
+ isConnectedHost: instanceConnectedHostVal,
1813
+ connectedDevicePath: instanceConnectedDeviceDP,
1814
+ isConnectedDevice: instanceConnectedDeviceVal,
1815
+ status: instanceStatus,
1816
+ });
1817
+ }
1818
+ await this.createInstanceList();
1819
+ await this.writeInstanceDPs();
1820
+ } catch (error) {
1821
+ this.errorReporting('[getInstance]', error);
1822
+ }
1823
+ }
1824
+
1825
+ /**
1826
+ * get Instances
1827
+ * @param {string} id - Path of alive datapoint
1828
+ */
1829
+ async getInstanceName(id) {
1830
+ let instance = id;
1831
+ instance = instance.slice(15); // remove "system.adapter."
1832
+ instance = instance.slice(0, instance.lastIndexOf('.') + 1 - 1); // remove ".alive"
1833
+ return instance;
1834
+ }
1835
+
1836
+ /**
1837
+ * set status for instance
1838
+ * @param {object} instanceMode
1839
+ * @param {object} scheduleTime
1840
+ * @param {object} instanceAlivePath
1841
+ * @param {object} isAliveVal
1842
+ * @param {object} connectedHostVal
1843
+ * @param {object} connectedDeviceVal
1844
+ */
1845
+ async setInstanceStatus(instanceMode, scheduleTime, instanceAlivePath, isAliveVal, connectedHostVal, connectedDeviceVal) {
1846
+ let instanceStatusString = 'not enabled';
1847
+ let lastUpdate;
1848
+ let lastCronRun;
1849
+ let diff;
1850
+ let previousCronRun = null;
1851
+ let isAlive = false;
1852
+ let isHealthy = false;
1853
+ let dpValue;
1854
+ switch (instanceMode) {
1855
+ case 'schedule':
1856
+ dpValue = await this.getForeignStateAsync(instanceAlivePath);
1857
+ if (dpValue) {
1858
+ lastUpdate = Math.round((Date.now() - dpValue.lc) / 1000); // Last state change in seconds
1859
+ previousCronRun = await this.getPreviousCronRun(scheduleTime); // When was the last cron run
1860
+ if (previousCronRun) {
1861
+ lastCronRun = Math.round(previousCronRun / 1000); // change distance to last run in seconds
1862
+ diff = lastCronRun - lastUpdate;
1863
+ if (diff > -300) {
1864
+ // if 5 minutes difference exceeded, instance is not alive
1865
+ isAlive = true;
1866
+ isHealthy = true;
1867
+ instanceStatusString = 'Instance okay';
1868
+ }
1869
+ }
1870
+ }
1871
+ break;
1872
+ case 'daemon':
1873
+ if (!isAliveVal) return ['Instance deactivated', false, null]; // if instance is turned off
1874
+
1875
+ // In case of (re)start, connection may take some time. We take 3 attempts.
1876
+ // Attempt 1/3 - immediately
1877
+ if (connectedHostVal && connectedDeviceVal) {
1878
+ isAlive = true;
1879
+ isHealthy = true;
1880
+ instanceStatusString = 'Instance okay';
1881
+ } else {
1882
+ // Attempt 2/3 - after 10 seconds
1883
+ await this.wait(10000);
1884
+ if (connectedHostVal && connectedDeviceVal) {
1885
+ isAlive = true;
1886
+ isHealthy = true;
1887
+ instanceStatusString = 'Instance okay';
1888
+ } else {
1889
+ // Attempt 3/3 - after 20 seconds in total
1890
+ await this.wait(10000);
1891
+ if (connectedHostVal && connectedDeviceVal) {
1892
+ isAlive = true;
1893
+ isHealthy = true;
1894
+ instanceStatusString = 'Instance okay';
1895
+ } else {
1896
+ if (!connectedDeviceVal) {
1897
+ instanceStatusString = 'not connected to Device';
1898
+ isAlive = true;
1899
+ isHealthy = false;
1900
+ } else if (!connectedHostVal) {
1901
+ instanceStatusString = 'not connected to host';
1902
+ isAlive = true;
1903
+ isHealthy = false;
1904
+ }
1905
+ }
1906
+ }
1907
+ }
1908
+ break;
1909
+ }
1910
+
1911
+ return [instanceStatusString, isAlive, isHealthy];
1912
+ }
1913
+
1914
+ async createAdapterUpdateData() {
1915
+ const adapterUpdateListDP = `admin.*.info.updatesJson`;
1916
+ const adapterUpdatesListVal = await this.getForeignStatesAsync(adapterUpdateListDP);
1917
+
1918
+ // subscribe to datapoint
1919
+ this.subscribeForeignStates(adapterUpdateListDP);
1920
+ let adapterJsonList;
1921
+
1922
+ for (const [id] of Object.entries(adapterUpdatesListVal)) {
1923
+ adapterJsonList = await this.parseData(adapterUpdatesListVal[id].val);
1924
+ }
1925
+
1926
+ for (const [id] of Object.entries(adapterJsonList)) {
1927
+ this.adapterUpdatesJsonRaw.push({
1928
+ Path: adapterUpdateListDP,
1929
+ Adapter: this.capitalize(id),
1930
+ newVersion: adapterJsonList[id].availableVersion,
1931
+ oldVersion: adapterJsonList[id].installedVersion,
1932
+ });
1933
+ }
1934
+ await this.createAdapterUpdateList();
1935
+ }
1936
+
1937
+ /**
1938
+ * create instanceList
1939
+ */
1940
+ async createAdapterUpdateList() {
1941
+ this.listAdapterUpdates = [];
1942
+ this.countAdapterUpdates = 0;
1943
+
1944
+ for (const adapter of this.adapterUpdatesJsonRaw) {
1945
+ this.listAdapterUpdates.push({
1946
+ Adapter: adapter.Adapter,
1947
+ 'Available Version': adapter.newVersion,
1948
+ 'Installed Version': adapter.oldVersion,
1949
+ });
1950
+ }
1951
+ this.countAdapterUpdates = this.listAdapterUpdates.length;
1952
+ await this.writeAdapterUpdatesDPs();
1953
+ }
1954
+
1955
+ /**
1956
+ * write datapoints for adapter with updates
1957
+ */
1958
+ async writeAdapterUpdatesDPs() {
1959
+ // Write Datapoints for counts
1960
+ await this.setStateAsync(`adapterAndInstances.countAdapterUpdates`, { val: this.countAdapterUpdates, ack: true });
1961
+
1962
+ // list deactivated instances
1963
+ if (this.countAdapterUpdates === 0) {
1964
+ this.listAdapterUpdates = [{ Adapter: '--none--', 'Available Version': '', 'Installed Version': '' }];
1965
+ }
1966
+ await this.setStateAsync(`adapterAndInstances.listAdapterUpdates`, { val: JSON.stringify(this.listAdapterUpdates), ack: true });
1967
+ }
1968
+
1969
+ /**
1970
+ * create instanceList
1971
+ */
1972
+ async createInstanceList() {
1973
+ this.listAllInstances = [];
1974
+ this.listDeactivatedInstances = [];
1975
+ this.listErrorInstanceRaw = [];
1976
+ this.listErrorInstance = [];
1977
+
1978
+ for (const instance of this.listInstanceRaw) {
1979
+ // fill raw list
1980
+ if (instance.isAlive && !instance.isHealthy) {
1981
+ this.listErrorInstanceRaw.push({
1982
+ Adapter: instance.Adapter,
1983
+ Instance: instance.InstanceName,
1984
+ Mode: instance.instanceMode,
1985
+ Status: instance.status,
1986
+ });
1987
+ }
1988
+
1989
+ if (this.blacklistInstancesLists.includes(instance.instanceAlivePath)) continue;
1990
+ this.listAllInstances.push({
1991
+ Adapter: instance.Adapter,
1992
+ Instance: instance.InstanceName,
1993
+ Mode: instance.instanceMode,
1994
+ Schedule: instance.schedule,
1995
+ Version: instance.adapterVersion,
1996
+ Status: instance.status,
1997
+ });
1998
+ if (!instance.isAlive) {
1999
+ this.listDeactivatedInstances.push({
2000
+ Adapter: instance.Adapter,
2001
+ Instance: instance.InstanceName,
2002
+ Status: instance.status,
2003
+ });
2004
+ }
2005
+
2006
+ // fill List for User
2007
+ if (instance.isAlive && !instance.isHealthy) {
2008
+ this.listErrorInstance.push({
2009
+ Adapter: instance.Adapter,
2010
+ Instance: instance.InstanceName,
2011
+ Mode: instance.instanceMode,
2012
+ Status: instance.status,
2013
+ });
2014
+ }
2015
+ }
2016
+ await this.countInstances();
2017
+ }
2018
+
2019
+ /**
2020
+ * count instanceList
2021
+ */
2022
+ async countInstances() {
2023
+ this.countAllInstances = 0;
2024
+ this.countDeactivatedInstances = 0;
2025
+ this.countErrorInstance = 0;
2026
+
2027
+ this.countAllInstances = this.listAllInstances.length;
2028
+ this.countDeactivatedInstances = this.listDeactivatedInstances.length;
2029
+ this.countErrorInstance = this.listErrorInstance.length;
2030
+ }
2031
+
2032
+ /**
2033
+ * write datapoints for instances list and counts
2034
+ */
2035
+ async writeInstanceDPs() {
2036
+ // Write Datapoints for counts
2037
+ await this.setStateAsync(`adapterAndInstances.countAllInstances`, { val: this.countAllInstances, ack: true });
2038
+ await this.setStateAsync(`adapterAndInstances.countDeactivatedInstances`, { val: this.countDeactivatedInstances, ack: true });
2039
+
2040
+ // List all instances
2041
+ await this.setStateAsync(`adapterAndInstances.listAllInstances`, { val: JSON.stringify(this.listAllInstances), ack: true });
2042
+
2043
+ // list deactivated instances
2044
+ if (this.countDeactivatedInstances === 0) {
2045
+ this.listDeactivatedInstances = [{ Instance: '--none--', Version: '', Status: '' }];
2046
+ }
2047
+ await this.setStateAsync(`adapterAndInstances.listDeactivatedInstances`, { val: JSON.stringify(this.listDeactivatedInstances), ack: true });
2048
+ await this.setStateAsync(`adapterAndInstances.countDeactivatedInstances`, { val: this.countDeactivatedInstances, ack: true });
2049
+
2050
+ // list error instances
2051
+ if (this.countErrorInstance === 0) {
2052
+ this.listErrorInstance = [{ Instance: '--none--', Mode: '', Status: '' }];
2053
+ }
2054
+ await this.setStateAsync(`adapterAndInstances.listInstancesError`, { val: JSON.stringify(this.listErrorInstance), ack: true });
2055
+ await this.setStateAsync(`adapterAndInstances.countInstancesError`, { val: this.countErrorInstance, ack: true });
2056
+ }
2057
+
2058
+ /**
2059
+ * create Datapoints for Instances
2060
+ */
2061
+ async createDPsForInstances() {
2062
+ await this.setObjectNotExistsAsync(`adapterAndInstances`, {
2063
+ type: 'channel',
2064
+ common: {
2065
+ name: {
2066
+ en: 'Adapter and Instances',
2067
+ de: 'Adapter und Instanzen',
2068
+ ru: 'Адаптер и Instances',
2069
+ pt: 'Adaptador e instâncias',
2070
+ nl: 'Adapter en Instance',
2071
+ fr: 'Adaptateur et instances',
2072
+ it: 'Adattatore e istanze',
2073
+ es: 'Adaptador e instalaciones',
2074
+ pl: 'Adapter and Instances',
2075
+ uk: 'Адаптер та інстанції',
2076
+ 'zh-cn': '道歉和案',
2077
+ },
2078
+ },
2079
+ native: {},
2080
+ });
2081
+
2082
+ // Instances
2083
+ await this.setObjectNotExistsAsync(`adapterAndInstances.listAllInstances`, {
2084
+ type: 'state',
2085
+ common: {
2086
+ name: {
2087
+ en: 'JSON List of all instances',
2088
+ de: 'JSON Liste aller Instanzen',
2089
+ ru: 'ДЖСОН Список всех инстанций',
2090
+ pt: 'J. Lista de todas as instâncias',
2091
+ nl: 'JSON List van alle instanties',
2092
+ fr: 'JSON Liste de tous les cas',
2093
+ it: 'JSON Elenco di tutte le istanze',
2094
+ es: 'JSON Lista de todos los casos',
2095
+ pl: 'JSON Lista wszystkich instancji',
2096
+ uk: 'Сонце Список всіх екземплярів',
2097
+ 'zh-cn': '附 件 所有事例一览表',
2098
+ },
2099
+ type: 'array',
2100
+ role: 'json',
2101
+ read: true,
2102
+ write: false,
2103
+ },
2104
+ native: {},
2105
+ });
2106
+ await this.setObjectNotExistsAsync(`adapterAndInstances.countAllInstances`, {
2107
+ type: 'state',
2108
+ common: {
2109
+ name: {
2110
+ en: 'Number of all instances',
2111
+ de: 'Anzahl aller Instanzen',
2112
+ ru: 'Количество всех инстанций',
2113
+ pt: 'Número de todas as instâncias',
2114
+ nl: 'Nummer van alle gevallen',
2115
+ fr: 'Nombre de cas',
2116
+ it: 'Numero di tutte le istanze',
2117
+ es: 'Número de casos',
2118
+ pl: 'Liczba wszystkich instancji',
2119
+ uk: 'Кількість всіх екземплярів',
2120
+ 'zh-cn': '各类案件数目',
2121
+ },
2122
+ type: 'number',
2123
+ role: 'value',
2124
+ read: true,
2125
+ write: false,
2126
+ },
2127
+ native: {},
2128
+ });
2129
+ await this.setObjectNotExistsAsync(`adapterAndInstances.listDeactivatedInstances`, {
2130
+ type: 'state',
2131
+ common: {
2132
+ name: {
2133
+ en: 'JSON List of deactivated instances',
2134
+ de: 'JSON Liste der deaktivierten Instanzen',
2135
+ ru: 'ДЖСОН Список деактивированных инстанций',
2136
+ pt: 'J. Lista de instâncias desativadas',
2137
+ nl: 'JSON List van gedeactiveerde instanties',
2138
+ fr: 'JSON Liste des cas désactivés',
2139
+ it: 'JSON Elenco delle istanze disattivate',
2140
+ es: 'JSON Lista de casos desactivados',
2141
+ pl: 'JSON Lista przypadków deaktywowanych',
2142
+ uk: 'Сонце Перелік деактивованих екземплярів',
2143
+ 'zh-cn': '附 件 被动事例清单',
2144
+ },
2145
+ type: 'array',
2146
+ role: 'json',
2147
+ read: true,
2148
+ write: false,
2149
+ },
2150
+ native: {},
2151
+ });
2152
+ await this.setObjectNotExistsAsync(`adapterAndInstances.countDeactivatedInstances`, {
2153
+ type: 'state',
2154
+ common: {
2155
+ name: {
2156
+ en: 'Number of deactivated instances',
2157
+ de: 'Anzahl deaktivierter Instanzen',
2158
+ ru: 'Количество деактивированных инстанций',
2159
+ pt: 'Número de instâncias desativadas',
2160
+ nl: 'Nummer van gedeactiveerde instanties',
2161
+ fr: 'Nombre de cas désactivés',
2162
+ it: 'Numero di istanze disattivate',
2163
+ es: 'Número de casos desactivados',
2164
+ pl: 'Liczba deaktywowanych instancji',
2165
+ uk: 'Кількість деактивованих екземплярів',
2166
+ 'zh-cn': 'A. 递解事件的数目',
2167
+ },
2168
+ type: 'number',
2169
+ role: 'value',
2170
+ read: true,
2171
+ write: false,
2172
+ },
2173
+ native: {},
2174
+ });
2175
+ await this.setObjectNotExistsAsync(`adapterAndInstances.listInstancesError`, {
2176
+ type: 'state',
2177
+ common: {
2178
+ name: {
2179
+ en: 'JSON list of instances with error',
2180
+ de: 'JSON-Liste von Instanzen mit Fehler',
2181
+ ru: 'JSON список инстанций с ошибкой',
2182
+ pt: 'Lista de instâncias JSON com erro',
2183
+ nl: 'JSON lijst met fouten',
2184
+ fr: 'Liste des instances avec erreur',
2185
+ it: 'Elenco JSON delle istanze con errore',
2186
+ es: 'JSON lista de casos con error',
2187
+ pl: 'Lista błędów JSON',
2188
+ uk: 'JSON список екземплярів з помилкою',
2189
+ 'zh-cn': '联合工作组办公室错误事件清单',
2190
+ },
2191
+ type: 'array',
2192
+ role: 'json',
2193
+ read: true,
2194
+ write: false,
2195
+ },
2196
+ native: {},
2197
+ });
2198
+ await this.setObjectNotExistsAsync(`adapterAndInstances.countInstancesError`, {
2199
+ type: 'state',
2200
+ common: {
2201
+ name: {
2202
+ en: 'Count of instances with error',
2203
+ de: 'Anzahl der Instanzen mit Fehler',
2204
+ ru: 'Количество инстанций с ошибкой',
2205
+ pt: 'Contagem de instâncias com erro',
2206
+ nl: 'Graaf van instoringen met fouten',
2207
+ fr: 'Nombre de cas avec erreur',
2208
+ it: 'Conteggio di istanze con errore',
2209
+ es: 'Cuenta de casos con error',
2210
+ pl: 'Liczba przykładów w przypadku błędów',
2211
+ uk: 'Кількість екземплярів з помилкою',
2212
+ 'zh-cn': '发生错误的情况',
2213
+ },
2214
+ type: 'number',
2215
+ role: 'value',
2216
+ read: true,
2217
+ write: false,
2218
+ },
2219
+ native: {},
2220
+ });
2221
+
2222
+ // Adapter
2223
+ await this.setObjectNotExistsAsync(`adapterAndInstances.listAdapterUpdates`, {
2224
+ type: 'state',
2225
+ common: {
2226
+ name: {
2227
+ en: 'JSON list of adapters with available updates',
2228
+ de: 'JSON-Liste der Adapter mit verfügbaren Updates',
2229
+ ru: 'JSON список адаптеров с доступными обновлениями',
2230
+ pt: 'Lista de adaptadores JSON com atualizações disponíveis',
2231
+ nl: 'JSON lijst met beschikbare updates',
2232
+ fr: 'Liste JSON des adaptateurs avec mises à jour disponibles',
2233
+ it: 'Elenco di adattatori JSON con aggiornamenti disponibili',
2234
+ es: 'JSON lista de adaptadores con actualizaciones disponibles',
2235
+ pl: 'JSON lista adapterów z dostępnymi aktualizacjami',
2236
+ uk: 'JSON список адаптерів з доступними оновленнями',
2237
+ 'zh-cn': '附录A',
2238
+ },
2239
+ type: 'array',
2240
+ role: 'json',
2241
+ read: true,
2242
+ write: false,
2243
+ },
2244
+ native: {},
2245
+ });
2246
+ await this.setObjectNotExistsAsync(`adapterAndInstances.countAdapterUpdates`, {
2247
+ type: 'state',
2248
+ common: {
2249
+ name: {
2250
+ en: 'Number of adapters with available updates',
2251
+ de: 'Anzahl der Adapter mit verfügbaren Updates',
2252
+ ru: 'Количество адаптеров с доступными обновлениями',
2253
+ pt: 'Número de adaptadores com atualizações disponíveis',
2254
+ nl: 'Nummer van adapters met beschikbare updates',
2255
+ fr: "Nombre d'adaptateurs avec mises à jour disponibles",
2256
+ it: 'Numero di adattatori con aggiornamenti disponibili',
2257
+ es: 'Número de adaptadores con actualizaciones disponibles',
2258
+ pl: 'Liczba adapterów z dostępną aktualizacją',
2259
+ uk: 'Кількість адаптерів з доступними оновленнями',
2260
+ 'zh-cn': '更新的适应者人数',
2261
+ },
2262
+ type: 'number',
2263
+ role: 'value',
2264
+ read: true,
2265
+ write: false,
2266
+ },
2267
+ native: {},
2268
+ });
2269
+ }
2270
+
1575
2271
  /*=============================================
1576
2272
  = functions to send notifications =
1577
2273
  =============================================*/
@@ -1722,6 +2418,7 @@ class DeviceWatcher extends utils.Adapter {
1722
2418
  }
1723
2419
  } // <-- End of sendNotification function
1724
2420
 
2421
+ /*---------- Battery notifications ----------*/
1725
2422
  /**
1726
2423
  * send shedule message for low battery devices
1727
2424
  */
@@ -1805,6 +2502,7 @@ class DeviceWatcher extends utils.Adapter {
1805
2502
  this.log.debug(`Finished the function: ${this.sendLowBatNoticiation.name}`);
1806
2503
  }
1807
2504
 
2505
+ /*---------- Offline/Online notifications ----------*/
1808
2506
  /**
1809
2507
  * send message if an device is offline
1810
2508
  * @param {string} deviceName
@@ -1896,6 +2594,7 @@ class DeviceWatcher extends utils.Adapter {
1896
2594
  }
1897
2595
  } //<--End of daily offline notification
1898
2596
 
2597
+ /*---------- Device Updates notifications ----------*/
1899
2598
  /**
1900
2599
  * check if device updates are available and send notification
1901
2600
  * @param {string} deviceName
@@ -1928,7 +2627,7 @@ class DeviceWatcher extends utils.Adapter {
1928
2627
  /**
1929
2628
  * send shedule message with offline devices
1930
2629
  */
1931
- async sendUpgradeNotificationsShedule() {
2630
+ async sendDeviceUpdateNotificationsShedule() {
1932
2631
  const time = this.config.checkSendUpgradeTime.split(':');
1933
2632
 
1934
2633
  const checkDays = []; // list of selected days
@@ -1950,6 +2649,91 @@ class DeviceWatcher extends utils.Adapter {
1950
2649
  return; // cancel function if no day is selected
1951
2650
  }
1952
2651
 
2652
+ if (!isUnloaded) {
2653
+ const cron = '10 ' + time[1] + ' ' + time[0] + ' * * ' + checkDays;
2654
+ schedule.scheduleJob(cron, () => {
2655
+ try {
2656
+ let deviceList = '';
2657
+
2658
+ for (const id of this.upgradableDevicesRaw) {
2659
+ if (!this.blacklistNotify.includes(id.Path)) {
2660
+ if (!this.config.showAdapterNameinMsg) {
2661
+ deviceList = `${deviceList}\n${id.Device}`;
2662
+ } else {
2663
+ deviceList = `${deviceList}\n${id.Adapter}: ${id.Device}`;
2664
+ }
2665
+ }
2666
+ }
2667
+ if (deviceList.length > 0) {
2668
+ this.log.info(`Geräte Upgrade: ${deviceList}`);
2669
+ this.setStateAsync('lastNotification', `Geräte Upgrade: ${deviceList}`, true);
2670
+
2671
+ this.sendNotification(`Geräte Upgrade:\n${deviceList}`);
2672
+ }
2673
+ } catch (error) {
2674
+ this.errorReporting('[sendUpgradeNotificationsShedule]', error);
2675
+ }
2676
+ });
2677
+ }
2678
+ } //<--End of daily device updates notification
2679
+
2680
+ /*---------- Adapter Updates notifications ----------*/
2681
+ /**
2682
+ * check if adapter updates are available and send notification
2683
+ * @param {string} id
2684
+ * @param {ioBroker.State | null | undefined} state
2685
+ */
2686
+ async sendAdapterUpdatesNotification(id, state) {
2687
+ this.log.debug(`Start the function: ${this.sendAdapterUpdatesNotification.name}`);
2688
+
2689
+ try {
2690
+ if (state && state !== undefined) {
2691
+ const list = await this.parseData(state.val);
2692
+ let msg = '';
2693
+ let adapterList = '';
2694
+
2695
+ for (const [id] of Object.entries(list)) {
2696
+ adapterList = `${adapterList}\n${this.capitalize(id)} - Version: ${list[id].availableVersion}`;
2697
+ }
2698
+ if (adapterList.length !== 0) {
2699
+ msg = `Neue Adapter Updates vorhanden: \n`;
2700
+
2701
+ this.log.info(msg + adapterList);
2702
+ await this.setStateAsync('lastNotification', msg + adapterList, true);
2703
+ await this.sendNotification(msg + adapterList);
2704
+ }
2705
+ }
2706
+ } catch (error) {
2707
+ this.errorReporting('[sendAdapterUpdatesNotification]', error);
2708
+ }
2709
+ this.log.debug(`Finished the function: ${this.sendAdapterUpdatesNotification.name}`);
2710
+ }
2711
+
2712
+ /**
2713
+ * send shedule message with list of updatable adapters
2714
+ */
2715
+ async sendAdapterUpdatesNotificatioShedule() {
2716
+ const time = this.config.checkSendAdapterUpdateTime.split(':');
2717
+
2718
+ const checkDays = []; // list of selected days
2719
+
2720
+ // push the selected days in list
2721
+ if (this.config.checkAdapterUpdateMonday) checkDays.push(1);
2722
+ if (this.config.checkAdapterUpdateTuesday) checkDays.push(2);
2723
+ if (this.config.checkAdapterUpdateWednesday) checkDays.push(3);
2724
+ if (this.config.checkAdapterUpdateThursday) checkDays.push(4);
2725
+ if (this.config.checkAdapterUpdateFriday) checkDays.push(5);
2726
+ if (this.config.checkAdapterUpdateSaturday) checkDays.push(6);
2727
+ if (this.config.checkAdapterUpdateSunday) checkDays.push(0);
2728
+
2729
+ if (checkDays.length >= 1) {
2730
+ // check if an day is selected
2731
+ this.log.debug(`Number of selected days for daily adapter update message: ${checkDays.length}. Send Message on: ${checkDays.join(', ')} ...`);
2732
+ } else {
2733
+ this.log.warn(`No days selected for daily adapter update message. Please check the instance configuration!`);
2734
+ return; // cancel function if no day is selected
2735
+ }
2736
+
1953
2737
  if (!isUnloaded) {
1954
2738
  const cron = '10 ' + time[1] + ' ' + time[0] + ' * * ' + checkDays;
1955
2739
  schedule.scheduleJob(cron, () => {
@@ -1978,6 +2762,81 @@ class DeviceWatcher extends utils.Adapter {
1978
2762
  }
1979
2763
  } //<--End of daily offline notification
1980
2764
 
2765
+ /*---------- Instance error notifications ----------*/
2766
+ /**
2767
+ * check if device updates are available and send notification
2768
+ * @param {string} instanceName
2769
+ * @param {string} error
2770
+ **/
2771
+ async sendInstanceErrorNotification(instanceName, error) {
2772
+ this.log.debug(`Start the function: ${this.sendInstanceErrorNotification.name}`);
2773
+
2774
+ try {
2775
+ let msg = '';
2776
+ let instanceList = '';
2777
+
2778
+ instanceList = `${instanceList}\n${instanceName}: ${error}`;
2779
+
2780
+ msg = `Fehler einer Instanz entdeckt: \n`;
2781
+
2782
+ this.log.info(msg + instanceList);
2783
+ await this.setStateAsync('lastNotification', msg + instanceList, true);
2784
+ await this.sendNotification(msg + instanceList);
2785
+ } catch (error) {
2786
+ this.errorReporting('[sendInstanceErrorNotification]', error);
2787
+ }
2788
+ this.log.debug(`Finished the function: ${this.sendInstanceErrorNotification.name}`);
2789
+ }
2790
+
2791
+ /**
2792
+ * send shedule message with offline devices
2793
+ */
2794
+ async sendInstanceErrorNotificationShedule() {
2795
+ const time = this.config.checkSendInstanceFailedTime.split(':');
2796
+
2797
+ const checkDays = []; // list of selected days
2798
+
2799
+ // push the selected days in list
2800
+ if (this.config.checkFailedInstancesMonday) checkDays.push(1);
2801
+ if (this.config.checkFailedInstancesTuesday) checkDays.push(2);
2802
+ if (this.config.checkFailedInstancesWednesday) checkDays.push(3);
2803
+ if (this.config.checkFailedInstancesThursday) checkDays.push(4);
2804
+ if (this.config.checkFailedInstancesFriday) checkDays.push(5);
2805
+ if (this.config.checkFailedInstancesSaturday) checkDays.push(6);
2806
+ if (this.config.checkFailedInstancesSunday) checkDays.push(0);
2807
+
2808
+ if (checkDays.length >= 1) {
2809
+ // check if an day is selected
2810
+ this.log.debug(`Number of selected days for daily instance error message: ${checkDays.length}. Send Message on: ${checkDays.join(', ')} ...`);
2811
+ } else {
2812
+ this.log.warn(`No days selected for daily instance error message. Please check the instance configuration!`);
2813
+ return; // cancel function if no day is selected
2814
+ }
2815
+
2816
+ if (!isUnloaded) {
2817
+ const cron = '10 ' + time[1] + ' ' + time[0] + ' * * ' + checkDays;
2818
+ schedule.scheduleJob(cron, () => {
2819
+ try {
2820
+ let instanceList = '';
2821
+
2822
+ for (const id of this.listErrorInstanceRaw) {
2823
+ if (!this.blacklistInstancesNotify.includes(id.instanceAlivePath)) {
2824
+ instanceList = `${instanceList}\n${id.Instance}: ${id.Status}`;
2825
+ }
2826
+ }
2827
+ if (instanceList.length > 0) {
2828
+ this.log.info(`Instanz Fehler: ${instanceList}`);
2829
+ this.setStateAsync('lastNotification', `Instanz Fehler: ${instanceList}`, true);
2830
+
2831
+ this.sendNotification(`Instanz Fehler:\n${instanceList}`);
2832
+ }
2833
+ } catch (error) {
2834
+ this.errorReporting('[sendInstanceErrorNotificationShedule]', error);
2835
+ }
2836
+ });
2837
+ }
2838
+ } //<--End of daily device updates notification
2839
+
1981
2840
  /*=============================================
1982
2841
  = functions to create html lists =
1983
2842
  =============================================*/
@@ -2103,7 +2962,7 @@ class DeviceWatcher extends utils.Adapter {
2103
2962
  * @param {object} adptName - Adaptername of devices
2104
2963
  */
2105
2964
  async createDPsForEachAdapter(adptName) {
2106
- await this.setObjectNotExistsAsync(`${adptName}`, {
2965
+ await this.setObjectNotExistsAsync(`devices.${adptName}`, {
2107
2966
  type: 'channel',
2108
2967
  common: {
2109
2968
  name: adptName,
@@ -2111,7 +2970,7 @@ class DeviceWatcher extends utils.Adapter {
2111
2970
  native: {},
2112
2971
  });
2113
2972
 
2114
- await this.setObjectNotExistsAsync(`${adptName}.offlineCount`, {
2973
+ await this.setObjectNotExistsAsync(`devices.${adptName}.offlineCount`, {
2115
2974
  type: 'state',
2116
2975
  common: {
2117
2976
  name: {
@@ -2134,7 +2993,7 @@ class DeviceWatcher extends utils.Adapter {
2134
2993
  native: {},
2135
2994
  });
2136
2995
 
2137
- await this.setObjectNotExistsAsync(`${adptName}.offlineList`, {
2996
+ await this.setObjectNotExistsAsync(`devices.${adptName}.offlineList`, {
2138
2997
  type: 'state',
2139
2998
  common: {
2140
2999
  name: {
@@ -2157,7 +3016,7 @@ class DeviceWatcher extends utils.Adapter {
2157
3016
  native: {},
2158
3017
  });
2159
3018
 
2160
- await this.setObjectNotExistsAsync(`${adptName}.oneDeviceOffline`, {
3019
+ await this.setObjectNotExistsAsync(`devices.${adptName}.oneDeviceOffline`, {
2161
3020
  type: 'state',
2162
3021
  common: {
2163
3022
  name: {
@@ -2182,7 +3041,7 @@ class DeviceWatcher extends utils.Adapter {
2182
3041
  native: {},
2183
3042
  });
2184
3043
 
2185
- await this.setObjectNotExistsAsync(`${adptName}.listAll`, {
3044
+ await this.setObjectNotExistsAsync(`devices.${adptName}.listAll`, {
2186
3045
  type: 'state',
2187
3046
  common: {
2188
3047
  name: {
@@ -2205,7 +3064,7 @@ class DeviceWatcher extends utils.Adapter {
2205
3064
  native: {},
2206
3065
  });
2207
3066
 
2208
- await this.setObjectNotExistsAsync(`${adptName}.linkQualityList`, {
3067
+ await this.setObjectNotExistsAsync(`devices.${adptName}.linkQualityList`, {
2209
3068
  type: 'state',
2210
3069
  common: {
2211
3070
  name: {
@@ -2228,7 +3087,7 @@ class DeviceWatcher extends utils.Adapter {
2228
3087
  native: {},
2229
3088
  });
2230
3089
 
2231
- await this.setObjectNotExistsAsync(`${adptName}.countAll`, {
3090
+ await this.setObjectNotExistsAsync(`devices.${adptName}.countAll`, {
2232
3091
  type: 'state',
2233
3092
  common: {
2234
3093
  name: {
@@ -2251,7 +3110,7 @@ class DeviceWatcher extends utils.Adapter {
2251
3110
  native: {},
2252
3111
  });
2253
3112
 
2254
- await this.setObjectNotExistsAsync(`${adptName}.batteryList`, {
3113
+ await this.setObjectNotExistsAsync(`devices.${adptName}.batteryList`, {
2255
3114
  type: 'state',
2256
3115
  common: {
2257
3116
  name: {
@@ -2274,7 +3133,7 @@ class DeviceWatcher extends utils.Adapter {
2274
3133
  native: {},
2275
3134
  });
2276
3135
 
2277
- await this.setObjectNotExistsAsync(`${adptName}.lowBatteryList`, {
3136
+ await this.setObjectNotExistsAsync(`devices.${adptName}.lowBatteryList`, {
2278
3137
  type: 'state',
2279
3138
  common: {
2280
3139
  name: {
@@ -2297,7 +3156,7 @@ class DeviceWatcher extends utils.Adapter {
2297
3156
  native: {},
2298
3157
  });
2299
3158
 
2300
- await this.setObjectNotExistsAsync(`${adptName}.lowBatteryCount`, {
3159
+ await this.setObjectNotExistsAsync(`devices.${adptName}.lowBatteryCount`, {
2301
3160
  type: 'state',
2302
3161
  common: {
2303
3162
  name: {
@@ -2320,7 +3179,7 @@ class DeviceWatcher extends utils.Adapter {
2320
3179
  native: {},
2321
3180
  });
2322
3181
 
2323
- await this.setObjectNotExistsAsync(`${adptName}.oneDeviceLowBat`, {
3182
+ await this.setObjectNotExistsAsync(`devices.${adptName}.oneDeviceLowBat`, {
2324
3183
  type: 'state',
2325
3184
  common: {
2326
3185
  name: {
@@ -2345,7 +3204,7 @@ class DeviceWatcher extends utils.Adapter {
2345
3204
  native: {},
2346
3205
  });
2347
3206
 
2348
- await this.setObjectNotExistsAsync(`${adptName}.batteryCount`, {
3207
+ await this.setObjectNotExistsAsync(`devices.${adptName}.batteryCount`, {
2349
3208
  type: 'state',
2350
3209
  common: {
2351
3210
  name: {
@@ -2368,7 +3227,7 @@ class DeviceWatcher extends utils.Adapter {
2368
3227
  native: {},
2369
3228
  });
2370
3229
 
2371
- await this.setObjectNotExistsAsync(`${adptName}.upgradableCount`, {
3230
+ await this.setObjectNotExistsAsync(`devices.${adptName}.upgradableCount`, {
2372
3231
  type: 'state',
2373
3232
  common: {
2374
3233
  name: {
@@ -2392,7 +3251,7 @@ class DeviceWatcher extends utils.Adapter {
2392
3251
  native: {},
2393
3252
  });
2394
3253
 
2395
- await this.setObjectNotExistsAsync(`${adptName}.upgradableList`, {
3254
+ await this.setObjectNotExistsAsync(`devices.${adptName}.upgradableList`, {
2396
3255
  type: 'state',
2397
3256
  common: {
2398
3257
  name: {
@@ -2416,7 +3275,7 @@ class DeviceWatcher extends utils.Adapter {
2416
3275
  native: {},
2417
3276
  });
2418
3277
 
2419
- await this.setObjectNotExistsAsync(`${adptName}.oneDeviceUpdatable`, {
3278
+ await this.setObjectNotExistsAsync(`devices.${adptName}.oneDeviceUpdatable`, {
2420
3279
  type: 'state',
2421
3280
  common: {
2422
3281
  name: {
@@ -2454,7 +3313,7 @@ class DeviceWatcher extends utils.Adapter {
2454
3313
  dpSubFolder = '';
2455
3314
  }
2456
3315
 
2457
- await this.setObjectNotExistsAsync(`${dpSubFolder}offlineListHTML`, {
3316
+ await this.setObjectNotExistsAsync(`devices.${dpSubFolder}offlineListHTML`, {
2458
3317
  type: 'state',
2459
3318
  common: {
2460
3319
  name: {
@@ -2477,7 +3336,7 @@ class DeviceWatcher extends utils.Adapter {
2477
3336
  native: {},
2478
3337
  });
2479
3338
 
2480
- await this.setObjectNotExistsAsync(`${dpSubFolder}linkQualityListHTML`, {
3339
+ await this.setObjectNotExistsAsync(`devices.${dpSubFolder}linkQualityListHTML`, {
2481
3340
  type: 'state',
2482
3341
  common: {
2483
3342
  name: {
@@ -2500,7 +3359,7 @@ class DeviceWatcher extends utils.Adapter {
2500
3359
  native: {},
2501
3360
  });
2502
3361
 
2503
- await this.setObjectNotExistsAsync(`${dpSubFolder}batteryListHTML`, {
3362
+ await this.setObjectNotExistsAsync(`devices.${dpSubFolder}batteryListHTML`, {
2504
3363
  type: 'state',
2505
3364
  common: {
2506
3365
  name: {
@@ -2523,7 +3382,7 @@ class DeviceWatcher extends utils.Adapter {
2523
3382
  native: {},
2524
3383
  });
2525
3384
 
2526
- await this.setObjectNotExistsAsync(`${dpSubFolder}lowBatteryListHTML`, {
3385
+ await this.setObjectNotExistsAsync(`devices.${dpSubFolder}lowBatteryListHTML`, {
2527
3386
  type: 'state',
2528
3387
  common: {
2529
3388
  name: {
@@ -2567,6 +3426,18 @@ class DeviceWatcher extends utils.Adapter {
2567
3426
  return (dpValue = Math.round((time.getTime() - dpValue) / 1000 / 60));
2568
3427
  }
2569
3428
 
3429
+ /**
3430
+ * @param {string} dp - get Time of this datapoint
3431
+ */
3432
+ async getTimestampConnectionDP(dp) {
3433
+ const time = new Date();
3434
+ const dpValue = await this.getForeignStateAsync(dp);
3435
+ if (dpValue !== null && dpValue !== undefined) {
3436
+ const dpLastStateChange = Math.round((time.getTime() - dpValue.lc) / 1000);
3437
+ return dpLastStateChange;
3438
+ }
3439
+ }
3440
+
2570
3441
  /**
2571
3442
  * @param {object} obj - State of datapoint
2572
3443
  */
@@ -2595,6 +3466,32 @@ class DeviceWatcher extends utils.Adapter {
2595
3466
  return {};
2596
3467
  }
2597
3468
 
3469
+ /**
3470
+ * @param {number} time
3471
+ */
3472
+ async wait(time) {
3473
+ return new Promise((resolve) => {
3474
+ setTimeout(resolve, time);
3475
+ });
3476
+ }
3477
+
3478
+ /**
3479
+ * Get previous run of cron job schedule
3480
+ * Requires cron-parser!
3481
+ * Inspired by https://stackoverflow.com/questions/68134104/
3482
+ * @param {string} lastCronRun
3483
+ */
3484
+ async getPreviousCronRun(lastCronRun) {
3485
+ try {
3486
+ const interval = cronParser.parseExpression(lastCronRun);
3487
+ const previous = interval.prev();
3488
+ return Math.floor(Date.now() - previous.getTime()); // in ms
3489
+ } catch (error) {
3490
+ this.log.warn(error);
3491
+ return;
3492
+ }
3493
+ }
3494
+
2598
3495
  /**
2599
3496
  * @param {string} codePart - Message Prefix
2600
3497
  * @param {object} error - Sentry message