iobroker.device-watcher 2.4.1 → 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,11 +280,80 @@ class DeviceWatcher extends utils.Adapter {
250
280
  let contactData;
251
281
  let oldStatus;
252
282
  let isLowBatValue;
283
+ let instanceStatusRaw;
253
284
  let instanceDeviceConnectionDpTS;
254
- const instanceDeviceConnectionDpTSminTime = 5;
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:
255
292
 
256
- // Wait if the conection to device was lost to avoid multiple messages.
257
- // const delay = (n) => new Promise((r) => setTimeout(r, n * 100));
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
+ }
258
357
 
259
358
  for (const device of this.listAllDevicesRaw) {
260
359
  // On statechange update available datapoint
@@ -269,7 +368,7 @@ class DeviceWatcher extends utils.Adapter {
269
368
  if (state.val !== device.Upgradable) {
270
369
  device.Upgradable = state.val;
271
370
  if (state.val) {
272
- if (!this.blacklistNotify.includes(device.Path)) {
371
+ if (this.config.checkSendDeviceUpgrade && !this.blacklistNotify.includes(device.Path)) {
273
372
  await this.sendDeviceUpdatesNotification(device.Device, device.Adapter);
274
373
  }
275
374
  }
@@ -377,6 +476,9 @@ class DeviceWatcher extends utils.Adapter {
377
476
  const devices = [];
378
477
  let myCount = 0;
379
478
  let result;
479
+ const instances = [];
480
+ let myCountInstances = 0;
481
+ let resultInstances;
380
482
 
381
483
  switch (obj.command) {
382
484
  case 'devicesList':
@@ -407,6 +509,35 @@ class DeviceWatcher extends utils.Adapter {
407
509
  this.sendTo(obj.from, obj.command, obj.callback);
408
510
  }
409
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;
410
541
  }
411
542
  }
412
543
 
@@ -462,6 +593,11 @@ class DeviceWatcher extends utils.Adapter {
462
593
  }
463
594
  }
464
595
 
596
+ if (this.config.checkAdapterInstances) {
597
+ await this.createInstanceList();
598
+ await this.writeInstanceDPs();
599
+ }
600
+
465
601
  // Clear existing timeout
466
602
  if (this.refreshDataTimeout) {
467
603
  this.log.debug('clearing old refresh timeout');
@@ -485,6 +621,7 @@ class DeviceWatcher extends utils.Adapter {
485
621
  async createBlacklist() {
486
622
  this.log.debug(`Function started: ${this.createBlacklist.name}`);
487
623
 
624
+ // DEVICES
488
625
  const myBlacklist = this.config.tableBlacklist;
489
626
 
490
627
  for (const i in myBlacklist) {
@@ -510,6 +647,28 @@ class DeviceWatcher extends utils.Adapter {
510
647
  if (this.blacklistAdapterLists.length >= 1) this.log.info(`Found items on blacklist for lists: ${this.blacklistAdapterLists}`);
511
648
  if (this.blacklistNotify.length >= 1) this.log.info(`Found items on blacklist for notificatioons: ${this.blacklistNotify}`);
512
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
+
513
672
  this.log.debug(`Function finished: ${this.createBlacklist.name}`);
514
673
  }
515
674
 
@@ -1447,18 +1606,18 @@ class DeviceWatcher extends utils.Adapter {
1447
1606
  }
1448
1607
 
1449
1608
  // Write Datapoints for counts
1450
- await this.setStateAsync(`${dpSubFolder}offlineCount`, { val: this.offlineDevicesCount, ack: true });
1451
- await this.setStateAsync(`${dpSubFolder}countAll`, { val: this.deviceCounter, ack: true });
1452
- await this.setStateAsync(`${dpSubFolder}batteryCount`, { val: this.batteryPoweredCount, ack: true });
1453
- await this.setStateAsync(`${dpSubFolder}lowBatteryCount`, { val: this.lowBatteryPoweredCount, ack: true });
1454
- 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 });
1455
1614
 
1456
1615
  // List all devices
1457
1616
  if (this.deviceCounter === 0) {
1458
1617
  // if no device is count, write the JSON List with default value
1459
1618
  this.listAllDevices = [{ Device: '--none--', Adapter: '', Battery: '', 'Last contact': '', 'Signal strength': '' }];
1460
1619
  }
1461
- 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 });
1462
1621
 
1463
1622
  // List link quality
1464
1623
  if (this.linkQualityCount === 0) {
@@ -1466,7 +1625,7 @@ class DeviceWatcher extends utils.Adapter {
1466
1625
  this.linkQualityDevices = [{ Device: '--none--', Adapter: '', 'Signal strength': '' }];
1467
1626
  }
1468
1627
  //write JSON list
1469
- await this.setStateAsync(`${dpSubFolder}linkQualityList`, {
1628
+ await this.setStateAsync(`devices.${dpSubFolder}linkQualityList`, {
1470
1629
  val: JSON.stringify(this.linkQualityDevices),
1471
1630
  ack: true,
1472
1631
  });
@@ -1477,7 +1636,7 @@ class DeviceWatcher extends utils.Adapter {
1477
1636
  this.offlineDevices = [{ Device: '--none--', Adapter: '', 'Last contact': '' }];
1478
1637
  }
1479
1638
  //write JSON list
1480
- await this.setStateAsync(`${dpSubFolder}offlineList`, {
1639
+ await this.setStateAsync(`devices.${dpSubFolder}offlineList`, {
1481
1640
  val: JSON.stringify(this.offlineDevices),
1482
1641
  ack: true,
1483
1642
  });
@@ -1488,7 +1647,7 @@ class DeviceWatcher extends utils.Adapter {
1488
1647
  this.upgradableList = [{ Device: '--none--', Adapter: '', 'Last contact': '' }];
1489
1648
  }
1490
1649
  //write JSON list
1491
- await this.setStateAsync(`${dpSubFolder}upgradableList`, {
1650
+ await this.setStateAsync(`devices.${dpSubFolder}upgradableList`, {
1492
1651
  val: JSON.stringify(this.upgradableList),
1493
1652
  ack: true,
1494
1653
  });
@@ -1499,7 +1658,7 @@ class DeviceWatcher extends utils.Adapter {
1499
1658
  this.batteryPowered = [{ Device: '--none--', Adapter: '', Battery: '' }];
1500
1659
  }
1501
1660
  //write JSON list
1502
- await this.setStateAsync(`${dpSubFolder}batteryList`, {
1661
+ await this.setStateAsync(`devices.${dpSubFolder}batteryList`, {
1503
1662
  val: JSON.stringify(this.batteryPowered),
1504
1663
  ack: true,
1505
1664
  });
@@ -1510,43 +1669,43 @@ class DeviceWatcher extends utils.Adapter {
1510
1669
  this.batteryLowPowered = [{ Device: '--none--', Adapter: '', Battery: '' }];
1511
1670
  }
1512
1671
  //write JSON list
1513
- await this.setStateAsync(`${dpSubFolder}lowBatteryList`, {
1672
+ await this.setStateAsync(`devices.${dpSubFolder}lowBatteryList`, {
1514
1673
  val: JSON.stringify(this.batteryLowPowered),
1515
1674
  ack: true,
1516
1675
  });
1517
1676
 
1518
1677
  // set booleans datapoints
1519
1678
  if (this.offlineDevicesCount === 0) {
1520
- await this.setStateAsync(`${dpSubFolder}oneDeviceOffline`, {
1679
+ await this.setStateAsync(`devices.${dpSubFolder}oneDeviceOffline`, {
1521
1680
  val: false,
1522
1681
  ack: true,
1523
1682
  });
1524
1683
  } else {
1525
- await this.setStateAsync(`${dpSubFolder}oneDeviceOffline`, {
1684
+ await this.setStateAsync(`devices.${dpSubFolder}oneDeviceOffline`, {
1526
1685
  val: true,
1527
1686
  ack: true,
1528
1687
  });
1529
1688
  }
1530
1689
 
1531
1690
  if (this.lowBatteryPoweredCount === 0) {
1532
- await this.setStateAsync(`${dpSubFolder}oneDeviceLowBat`, {
1691
+ await this.setStateAsync(`devices.${dpSubFolder}oneDeviceLowBat`, {
1533
1692
  val: false,
1534
1693
  ack: true,
1535
1694
  });
1536
1695
  } else {
1537
- await this.setStateAsync(`${dpSubFolder}oneDeviceLowBat`, {
1696
+ await this.setStateAsync(`devices.${dpSubFolder}oneDeviceLowBat`, {
1538
1697
  val: true,
1539
1698
  ack: true,
1540
1699
  });
1541
1700
  }
1542
1701
 
1543
1702
  if (this.upgradableDevicesCount === 0) {
1544
- await this.setStateAsync(`${dpSubFolder}oneDeviceUpdatable`, {
1703
+ await this.setStateAsync(`devices.${dpSubFolder}oneDeviceUpdatable`, {
1545
1704
  val: false,
1546
1705
  ack: true,
1547
1706
  });
1548
1707
  } else {
1549
- await this.setStateAsync(`${dpSubFolder}oneDeviceUpdatable`, {
1708
+ await this.setStateAsync(`devices.${dpSubFolder}oneDeviceUpdatable`, {
1550
1709
  val: true,
1551
1710
  ack: true,
1552
1711
  });
@@ -1554,19 +1713,19 @@ class DeviceWatcher extends utils.Adapter {
1554
1713
 
1555
1714
  //write HTML list
1556
1715
  if (this.createHtmlList) {
1557
- await this.setStateAsync(`${dpSubFolder}linkQualityListHTML`, {
1716
+ await this.setStateAsync(`devices.${dpSubFolder}linkQualityListHTML`, {
1558
1717
  val: await this.creatLinkQualityListHTML(this.linkQualityDevices, this.linkQualityCount),
1559
1718
  ack: true,
1560
1719
  });
1561
- await this.setStateAsync(`${dpSubFolder}offlineListHTML`, {
1720
+ await this.setStateAsync(`devices.${dpSubFolder}offlineListHTML`, {
1562
1721
  val: await this.createOfflineListHTML(this.offlineDevices, this.offlineDevicesCount),
1563
1722
  ack: true,
1564
1723
  });
1565
- await this.setStateAsync(`${dpSubFolder}batteryListHTML`, {
1724
+ await this.setStateAsync(`devices.${dpSubFolder}batteryListHTML`, {
1566
1725
  val: await this.createBatteryListHTML(this.batteryPowered, this.batteryPoweredCount, false),
1567
1726
  ack: true,
1568
1727
  });
1569
- await this.setStateAsync(`${dpSubFolder}lowBatteryListHTML`, {
1728
+ await this.setStateAsync(`devices.${dpSubFolder}lowBatteryListHTML`, {
1570
1729
  val: await this.createBatteryListHTML(this.batteryLowPowered, this.lowBatteryPoweredCount, true),
1571
1730
  ack: true,
1572
1731
  });
@@ -1581,6 +1740,534 @@ class DeviceWatcher extends utils.Adapter {
1581
1740
  this.log.debug(`Function finished: ${this.writeDatapoints.name}`);
1582
1741
  } //<--End of writing Datapoints
1583
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
+
1584
2271
  /*=============================================
1585
2272
  = functions to send notifications =
1586
2273
  =============================================*/
@@ -1731,6 +2418,7 @@ class DeviceWatcher extends utils.Adapter {
1731
2418
  }
1732
2419
  } // <-- End of sendNotification function
1733
2420
 
2421
+ /*---------- Battery notifications ----------*/
1734
2422
  /**
1735
2423
  * send shedule message for low battery devices
1736
2424
  */
@@ -1814,6 +2502,7 @@ class DeviceWatcher extends utils.Adapter {
1814
2502
  this.log.debug(`Finished the function: ${this.sendLowBatNoticiation.name}`);
1815
2503
  }
1816
2504
 
2505
+ /*---------- Offline/Online notifications ----------*/
1817
2506
  /**
1818
2507
  * send message if an device is offline
1819
2508
  * @param {string} deviceName
@@ -1905,6 +2594,7 @@ class DeviceWatcher extends utils.Adapter {
1905
2594
  }
1906
2595
  } //<--End of daily offline notification
1907
2596
 
2597
+ /*---------- Device Updates notifications ----------*/
1908
2598
  /**
1909
2599
  * check if device updates are available and send notification
1910
2600
  * @param {string} deviceName
@@ -1937,7 +2627,7 @@ class DeviceWatcher extends utils.Adapter {
1937
2627
  /**
1938
2628
  * send shedule message with offline devices
1939
2629
  */
1940
- async sendUpgradeNotificationsShedule() {
2630
+ async sendDeviceUpdateNotificationsShedule() {
1941
2631
  const time = this.config.checkSendUpgradeTime.split(':');
1942
2632
 
1943
2633
  const checkDays = []; // list of selected days
@@ -1959,6 +2649,91 @@ class DeviceWatcher extends utils.Adapter {
1959
2649
  return; // cancel function if no day is selected
1960
2650
  }
1961
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
+
1962
2737
  if (!isUnloaded) {
1963
2738
  const cron = '10 ' + time[1] + ' ' + time[0] + ' * * ' + checkDays;
1964
2739
  schedule.scheduleJob(cron, () => {
@@ -1987,6 +2762,81 @@ class DeviceWatcher extends utils.Adapter {
1987
2762
  }
1988
2763
  } //<--End of daily offline notification
1989
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
+
1990
2840
  /*=============================================
1991
2841
  = functions to create html lists =
1992
2842
  =============================================*/
@@ -2112,7 +2962,7 @@ class DeviceWatcher extends utils.Adapter {
2112
2962
  * @param {object} adptName - Adaptername of devices
2113
2963
  */
2114
2964
  async createDPsForEachAdapter(adptName) {
2115
- await this.setObjectNotExistsAsync(`${adptName}`, {
2965
+ await this.setObjectNotExistsAsync(`devices.${adptName}`, {
2116
2966
  type: 'channel',
2117
2967
  common: {
2118
2968
  name: adptName,
@@ -2120,7 +2970,7 @@ class DeviceWatcher extends utils.Adapter {
2120
2970
  native: {},
2121
2971
  });
2122
2972
 
2123
- await this.setObjectNotExistsAsync(`${adptName}.offlineCount`, {
2973
+ await this.setObjectNotExistsAsync(`devices.${adptName}.offlineCount`, {
2124
2974
  type: 'state',
2125
2975
  common: {
2126
2976
  name: {
@@ -2143,7 +2993,7 @@ class DeviceWatcher extends utils.Adapter {
2143
2993
  native: {},
2144
2994
  });
2145
2995
 
2146
- await this.setObjectNotExistsAsync(`${adptName}.offlineList`, {
2996
+ await this.setObjectNotExistsAsync(`devices.${adptName}.offlineList`, {
2147
2997
  type: 'state',
2148
2998
  common: {
2149
2999
  name: {
@@ -2166,7 +3016,7 @@ class DeviceWatcher extends utils.Adapter {
2166
3016
  native: {},
2167
3017
  });
2168
3018
 
2169
- await this.setObjectNotExistsAsync(`${adptName}.oneDeviceOffline`, {
3019
+ await this.setObjectNotExistsAsync(`devices.${adptName}.oneDeviceOffline`, {
2170
3020
  type: 'state',
2171
3021
  common: {
2172
3022
  name: {
@@ -2191,7 +3041,7 @@ class DeviceWatcher extends utils.Adapter {
2191
3041
  native: {},
2192
3042
  });
2193
3043
 
2194
- await this.setObjectNotExistsAsync(`${adptName}.listAll`, {
3044
+ await this.setObjectNotExistsAsync(`devices.${adptName}.listAll`, {
2195
3045
  type: 'state',
2196
3046
  common: {
2197
3047
  name: {
@@ -2214,7 +3064,7 @@ class DeviceWatcher extends utils.Adapter {
2214
3064
  native: {},
2215
3065
  });
2216
3066
 
2217
- await this.setObjectNotExistsAsync(`${adptName}.linkQualityList`, {
3067
+ await this.setObjectNotExistsAsync(`devices.${adptName}.linkQualityList`, {
2218
3068
  type: 'state',
2219
3069
  common: {
2220
3070
  name: {
@@ -2237,7 +3087,7 @@ class DeviceWatcher extends utils.Adapter {
2237
3087
  native: {},
2238
3088
  });
2239
3089
 
2240
- await this.setObjectNotExistsAsync(`${adptName}.countAll`, {
3090
+ await this.setObjectNotExistsAsync(`devices.${adptName}.countAll`, {
2241
3091
  type: 'state',
2242
3092
  common: {
2243
3093
  name: {
@@ -2260,7 +3110,7 @@ class DeviceWatcher extends utils.Adapter {
2260
3110
  native: {},
2261
3111
  });
2262
3112
 
2263
- await this.setObjectNotExistsAsync(`${adptName}.batteryList`, {
3113
+ await this.setObjectNotExistsAsync(`devices.${adptName}.batteryList`, {
2264
3114
  type: 'state',
2265
3115
  common: {
2266
3116
  name: {
@@ -2283,7 +3133,7 @@ class DeviceWatcher extends utils.Adapter {
2283
3133
  native: {},
2284
3134
  });
2285
3135
 
2286
- await this.setObjectNotExistsAsync(`${adptName}.lowBatteryList`, {
3136
+ await this.setObjectNotExistsAsync(`devices.${adptName}.lowBatteryList`, {
2287
3137
  type: 'state',
2288
3138
  common: {
2289
3139
  name: {
@@ -2306,7 +3156,7 @@ class DeviceWatcher extends utils.Adapter {
2306
3156
  native: {},
2307
3157
  });
2308
3158
 
2309
- await this.setObjectNotExistsAsync(`${adptName}.lowBatteryCount`, {
3159
+ await this.setObjectNotExistsAsync(`devices.${adptName}.lowBatteryCount`, {
2310
3160
  type: 'state',
2311
3161
  common: {
2312
3162
  name: {
@@ -2329,7 +3179,7 @@ class DeviceWatcher extends utils.Adapter {
2329
3179
  native: {},
2330
3180
  });
2331
3181
 
2332
- await this.setObjectNotExistsAsync(`${adptName}.oneDeviceLowBat`, {
3182
+ await this.setObjectNotExistsAsync(`devices.${adptName}.oneDeviceLowBat`, {
2333
3183
  type: 'state',
2334
3184
  common: {
2335
3185
  name: {
@@ -2354,7 +3204,7 @@ class DeviceWatcher extends utils.Adapter {
2354
3204
  native: {},
2355
3205
  });
2356
3206
 
2357
- await this.setObjectNotExistsAsync(`${adptName}.batteryCount`, {
3207
+ await this.setObjectNotExistsAsync(`devices.${adptName}.batteryCount`, {
2358
3208
  type: 'state',
2359
3209
  common: {
2360
3210
  name: {
@@ -2377,7 +3227,7 @@ class DeviceWatcher extends utils.Adapter {
2377
3227
  native: {},
2378
3228
  });
2379
3229
 
2380
- await this.setObjectNotExistsAsync(`${adptName}.upgradableCount`, {
3230
+ await this.setObjectNotExistsAsync(`devices.${adptName}.upgradableCount`, {
2381
3231
  type: 'state',
2382
3232
  common: {
2383
3233
  name: {
@@ -2401,7 +3251,7 @@ class DeviceWatcher extends utils.Adapter {
2401
3251
  native: {},
2402
3252
  });
2403
3253
 
2404
- await this.setObjectNotExistsAsync(`${adptName}.upgradableList`, {
3254
+ await this.setObjectNotExistsAsync(`devices.${adptName}.upgradableList`, {
2405
3255
  type: 'state',
2406
3256
  common: {
2407
3257
  name: {
@@ -2425,7 +3275,7 @@ class DeviceWatcher extends utils.Adapter {
2425
3275
  native: {},
2426
3276
  });
2427
3277
 
2428
- await this.setObjectNotExistsAsync(`${adptName}.oneDeviceUpdatable`, {
3278
+ await this.setObjectNotExistsAsync(`devices.${adptName}.oneDeviceUpdatable`, {
2429
3279
  type: 'state',
2430
3280
  common: {
2431
3281
  name: {
@@ -2463,7 +3313,7 @@ class DeviceWatcher extends utils.Adapter {
2463
3313
  dpSubFolder = '';
2464
3314
  }
2465
3315
 
2466
- await this.setObjectNotExistsAsync(`${dpSubFolder}offlineListHTML`, {
3316
+ await this.setObjectNotExistsAsync(`devices.${dpSubFolder}offlineListHTML`, {
2467
3317
  type: 'state',
2468
3318
  common: {
2469
3319
  name: {
@@ -2486,7 +3336,7 @@ class DeviceWatcher extends utils.Adapter {
2486
3336
  native: {},
2487
3337
  });
2488
3338
 
2489
- await this.setObjectNotExistsAsync(`${dpSubFolder}linkQualityListHTML`, {
3339
+ await this.setObjectNotExistsAsync(`devices.${dpSubFolder}linkQualityListHTML`, {
2490
3340
  type: 'state',
2491
3341
  common: {
2492
3342
  name: {
@@ -2509,7 +3359,7 @@ class DeviceWatcher extends utils.Adapter {
2509
3359
  native: {},
2510
3360
  });
2511
3361
 
2512
- await this.setObjectNotExistsAsync(`${dpSubFolder}batteryListHTML`, {
3362
+ await this.setObjectNotExistsAsync(`devices.${dpSubFolder}batteryListHTML`, {
2513
3363
  type: 'state',
2514
3364
  common: {
2515
3365
  name: {
@@ -2532,7 +3382,7 @@ class DeviceWatcher extends utils.Adapter {
2532
3382
  native: {},
2533
3383
  });
2534
3384
 
2535
- await this.setObjectNotExistsAsync(`${dpSubFolder}lowBatteryListHTML`, {
3385
+ await this.setObjectNotExistsAsync(`devices.${dpSubFolder}lowBatteryListHTML`, {
2536
3386
  type: 'state',
2537
3387
  common: {
2538
3388
  name: {
@@ -2616,6 +3466,32 @@ class DeviceWatcher extends utils.Adapter {
2616
3466
  return {};
2617
3467
  }
2618
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
+
2619
3495
  /**
2620
3496
  * @param {string} codePart - Message Prefix
2621
3497
  * @param {object} error - Sentry message