iobroker.device-watcher 2.1.0 → 2.2.1

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
@@ -10,7 +10,7 @@ const schedule = require('node-schedule');
10
10
  const arrApart = require('./lib/arrApart.js'); // list of supported adapters
11
11
 
12
12
  // Sentry error reporting, disable when testing code!
13
- const enableSendSentry = true;
13
+ const enableSendSentry = false;
14
14
 
15
15
  // indicator if the adapter is running or not (for intervall/shedule)
16
16
  let isUnloaded = false;
@@ -40,18 +40,13 @@ class DeviceWatcher extends utils.Adapter {
40
40
  this.batteryLowPoweredRaw = [];
41
41
  this.offlineDevicesRaw = [];
42
42
 
43
- // raw counts
44
- this.offlineDevicesCountRaw = 0;
45
- this.offlineDevicesCountRawOld = 0;
46
- this.lowBatteryPoweredCountRaw = 0;
47
- this.upgradableDevicesCountRawOld = 0;
48
-
49
43
  // counts
50
44
  this.offlineDevicesCount = 0;
51
45
  this.deviceCounter = 0;
52
46
  this.linkQualityCount = 0;
53
47
  this.batteryPoweredCount = 0;
54
48
  this.lowBatteryPoweredCount = 0;
49
+ this.upgradableDevicesCount = 0;
55
50
 
56
51
  // Interval timer
57
52
  this.refreshDataTimeout = null;
@@ -90,6 +85,7 @@ class DeviceWatcher extends utils.Adapter {
90
85
  hs100: this.config.hs100Devices,
91
86
  hue: this.config.hueDevices,
92
87
  hueExt: this.config.hueExtDevices,
88
+ innogy: this.config.innogyDevices,
93
89
  jeelink: this.config.jeelinkDevices,
94
90
  lupusec: this.config.lupusecDevices,
95
91
  maxcube: this.config.maxcubeDevices,
@@ -99,6 +95,7 @@ class DeviceWatcher extends utils.Adapter {
99
95
  mihomeVacuum: this.config.mihomeVacuumDevices,
100
96
  mqttClientZigbee2Mqtt: this.config.mqttClientZigbee2MqttDevices,
101
97
  mqttNuki: this.config.mqttNukiDevices,
98
+ musiccast: this.config.musiccastDevices,
102
99
  netatmo: this.config.netatmoDevices,
103
100
  nukiExt: this.config.nukiExtDevices,
104
101
  nut: this.config.nutDevices,
@@ -136,6 +133,7 @@ class DeviceWatcher extends utils.Adapter {
136
133
  hs100: this.config.hs100MaxMinutes,
137
134
  hue: this.config.hueMaxMinutes,
138
135
  hueExt: this.config.hueextMaxMinutes,
136
+ innogy: this.config.innogyMaxMinutes,
139
137
  jeelink: this.config.jeelinkMaxMinutes,
140
138
  lupusec: this.config.lupusecMaxMinutes,
141
139
  maxcube: this.config.maxcubeMaxMinutes,
@@ -145,6 +143,7 @@ class DeviceWatcher extends utils.Adapter {
145
143
  mihomeVacuum: this.config.mihomeVacuumMaxMinutes,
146
144
  mqttClientZigbee2Mqtt: this.config.mqttClientZigbee2MqttMaxMinutes,
147
145
  mqttNuki: this.config.mqttNukiMaxMinutes,
146
+ musiccast: this.config.musiccastMaxMinutes,
148
147
  netatmo: this.config.netatmoMaxMinutes,
149
148
  nukiExt: this.config.nukiextendMaxMinutes,
150
149
  nut: this.config.nutMaxMinutes,
@@ -196,41 +195,41 @@ class DeviceWatcher extends utils.Adapter {
196
195
  }
197
196
 
198
197
  //create and fill datapoints for each adapter if selected
199
- try {
200
- for (const [id] of Object.entries(arrApart)) {
201
- if (!isUnloaded) {
202
- if (this.supAdapter !== undefined && this.supAdapter[id]) {
203
- if (this.config.createOwnFolder) {
198
+ if (this.config.createOwnFolder) {
199
+ try {
200
+ for (const [id] of Object.entries(arrApart)) {
201
+ if (!isUnloaded) {
202
+ if (this.supAdapter !== undefined && this.supAdapter[id]) {
204
203
  await this.createDPsForEachAdapter(id);
205
204
  if (this.config.createHtmlList) await this.createHtmlListDatapoints(id);
206
- this.log.debug(`Created datapoints for ${await this.capitalize(id)}`);
205
+ this.log.debug(`Created datapoints for ${this.capitalize(id)}`);
207
206
  }
207
+ } else {
208
+ return; // cancel run if unloaded was called.
208
209
  }
209
- } else {
210
- return; // cancel run if unloaded was called.
211
210
  }
211
+ } catch (error) {
212
+ this.errorReporting('[onReady - create and fill datapoints for each adapter]', error);
212
213
  }
213
- } catch (error) {
214
- this.errorReporting('[onReady - create and fill datapoints for each adapter]', error);
215
214
  }
216
215
 
217
216
  // create HTML list
218
217
  if (this.config.createHtmlList) await this.createHtmlListDatapoints();
219
218
 
220
- // update data in interval
221
- await this.refreshData();
219
+ //read data first at start
220
+ await this.main();
222
221
 
223
- // trigger update notification on state change
224
- /*
225
- if (this.config.checkSendAdapterUpdateNotify) {
226
- this.subscribeForeignStatesAsync(`admin.*.info.updatesJson`);
227
- }*/
222
+ // update last contact data in interval
223
+ await this.refreshData();
228
224
 
229
225
  // send overview for low battery devices
230
- if (this.config.checkSendBatteryMsg) await this.sendBatteryNotifyShedule();
226
+ if (this.config.checkSendBatteryMsgDaily) await this.sendBatteryNotifyShedule();
231
227
 
232
228
  // send overview of offline devices
233
229
  if (this.config.checkSendOfflineMsgDaily) await this.sendOfflineNotificationsShedule();
230
+
231
+ // send overview of upgradeable devices
232
+ if (this.config.checkSendUpgradeMsgDaily) await this.sendUpgradeNotificationsShedule();
234
233
  } catch (error) {
235
234
  this.errorReporting('[onReady]', error);
236
235
  this.terminate ? this.terminate(15) : process.exit(15);
@@ -242,11 +241,121 @@ class DeviceWatcher extends utils.Adapter {
242
241
  * @param {string} id
243
242
  * @param {ioBroker.State | null | undefined} state
244
243
  */
245
- onStateChange(id, state) {
244
+ async onStateChange(id, state) {
246
245
  // Admin JSON for Adapter updates
247
246
  if (id && state) {
248
247
  this.log.debug(`State changed: ${id} changed ${state.val}`);
249
- //this.sendAdapterUpdatesNotification(id, state);
248
+ let batteryData;
249
+ let oldLowBatState;
250
+ let contactData;
251
+ let oldStatus;
252
+ let oldSignalStrength;
253
+
254
+ for (const device of this.listAllDevicesRaw) {
255
+ // On statechange update available datapoint
256
+ switch (id) {
257
+ case device.UpdateDP:
258
+ if (state.val) {
259
+ device.Upgradable = state.val;
260
+
261
+ await this.createLists();
262
+ await this.writeDatapoints();
263
+ if (this.config.createOwnFolder) await this.createDataForEachAdapter(device.adapterID);
264
+ if (!this.blacklistNotify.includes(device.Path)) {
265
+ await this.sendDeviceUpdatesNotification(device.Device, device.Adapter);
266
+ }
267
+ }
268
+ break;
269
+
270
+ case device.SignalStrengthDP:
271
+ oldSignalStrength = device.SignalStrength;
272
+ device.SignalStrength = await this.calculateSignalStrength(state, device.adapterID);
273
+ if (oldSignalStrength !== device.SignalStrength) {
274
+ await this.createLists();
275
+ await this.writeDatapoints();
276
+ if (this.config.createOwnFolder) await this.createDataForEachAdapter(device.adapterID);
277
+ }
278
+ break;
279
+
280
+ case device.batteryDP:
281
+ if (device.isBatteryDevice) {
282
+ oldLowBatState = device.LowBat;
283
+ batteryData = await this.getBatteryData(state.val, oldLowBatState, device.adapterID);
284
+
285
+ device.Battery = batteryData[0];
286
+ device.BatteryRaw = batteryData[2];
287
+ device.LowBat = await this.setLowbatIndicator(state.val, undefined, device.LowBatDP);
288
+
289
+ if (device.LowBat && oldLowBatState !== device.LowBat) {
290
+ await this.createLists();
291
+ await this.writeDatapoints();
292
+ if (this.config.createOwnFolder) await this.createDataForEachAdapter(device.adapterID);
293
+ if (this.config.checkSendBatteryMsg && !this.blacklistNotify.includes(device.Path)) {
294
+ await this.sendLowBatNoticiation(device.Device, device.Adapter, device.Battery);
295
+ }
296
+ } else if (!device.LowBat && oldLowBatState !== device.LowBat) {
297
+ await this.createLists();
298
+ await this.writeDatapoints();
299
+ if (this.config.createOwnFolder) await this.createDataForEachAdapter(device.adapterID);
300
+ }
301
+ }
302
+ break;
303
+
304
+ case device.LowBatDP:
305
+ if (device.isBatteryDevice) {
306
+ oldLowBatState = device.LowBat;
307
+ batteryData = await this.getBatteryData(device.BatteryRaw, state.val, device.adapterID);
308
+ device.Battery = batteryData[0];
309
+ device.BatteryRaw = batteryData[2];
310
+ device.LowBat = await this.setLowbatIndicator(device.BatteryRaw, state.val, device.LowBatDP);
311
+
312
+ if (device.LowBat && oldLowBatState !== device.LowBat) {
313
+ await this.createLists();
314
+ await this.writeDatapoints();
315
+ if (this.config.createOwnFolder) await this.createDataForEachAdapter(device.adapterID);
316
+ if (this.config.checkSendBatteryMsg && !this.blacklistNotify.includes(device.Path)) {
317
+ await this.sendLowBatNoticiation(device.Device, device.Adapter, device.Battery);
318
+ }
319
+ } else if (!device.LowBat && oldLowBatState !== device.LowBat) {
320
+ await this.createLists();
321
+ await this.writeDatapoints();
322
+ if (this.config.createOwnFolder) await this.createDataForEachAdapter(device.adapterID);
323
+ }
324
+ }
325
+
326
+ break;
327
+ case device.UnreachDP:
328
+ case device.DeviceStateSelectorDP:
329
+ case device.rssiPeerSelectorDP:
330
+ case device.Path:
331
+ oldStatus = device.Status;
332
+ device.UnreachState = await this.getInitValue(device.UnreachDP);
333
+ contactData = await this.getOnlineState(
334
+ device.Path,
335
+ device.adapterID,
336
+ device.UnreachDP,
337
+ device.SignalStrength,
338
+ device.UnreachState,
339
+ device.DeviceStateSelectorDP,
340
+ device.rssiPeerSelectorDP,
341
+ );
342
+ if (contactData !== undefined) {
343
+ device.LastContact = contactData[0];
344
+ device.Status = contactData[1];
345
+ device.SignalStrength = contactData[2];
346
+ }
347
+ if (device.Status !== oldStatus) {
348
+ await this.createLists();
349
+ await this.writeDatapoints();
350
+ if (this.config.createOwnFolder) await this.createDataForEachAdapter(device.adapterID);
351
+ }
352
+
353
+ if (device.Status && oldStatus !== device.Status && this.config.checkSendOfflineMsg && !this.blacklistNotify.includes(device.Path)) {
354
+ await this.sendOfflineNotifications(device.Device, device.Adapter, device.Status, device.LastContact);
355
+ }
356
+ break;
357
+ }
358
+ }
250
359
  }
251
360
  }
252
361
 
@@ -286,11 +395,12 @@ class DeviceWatcher extends utils.Adapter {
286
395
 
287
396
  /**
288
397
  * refresh data with interval
398
+ * is neccessary to refresh lastContact data, especially of devices without state changes
289
399
  */
290
400
  async refreshData() {
291
401
  const nextTimeout = this.config.updateinterval * 1000;
292
402
 
293
- await this.main();
403
+ await this.checkLastContact();
294
404
 
295
405
  // Clear existing timeout
296
406
  if (this.refreshDataTimeout) {
@@ -315,25 +425,6 @@ class DeviceWatcher extends utils.Adapter {
315
425
  async main() {
316
426
  this.log.debug(`Function started: ${this.main.name}`);
317
427
 
318
- // fill datapoints for each adapter if selected
319
- try {
320
- for (const [id] of Object.entries(arrApart)) {
321
- if (!isUnloaded) {
322
- if (this.supAdapter !== undefined && this.supAdapter[id]) {
323
- if (this.config.createOwnFolder) {
324
- await this.createDataForEachAdapter(id);
325
- this.log.debug(`Created and filled data for ${await this.capitalize(id)}`);
326
- }
327
- }
328
- } else {
329
- this.log.warn('broke up');
330
- return; // cancel run if unloaded was called.
331
- }
332
- }
333
- } catch (error) {
334
- this.errorReporting('[main - create and fill datapoints for each adapter]', error);
335
- }
336
-
337
428
  // fill counts and lists of all selected adapter
338
429
  try {
339
430
  await this.createDataOfAllAdapter();
@@ -342,6 +433,20 @@ class DeviceWatcher extends utils.Adapter {
342
433
  this.errorReporting('[main - create data of all adapter]', error);
343
434
  }
344
435
 
436
+ // fill datapoints for each adapter if selected
437
+ if (this.config.createOwnFolder) {
438
+ try {
439
+ for (const [id] of Object.entries(arrApart)) {
440
+ if (this.supAdapter !== undefined && this.supAdapter[id]) {
441
+ await this.createDataForEachAdapter(id);
442
+ this.log.debug(`Created and filled data for ${this.capitalize(id)}`);
443
+ }
444
+ }
445
+ } catch (error) {
446
+ this.errorReporting('[main - create and fill datapoints for each adapter]', error);
447
+ }
448
+ }
449
+
345
450
  this.log.debug(`Function finished: ${this.main.name}`);
346
451
  } //<--End of main function
347
452
 
@@ -438,17 +543,22 @@ class DeviceWatcher extends utils.Adapter {
438
543
  const shortDeviceObject = await this.getForeignObjectAsync(shortCurrDeviceString);
439
544
  const shortshortDeviceObject = await this.getForeignObjectAsync(shortshortCurrDeviceString);
440
545
  let deviceName;
546
+ let folderName;
547
+ let deviceID;
441
548
 
442
549
  // Get ID with currDeviceString from datapoint
443
550
  switch (this.arrDev[i].adapterID) {
444
551
  // Get ID for Switchbot and ESPHome Devices
445
552
  case 'switchbotBle':
446
553
  case 'esphome':
447
- case 'fullybrowser':
448
554
  case 'apcups':
449
555
  deviceName = await this.getInitValue(currDeviceString + this.arrDev[i].id);
450
556
  break;
451
557
 
558
+ case 'fullybrowser':
559
+ deviceName = (await this.getInitValue(currDeviceString + this.arrDev[i].id)) + ' ' + (await this.getInitValue(currDeviceString + this.arrDev[i].id2));
560
+ break;
561
+
452
562
  // Get ID with short currDeviceString from objectjson
453
563
  case 'hueExt':
454
564
  case 'hmrpc':
@@ -469,7 +579,9 @@ class DeviceWatcher extends utils.Adapter {
469
579
  // Get ID with short currDeviceString from datapoint
470
580
  case 'mihomeVacuum':
471
581
  case 'roomba':
472
- deviceName = await this.getInitValue(shortCurrDeviceString + this.arrDev[i].id);
582
+ folderName = shortCurrDeviceString.slice(shortCurrDeviceString.lastIndexOf('.') + 1);
583
+ deviceID = await this.getInitValue(shortCurrDeviceString + this.arrDev[i].id);
584
+ deviceName = `I${folderName} ${deviceID}`;
473
585
  break;
474
586
 
475
587
  //Get ID of foldername
@@ -508,6 +620,163 @@ class DeviceWatcher extends utils.Adapter {
508
620
  }
509
621
  }
510
622
 
623
+ /**
624
+ * calculate Signalstrength
625
+ * @param {object} deviceQualityState - State value
626
+ * @param {object} adapterID - adapter name
627
+ */
628
+ async calculateSignalStrength(deviceQualityState, adapterID) {
629
+ let linkQuality;
630
+ let mqttNukiValue;
631
+
632
+ if (deviceQualityState != null) {
633
+ switch (typeof deviceQualityState.val) {
634
+ case 'number':
635
+ if (this.config.trueState) {
636
+ linkQuality = deviceQualityState.val;
637
+ } else {
638
+ switch (adapterID) {
639
+ case 'roomba':
640
+ case 'sonoff':
641
+ linkQuality = deviceQualityState.val + '%'; // If quality state is already an percent value
642
+ break;
643
+ case 'lupusec':
644
+ linkQuality = deviceQualityState.val;
645
+ break;
646
+
647
+ default:
648
+ // If quality state is an RSSI value calculate in percent:
649
+ if (deviceQualityState.val == -255) {
650
+ linkQuality = ' - ';
651
+ } else if (deviceQualityState.val < 0) {
652
+ linkQuality = Math.min(Math.max(2 * (deviceQualityState.val + 100), 0), 100) + '%';
653
+ // If Quality State is an value between 0-255 (zigbee) calculate in percent:
654
+ } else if (deviceQualityState.val >= 0) {
655
+ linkQuality = parseFloat(((100 / 255) * deviceQualityState.val).toFixed(0)) + '%';
656
+ }
657
+ break;
658
+ }
659
+ }
660
+ break;
661
+
662
+ case 'string':
663
+ switch (adapterID) {
664
+ case 'netatmo':
665
+ // for Netatmo devices
666
+ linkQuality = deviceQualityState.val;
667
+ break;
668
+ case 'nukiExt':
669
+ linkQuality = ' - ';
670
+ break;
671
+ case 'mqttNuki':
672
+ linkQuality = deviceQualityState.val;
673
+ mqttNukiValue = parseInt(linkQuality);
674
+ if (this.config.trueState) {
675
+ linkQuality = deviceQualityState.val;
676
+ } else if (mqttNukiValue < 0) {
677
+ linkQuality = Math.min(Math.max(2 * (mqttNukiValue + 100), 0), 100) + '%';
678
+ // If Quality State is an value between 0-255 (zigbee) calculate in percent:
679
+ }
680
+ }
681
+ break;
682
+ }
683
+ } else {
684
+ linkQuality = ' - ';
685
+ }
686
+ return linkQuality;
687
+ }
688
+
689
+ /**
690
+ * get battery data
691
+ * @param {object} deviceBatteryState - State value
692
+ * @param {object} deviceLowBatState - State value
693
+ * @param {object} adapterID - adapter name
694
+ */
695
+ async getBatteryData(deviceBatteryState, deviceLowBatState, adapterID) {
696
+ let batteryHealthRaw;
697
+ let batteryHealth;
698
+ let isBatteryDevice;
699
+
700
+ if (deviceBatteryState === undefined) {
701
+ if (deviceLowBatState !== undefined) {
702
+ switch (deviceLowBatState) {
703
+ case 'none':
704
+ break;
705
+ default:
706
+ if (deviceLowBatState !== true || deviceLowBatState === 'NORMAL' || deviceLowBatState === 1) {
707
+ batteryHealth = 'ok';
708
+ isBatteryDevice = true;
709
+ } else {
710
+ batteryHealth = 'low';
711
+ isBatteryDevice = true;
712
+ }
713
+ break;
714
+ }
715
+ } else {
716
+ batteryHealth = ' - ';
717
+ }
718
+ } else {
719
+ switch (adapterID) {
720
+ case 'hmrpc':
721
+ if (deviceBatteryState === 0 || (deviceBatteryState && deviceBatteryState >= 6)) {
722
+ batteryHealth = ' - ';
723
+ } else {
724
+ batteryHealth = deviceBatteryState + 'V';
725
+ batteryHealthRaw = deviceBatteryState;
726
+ isBatteryDevice = true;
727
+ }
728
+ break;
729
+ default:
730
+ batteryHealth = deviceBatteryState + '%';
731
+ batteryHealthRaw = deviceBatteryState;
732
+ isBatteryDevice = true;
733
+ break;
734
+ }
735
+ }
736
+ return [batteryHealth, isBatteryDevice, batteryHealthRaw];
737
+ }
738
+
739
+ /**
740
+ *set low bat indicator
741
+ * @param {object} deviceBatteryState
742
+ * @param {object} deviceLowBatState
743
+ * @param {object} isLowBatDP
744
+ */
745
+
746
+ async setLowbatIndicator(deviceBatteryState, deviceLowBatState, isLowBatDP) {
747
+ let lowBatIndicator = false;
748
+ /*=============================================
749
+ = Set Lowbat indicator =
750
+ =============================================*/
751
+ if (deviceLowBatState !== null && isLowBatDP !== 'none') {
752
+ switch (typeof deviceLowBatState) {
753
+ case 'number':
754
+ if (deviceLowBatState === 0) {
755
+ lowBatIndicator = true;
756
+ }
757
+ break;
758
+
759
+ case 'string':
760
+ if (deviceLowBatState !== 'NORMAL') {
761
+ // Tado devices
762
+ lowBatIndicator = true;
763
+ }
764
+ break;
765
+
766
+ case 'boolean':
767
+ if (deviceLowBatState) {
768
+ lowBatIndicator = true;
769
+ }
770
+ break;
771
+ }
772
+ } else {
773
+ if (deviceBatteryState < this.config.minWarnBatterie) {
774
+ lowBatIndicator = true;
775
+ }
776
+ }
777
+ return lowBatIndicator;
778
+ }
779
+
511
780
  /**
512
781
  * get Last Contact
513
782
  * @param {object} selector - Selector
@@ -527,118 +796,181 @@ class DeviceWatcher extends utils.Adapter {
527
796
  }
528
797
 
529
798
  /**
530
- * Create Lists
799
+ * get online state and time
800
+ * @param {object} id - ID
801
+ * @param {string} adapterID - ID of Adapter
802
+ * @param {string} unreachDP - Datapoint of Unreach
803
+ * @param {object} linkQuality - Linkquality Value
804
+ * @param {object} deviceUnreachState - State of deviceUnreach datapoint
805
+ * @param {string} deviceStateSelectorDP - Selector of device state (like .state)
806
+ * @param {string} rssiPeerSelectorDP - HM RSSI Peer Datapoint
531
807
  */
532
- async createLists() {
533
- this.linkQualityDevices = [];
534
- this.batteryPowered = [];
535
- this.batteryLowPowered = [];
536
- this.listAllDevices = [];
537
- this.offlineDevices = [];
538
- this.batteryLowPoweredRaw = [];
539
- this.offlineDevicesRaw = [];
540
- this.upgradableList = [];
541
-
542
- for (const device of this.listAllDevicesRaw) {
543
- /*---------- fill raw lists ----------*/
544
- // low bat list
545
- if (device['LowBat'] && device['Status'] !== 'Offline') {
546
- this.batteryLowPoweredRaw.push({
547
- Path: device['Path'],
548
- Device: device['Device'],
549
- Adapter: device['Adapter'],
550
- Battery: device['Battery'],
551
- });
552
- }
553
- // offline raw list
554
- if (device['Status'] === 'Offline') {
555
- this.offlineDevicesRaw.push({
556
- Path: device['Path'],
557
- Device: device['Device'],
558
- Adapter: device['Adapter'],
559
- 'Last contact': device['Last contact'],
560
- });
561
- }
808
+ async getOnlineState(id, adapterID, unreachDP, linkQuality, deviceUnreachState, deviceStateSelectorDP, rssiPeerSelectorDP) {
809
+ let lastContactString;
810
+ let deviceState = 'Online';
562
811
 
563
- /*---------- fill user lists ----------*/
564
- if (!this.blacklistLists.includes(device['Path'])) {
565
- this.listAllDevices.push({
566
- Device: device['Device'],
567
- Adapter: device['Adapter'],
568
- Battery: device['Battery'],
569
- 'Signal strength': device['Signal strength'],
570
- 'Last contact': device['Last contact'],
571
- Status: device['Status'],
572
- });
573
- // LinkQuality lists
574
- if (device['Signal strength'] != ' - ') {
575
- this.linkQualityDevices.push({
576
- Device: device['Device'],
577
- Adapter: device['Adapter'],
578
- 'Signal strength': device['Signal strength'],
579
- });
580
- }
581
- // Battery lists
582
- if (device['isBatteryDevice']) {
583
- this.batteryPowered.push({
584
- Device: device['Device'],
585
- Adapter: device['Adapter'],
586
- Battery: device['Battery'],
587
- Status: device['Status'],
588
- });
589
- }
590
- // Low Bat lists
591
- if (device['LowBat'] && device['Status'] !== 'Offline') {
592
- this.batteryLowPowered.push({
593
- Device: device['Device'],
594
- Adapter: device['Adapter'],
595
- Battery: device['Battery'],
596
- });
597
- }
812
+ try {
813
+ const deviceMainSelector = await this.getForeignStateAsync(id);
814
+ if (deviceMainSelector) {
815
+ const deviceUnreachSelector = await this.getForeignStateAsync(unreachDP);
816
+ const deviceStateSelector = await this.getForeignStateAsync(deviceStateSelectorDP); // for hmrpc devices
817
+ const rssiPeerSelector = await this.getForeignStateAsync(rssiPeerSelectorDP);
818
+ const lastContact = await this.getTimestamp(deviceMainSelector.ts);
819
+ const lastDeviceUnreachStateChange = deviceUnreachSelector != undefined ? await this.getTimestamp(deviceUnreachSelector.lc) : await this.getTimestamp(deviceMainSelector.ts);
820
+ // If there is no contact since user sets minutes add device in offline list
821
+ // calculate to days after 48 hours
822
+ switch (unreachDP) {
823
+ case 'none':
824
+ lastContactString = await this.getLastContact(deviceMainSelector.ts);
825
+ break;
598
826
 
599
- // Offline List
600
- if (device['Status'] === 'Offline') {
601
- this.offlineDevices.push({
602
- Device: device['Device'],
603
- Adapter: device['Adapter'],
604
- 'Last contact': device['Last contact'],
605
- });
827
+ default:
828
+ //State changed
829
+ if (adapterID === 'hmrpc') {
830
+ if (linkQuality !== ' - ') {
831
+ if (deviceUnreachState) {
832
+ lastContactString = await this.getLastContact(deviceMainSelector.lc);
833
+ } else {
834
+ lastContactString = await this.getLastContact(deviceMainSelector.ts);
835
+ }
836
+ } else {
837
+ if (deviceStateSelector) {
838
+ // because old hm devices don't send rssi states
839
+ lastContactString = await this.getLastContact(deviceStateSelector.ts);
840
+ } else if (rssiPeerSelector) {
841
+ // because old hm sensors don't send rssi/state values
842
+ lastContactString = await this.getLastContact(rssiPeerSelector.ts);
843
+ }
844
+ }
845
+ } else {
846
+ if (!deviceUnreachState) {
847
+ lastContactString = await this.getLastContact(deviceMainSelector.lc);
848
+ } else {
849
+ lastContactString = await this.getLastContact(deviceMainSelector.ts);
850
+ }
851
+ break;
852
+ }
606
853
  }
607
854
 
608
- // Device update List
609
- if (device['Upgradable']) {
610
- this.upgradableList.push({
611
- Device: device['Device'],
612
- Adapter: device['Adapter'],
613
- });
855
+ /*=============================================
856
+ = Set Online Status =
857
+ =============================================*/
858
+ if (this.maxMinutes !== undefined) {
859
+ switch (adapterID) {
860
+ case 'hmrpc':
861
+ case 'hmiP':
862
+ case 'maxcube':
863
+ if (this.maxMinutes[adapterID] <= 0) {
864
+ if (deviceUnreachState) {
865
+ deviceState = 'Offline'; //set online state to offline
866
+ linkQuality = '0%'; // set linkQuality to nothing
867
+ }
868
+ } else if (lastDeviceUnreachStateChange > this.maxMinutes[adapterID] && deviceUnreachState) {
869
+ deviceState = 'Offline'; //set online state to offline
870
+ linkQuality = '0%'; // set linkQuality to nothing
871
+ }
872
+ break;
873
+ case 'apcups':
874
+ case 'hue':
875
+ case 'hueExt':
876
+ case 'ping':
877
+ case 'deconz':
878
+ case 'shelly':
879
+ case 'sonoff':
880
+ case 'unifi':
881
+ case 'zigbee':
882
+ case 'zigbee2MQTT':
883
+ if (this.maxMinutes[adapterID] <= 0) {
884
+ if (!deviceUnreachState) {
885
+ deviceState = 'Offline'; //set online state to offline
886
+ linkQuality = '0%'; // set linkQuality to nothing
887
+ }
888
+ } else if (!deviceUnreachState && lastDeviceUnreachStateChange > this.maxMinutes[adapterID]) {
889
+ deviceState = 'Offline'; //set online state to offline
890
+ linkQuality = '0%'; // set linkQuality to nothing
891
+ }
892
+ break;
893
+ case 'mqttClientZigbee2Mqtt':
894
+ if (this.maxMinutes[adapterID] <= 0) {
895
+ if (deviceUnreachState !== 'online') {
896
+ deviceState = 'Offline'; //set online state to offline
897
+ linkQuality = '0%'; // set linkQuality to nothing
898
+ }
899
+ } else if (deviceUnreachState !== 'online' && lastDeviceUnreachStateChange > this.maxMinutes[adapterID]) {
900
+ deviceState = 'Offline'; //set online state to offline
901
+ linkQuality = '0%'; // set linkQuality to nothing
902
+ }
903
+ break;
904
+ case 'mihome':
905
+ if (deviceUnreachState !== undefined) {
906
+ if (this.maxMinutes[adapterID] <= 0) {
907
+ if (!deviceUnreachState) {
908
+ deviceState = 'Offline'; //set online state to offline
909
+ linkQuality = '0%'; // set linkQuality to nothing
910
+ }
911
+ } else if (lastContact > this.maxMinutes[adapterID]) {
912
+ deviceState = 'Offline'; //set online state to offline
913
+ linkQuality = '0%'; // set linkQuality to nothing
914
+ }
915
+ } else {
916
+ if (this.config.mihomeMaxMinutes <= 0) {
917
+ if (this.maxMinutes[adapterID] <= 0) {
918
+ deviceState = 'Offline'; //set online state to offline
919
+ linkQuality = '0%'; // set linkQuality to nothing
920
+ }
921
+ } else if (lastContact > this.maxMinutes[adapterID]) {
922
+ deviceState = 'Offline'; //set online state to offline
923
+ linkQuality = '0%'; // set linkQuality to nothing
924
+ }
925
+ }
926
+ break;
927
+ default:
928
+ if (this.maxMinutes[adapterID] <= 0) {
929
+ if (!deviceUnreachState) {
930
+ deviceState = 'Offline'; //set online state to offline
931
+ linkQuality = '0%'; // set linkQuality to nothing
932
+ }
933
+ } else if (lastContact > this.maxMinutes[adapterID]) {
934
+ deviceState = 'Offline'; //set online state to offline
935
+ linkQuality = '0%'; // set linkQuality to nothing
936
+ }
937
+ break;
938
+ }
614
939
  }
615
940
  }
941
+ return [lastContactString, deviceState, linkQuality];
942
+ } catch (error) {
943
+ this.errorReporting('[getLastContact]', error);
616
944
  }
617
945
  }
618
946
 
619
947
  /**
620
- * Count devices for each type
948
+ * when was last contact of device
621
949
  */
622
- async countDevices() {
623
- // Count how many devices with link Quality
624
- this.linkQualityCount = this.linkQualityDevices.length;
625
-
626
- // Count how many devcies are offline
627
- this.offlineDevicesCount = this.offlineDevices.length;
628
-
629
- // Count how many devices are with battery
630
- this.batteryPoweredCount = this.batteryPowered.length;
631
-
632
- // 3d. Count how many devices are with low battery
633
- this.lowBatteryPoweredCount = this.batteryLowPowered.length;
634
-
635
- // Count how many devices are exists
636
- this.deviceCounter = this.listAllDevices.length;
637
-
638
- // raws
639
-
640
- // Count how many devcies are offline
641
- this.offlineDevicesCountRaw = this.offlineDevicesRaw.length;
950
+ async checkLastContact() {
951
+ for (const device of this.listAllDevicesRaw) {
952
+ const oldContactState = device.Status;
953
+ device.UnreachState = await this.getInitValue(device.UnreachDP);
954
+ const contactData = await this.getOnlineState(
955
+ device.Path,
956
+ device.adapterID,
957
+ device.UnreachDP,
958
+ device.SignalStrength,
959
+ device.UnreachState,
960
+ device.DeviceStateSelectorDP,
961
+ device.rssiPeerSelectorDP,
962
+ );
963
+ if (contactData !== undefined) {
964
+ device.LastContact = contactData[0];
965
+ device.Status = contactData[1];
966
+ device.linkQuality = contactData[2];
967
+ }
968
+ if (this.config.checkSendOfflineMsg && oldContactState !== device.Status && !this.blacklistNotify.includes(device.Path)) {
969
+ await this.sendOfflineNotifications(device.Device, device.Adapter, device.Status, device.LastContact);
970
+ }
971
+ }
972
+ await this.createLists();
973
+ await this.writeDatapoints();
642
974
  }
643
975
 
644
976
  /**
@@ -670,347 +1002,127 @@ class DeviceWatcher extends utils.Adapter {
670
1002
  /*=============================================
671
1003
  = Get signal strength =
672
1004
  =============================================*/
1005
+ let deviceQualityDP = currDeviceString + this.arrDev[i].rssiState;
673
1006
  let deviceQualityState;
674
- let linkQuality;
675
- let mqttNukiValue;
676
1007
 
677
1008
  switch (adapterID) {
678
1009
  case 'mihomeVacuum':
679
- deviceQualityState = await this.getForeignStateAsync(shortCurrDeviceString + this.arrDev[i].rssiState);
1010
+ deviceQualityDP = shortCurrDeviceString + this.arrDev[i].rssiState;
1011
+ deviceQualityState = await this.getForeignStateAsync(deviceQualityDP);
680
1012
  break;
681
1013
 
682
1014
  case 'netatmo':
683
- deviceQualityState = await this.getForeignStateAsync(currDeviceString + this.arrDev[i].rssiState);
1015
+ deviceQualityState = await this.getForeignStateAsync(deviceQualityDP);
684
1016
  if (!deviceQualityState) {
685
- deviceQualityState = await this.getForeignStateAsync(currDeviceString + this.arrDev[i].rfState);
1017
+ deviceQualityDP = currDeviceString + this.arrDev[i].rfState;
1018
+ deviceQualityState = await this.getForeignStateAsync(deviceQualityDP);
686
1019
  }
687
1020
  break;
688
1021
 
689
1022
  default:
690
- deviceQualityState = await this.getForeignStateAsync(currDeviceString + this.arrDev[i].rssiState);
1023
+ deviceQualityState = await this.getForeignStateAsync(deviceQualityDP);
691
1024
  break;
692
1025
  }
1026
+ //subscribe to states
1027
+ this.subscribeForeignStatesAsync(deviceQualityDP);
693
1028
 
694
- if (deviceQualityState != null) {
695
- switch (typeof deviceQualityState.val) {
696
- case 'number':
697
- if (this.config.trueState) {
698
- linkQuality = deviceQualityState.val;
699
- } else {
700
- switch (adapterID) {
701
- case 'roomba':
702
- case 'sonoff':
703
- linkQuality = deviceQualityState.val + '%'; // If quality state is already an percent value
704
- break;
705
- case 'lupusec':
706
- linkQuality = deviceQualityState.val;
707
- break;
708
-
709
- default:
710
- // If quality state is an RSSI value calculate in percent:
711
- if (deviceQualityState.val == -255) {
712
- linkQuality = ' - ';
713
- } else if (deviceQualityState.val < 0) {
714
- linkQuality = Math.min(Math.max(2 * (deviceQualityState.val + 100), 0), 100) + '%';
715
- // If Quality State is an value between 0-255 (zigbee) calculate in percent:
716
- } else if (deviceQualityState.val >= 0) {
717
- linkQuality = parseFloat(((100 / 255) * deviceQualityState.val).toFixed(0)) + '%';
718
- }
719
- break;
720
- }
721
- }
722
- break;
723
-
724
- case 'string':
725
- switch (adapterID) {
726
- case 'netatmo':
727
- // for Netatmo devices
728
- linkQuality = deviceQualityState.val;
729
- break;
730
- case 'nukiExt':
731
- linkQuality = ' - ';
732
- break;
733
- case 'mqttNuki':
734
- linkQuality = deviceQualityState.val;
735
- mqttNukiValue = parseInt(linkQuality);
736
- if (this.config.trueState) {
737
- linkQuality = deviceQualityState.val;
738
- } else if (mqttNukiValue < 0) {
739
- linkQuality = Math.min(Math.max(2 * (mqttNukiValue + 100), 0), 100) + '%';
740
- // If Quality State is an value between 0-255 (zigbee) calculate in percent:
741
- }
742
- }
743
- break;
744
- }
745
- } else {
746
- linkQuality = ' - ';
747
- }
1029
+ let linkQuality = await this.calculateSignalStrength(deviceQualityState, adapterID);
748
1030
 
749
1031
  /*=============================================
750
1032
  = Get battery data =
751
1033
  =============================================*/
752
- let batteryHealth;
753
- let lowBatIndicator;
754
- let isBatteryDevice;
755
-
1034
+ let deviceBatteryStateDP;
1035
+ let deviceBatteryState;
756
1036
  // Get battery states
757
- let deviceBatteryState = await this.getInitValue(currDeviceString + this.arrDev[i].battery);
758
- if (deviceBatteryState === undefined) {
759
- deviceBatteryState = await this.getInitValue(currDeviceString + this.arrDev[i].battery2);
760
- }
761
-
762
- // Get battery states with short path
763
- let shortDeviceBatteryState = await this.getInitValue(shortCurrDeviceString + this.arrDev[i].battery);
764
- if (shortDeviceBatteryState === undefined) {
765
- shortDeviceBatteryState = await this.getInitValue(shortCurrDeviceString + this.arrDev[i].battery2);
766
- }
767
-
768
- // Get low bat states
769
- let deviceLowBatState = await this.getInitValue(currDeviceString + this.arrDev[i].isLowBat);
770
- if (deviceLowBatState === undefined) {
771
- deviceLowBatState = await this.getInitValue(currDeviceString + this.arrDev[i].isLowBat2);
772
- }
773
-
774
- if (deviceBatteryState === undefined && shortDeviceBatteryState === undefined) {
775
- if (deviceLowBatState !== undefined) {
776
- switch (this.arrDev[i].isLowBat || this.arrDev[i].isLowBat2) {
777
- case 'none':
778
- batteryHealth = ' - ';
779
- break;
780
- default:
781
- if (deviceLowBatState === false || deviceLowBatState === 'NORMAL' || deviceLowBatState === 1) {
782
- batteryHealth = 'ok';
783
- isBatteryDevice = true;
784
- } else {
785
- batteryHealth = 'low';
786
- isBatteryDevice = true;
787
- }
788
- break;
789
- }
790
- } else {
791
- batteryHealth = ' - ';
792
- }
793
- } else {
794
- switch (adapterID) {
795
- case 'hmrpc':
796
- if (deviceBatteryState === 0 || (deviceBatteryState && deviceBatteryState >= 6)) {
797
- batteryHealth = ' - ';
798
- } else {
799
- batteryHealth = deviceBatteryState + 'V';
800
- isBatteryDevice = true;
801
- }
802
- break;
803
-
804
- case 'hueExt':
805
- case 'mihomeVacuum':
806
- case 'mqttNuki':
807
- if (shortDeviceBatteryState) {
808
- batteryHealth = shortDeviceBatteryState + '%';
809
- isBatteryDevice = true;
810
- }
811
- break;
812
-
813
- default:
814
- batteryHealth = deviceBatteryState + '%';
815
- isBatteryDevice = true;
816
- }
817
- }
818
-
819
- /*=============================================
820
- = Set Lowbat indicator =
821
- =============================================*/
822
- switch (typeof deviceLowBatState) {
823
- case 'number':
824
- if (deviceLowBatState === 0) {
825
- lowBatIndicator = true;
826
- }
827
- break;
828
-
829
- case 'string':
830
- if (deviceLowBatState !== 'NORMAL') {
831
- // Tado devices
832
- lowBatIndicator = true;
833
- }
834
- break;
835
-
836
- case 'boolean':
837
- if (deviceLowBatState) {
838
- lowBatIndicator = true;
1037
+ switch (adapterID) {
1038
+ case 'hueExt':
1039
+ case 'mihomeVacuum':
1040
+ case 'mqttNuki':
1041
+ deviceBatteryStateDP = shortCurrDeviceString + this.arrDev[i].battery;
1042
+ deviceBatteryState = await this.getInitValue(deviceBatteryStateDP);
1043
+ if (deviceBatteryState === undefined) {
1044
+ deviceBatteryStateDP = shortCurrDeviceString + this.arrDev[i].battery2;
1045
+ deviceBatteryState = await this.getInitValue(deviceBatteryStateDP);
839
1046
  }
840
1047
  break;
841
-
842
- default: // if the battery state is under the set limit
843
- if (deviceBatteryState && deviceBatteryState < this.config.minWarnBatterie) {
844
- lowBatIndicator = true;
1048
+ default:
1049
+ deviceBatteryStateDP = currDeviceString + this.arrDev[i].battery;
1050
+ deviceBatteryState = await this.getInitValue(deviceBatteryStateDP);
1051
+ if (deviceBatteryState === undefined) {
1052
+ deviceBatteryStateDP = currDeviceString + this.arrDev[i].battery2;
1053
+ deviceBatteryState = await this.getInitValue(deviceBatteryStateDP);
845
1054
  }
846
1055
  break;
847
1056
  }
848
1057
 
849
- /*=============================================
850
- = Get last contact of device =
851
- =============================================*/
852
- let lastContactString;
853
- let deviceState = 'Online';
854
-
855
- const deviceMainSelector = await this.getForeignStateAsync(id);
856
- const deviceUnreachSelector = await this.getForeignStateAsync(currDeviceString + this.arrDev[i].reach);
857
- const deviceStateSelector = await this.getForeignStateAsync(shortCurrDeviceString + this.arrDev[i].stateValue); // for hmrpc devices
858
- const rssiPeerSelector = await this.getForeignStateAsync(currDeviceString + this.arrDev[i].rssiPeerState);
859
-
860
- if (deviceMainSelector) {
861
- try {
862
- const lastContact = await this.getTimestamp(deviceMainSelector.ts);
863
- const deviceUnreachState = await this.getInitValue(currDeviceString + this.arrDev[i].reach);
864
- const lastDeviceUnreachStateChange = deviceUnreachSelector != undefined ? await this.getTimestamp(deviceUnreachSelector.lc) : await this.getTimestamp(deviceMainSelector.ts);
865
- const shortDeviceUnreachState = await this.getForeignStateAsync(shortCurrDeviceString + this.arrDev[i].reach);
866
- // If there is no contact since user sets minutes add device in offline list
867
- // calculate to days after 48 hours
868
- switch (this.arrDev[i].reach) {
869
- case 'none':
870
- lastContactString = await this.getLastContact(deviceMainSelector.ts);
871
- break;
872
-
873
- default:
874
- //State changed
875
- if (adapterID === 'hmrpc') {
876
- if (linkQuality !== ' - ') {
877
- if (deviceUnreachState) {
878
- lastContactString = await this.getLastContact(deviceMainSelector.lc);
879
- } else {
880
- lastContactString = await this.getLastContact(deviceMainSelector.ts);
881
- }
882
- } else {
883
- if (deviceStateSelector) {
884
- // because old hm devices don't send rssi states
885
- lastContactString = await this.getLastContact(deviceStateSelector.ts);
886
- } else if (rssiPeerSelector) {
887
- // because old hm sensors don't send rssi/state values
888
- lastContactString = await this.getLastContact(rssiPeerSelector.ts);
889
- }
890
- }
891
- } else {
892
- if (!deviceUnreachState) {
893
- lastContactString = await this.getLastContact(deviceMainSelector.lc);
894
- } else {
895
- lastContactString = await this.getLastContact(deviceMainSelector.ts);
896
- }
897
- break;
898
- }
899
- }
900
-
901
- /*=============================================
902
- = Set Online Status =
903
- =============================================*/
904
- if (this.maxMinutes !== undefined) {
905
- switch (adapterID) {
906
- case 'hmrpc':
907
- case 'hmiP':
908
- case 'maxcube':
909
- if (this.maxMinutes[adapterID] <= 0) {
910
- if (deviceUnreachState) {
911
- deviceState = 'Offline'; //set online state to offline
912
- linkQuality = '0%'; // set linkQuality to nothing
913
- }
914
- } else if (lastDeviceUnreachStateChange > this.maxMinutes[adapterID] && deviceUnreachState) {
915
- deviceState = 'Offline'; //set online state to offline
916
- linkQuality = '0%'; // set linkQuality to nothing
917
- }
918
- break;
919
- case 'apcups':
920
- case 'hue':
921
- case 'hueExt':
922
- case 'ping':
923
- case 'deconz':
924
- case 'shelly':
925
- case 'sonoff':
926
- case 'unifi':
927
- case 'zigbee':
928
- case 'zigbee2MQTT':
929
- if (this.maxMinutes[adapterID] <= 0) {
930
- if (!deviceUnreachState) {
931
- deviceState = 'Offline'; //set online state to offline
932
- linkQuality = '0%'; // set linkQuality to nothing
933
- }
934
- } else if (!deviceUnreachState && lastDeviceUnreachStateChange > this.maxMinutes[adapterID]) {
935
- deviceState = 'Offline'; //set online state to offline
936
- linkQuality = '0%'; // set linkQuality to nothing
937
- }
938
- break;
939
- case 'mqttClientZigbee2Mqtt':
940
- if (this.maxMinutes[adapterID] <= 0) {
941
- if (deviceUnreachState !== 'online') {
942
- deviceState = 'Offline'; //set online state to offline
943
- linkQuality = '0%'; // set linkQuality to nothing
944
- }
945
- } else if (deviceUnreachState !== 'online' && lastDeviceUnreachStateChange > this.maxMinutes[adapterID]) {
946
- deviceState = 'Offline'; //set online state to offline
947
- linkQuality = '0%'; // set linkQuality to nothing
948
- }
949
- break;
950
- case 'mihomeVacuum':
951
- if (this.maxMinutes[adapterID] <= 0) {
952
- if (!shortDeviceUnreachState) {
953
- deviceState = 'Offline'; //set online state to offline
954
- linkQuality = '0%'; // set linkQuality to nothing
955
- }
956
- } else if (lastContact > this.maxMinutes[adapterID]) {
957
- deviceState = 'Offline'; //set online state to offline
958
- linkQuality = '0%'; // set linkQuality to nothing
959
- }
960
- break;
961
- case 'mihome':
962
- if (this.arrDev[i].battery === 'none') {
963
- if (this.maxMinutes[adapterID] <= 0) {
964
- if (!deviceUnreachState) {
965
- deviceState = 'Offline'; //set online state to offline
966
- linkQuality = '0%'; // set linkQuality to nothing
967
- }
968
- } else if (lastContact > this.maxMinutes[adapterID]) {
969
- deviceState = 'Offline'; //set online state to offline
970
- linkQuality = '0%'; // set linkQuality to nothing
971
- }
972
- } else {
973
- if (this.config.mihomeMaxMinutes <= 0) {
974
- if (this.maxMinutes[adapterID] <= 0) {
975
- deviceState = 'Offline'; //set online state to offline
976
- linkQuality = '0%'; // set linkQuality to nothing
977
- }
978
- } else if (lastContact > this.maxMinutes[adapterID]) {
979
- deviceState = 'Offline'; //set online state to offline
980
- linkQuality = '0%'; // set linkQuality to nothing
981
- }
982
- }
983
- break;
984
- default:
985
- if (this.maxMinutes[adapterID] <= 0) {
986
- if (!deviceUnreachState) {
987
- deviceState = 'Offline'; //set online state to offline
988
- linkQuality = '0%'; // set linkQuality to nothing
989
- }
990
- } else if (lastContact > this.maxMinutes[adapterID]) {
991
- deviceState = 'Offline'; //set online state to offline
992
- linkQuality = '0%'; // set linkQuality to nothing
993
- }
994
- break;
995
- }
996
- }
997
- } catch (error) {
998
- this.errorReporting('[getLastContact]', error);
999
- }
1058
+ // Get low bat states
1059
+ let isLowBatDP = currDeviceString + this.arrDev[i].isLowBat;
1060
+ let deviceLowBatState = await this.getInitValue(isLowBatDP);
1061
+ if (deviceLowBatState === undefined) {
1062
+ isLowBatDP = currDeviceString + this.arrDev[i].isLowBat2;
1063
+ deviceLowBatState = await this.getInitValue(isLowBatDP);
1064
+ }
1065
+ if (deviceLowBatState === undefined) isLowBatDP = 'none';
1066
+
1067
+ //subscribe to states
1068
+ this.subscribeForeignStatesAsync(deviceBatteryStateDP);
1069
+ this.subscribeForeignStatesAsync(isLowBatDP);
1070
+
1071
+ const batteryData = await this.getBatteryData(deviceBatteryState, deviceLowBatState, adapterID);
1072
+ const batteryHealth = batteryData[0];
1073
+ const batteryHealthRaw = batteryData[2];
1074
+ const isBatteryDevice = batteryData[1];
1075
+ let lowBatIndicator;
1076
+
1077
+ if (isBatteryDevice) {
1078
+ lowBatIndicator = await this.setLowbatIndicator(deviceBatteryState, deviceLowBatState, isLowBatDP);
1079
+ }
1080
+
1081
+ /*=============================================
1082
+ = Get last contact of device =
1083
+ =============================================*/
1084
+ let unreachDP = currDeviceString + this.arrDev[i].reach;
1085
+ const deviceStateSelectorDP = shortCurrDeviceString + this.arrDev[i].stateValue;
1086
+ const rssiPeerSelectorDP = currDeviceString + this.arrDev[i].rssiPeerState;
1087
+
1088
+ let deviceUnreachState = await this.getInitValue(unreachDP);
1089
+ if (deviceUnreachState === undefined) {
1090
+ unreachDP = shortCurrDeviceString + this.arrDev[i].reach;
1091
+ deviceUnreachState = await this.getInitValue(shortCurrDeviceString + this.arrDev[i].reach);
1092
+ }
1093
+
1094
+ // subscribe to states
1095
+ this.subscribeForeignStatesAsync(id);
1096
+ this.subscribeForeignStatesAsync(unreachDP);
1097
+ this.subscribeForeignStatesAsync(deviceStateSelectorDP);
1098
+ this.subscribeForeignStatesAsync(rssiPeerSelectorDP);
1099
+
1100
+ const onlineState = await this.getOnlineState(id, adapterID, unreachDP, linkQuality, deviceUnreachState, deviceStateSelectorDP, rssiPeerSelectorDP);
1101
+ let deviceState;
1102
+ let lastContactString;
1103
+
1104
+ if (onlineState) {
1105
+ lastContactString = onlineState[0];
1106
+ deviceState = onlineState[1];
1107
+ linkQuality = onlineState[2];
1000
1108
  }
1109
+
1001
1110
  /*=============================================
1002
1111
  = Get update data =
1003
1112
  =============================================*/
1004
- let deviceUpdateSelector;
1113
+ const deviceUpdateDP = currDeviceString + this.arrDev[i].upgrade;
1005
1114
  let isUpgradable;
1006
1115
 
1007
1116
  if (this.config.checkSendDeviceUpgrade) {
1008
- deviceUpdateSelector = await this.getInitValue(currDeviceString + this.arrDev[i].upgrade);
1117
+ const deviceUpdateSelector = await this.getInitValue(deviceUpdateDP);
1118
+
1009
1119
  if (deviceUpdateSelector) {
1010
1120
  isUpgradable = true;
1011
- } else {
1121
+ } else if (!deviceUpdateSelector) {
1012
1122
  isUpgradable = false;
1013
1123
  }
1124
+ // subscribe to states
1125
+ this.subscribeForeignStatesAsync(deviceUpdateDP);
1014
1126
  }
1015
1127
 
1016
1128
  /*=============================================
@@ -1018,33 +1130,51 @@ class DeviceWatcher extends utils.Adapter {
1018
1130
  =============================================*/
1019
1131
 
1020
1132
  /* Add only devices with battery in the rawlist */
1021
- if (this.listOnlyBattery) {
1022
- if (isBatteryDevice) {
1023
- this.listAllDevicesRaw.push({
1024
- Path: id,
1025
- Device: deviceName,
1026
- Adapter: adapter,
1027
- isBatteryDevice: isBatteryDevice,
1028
- Battery: batteryHealth,
1029
- LowBat: lowBatIndicator,
1030
- 'Signal strength': linkQuality,
1031
- 'Last contact': lastContactString,
1032
- Status: deviceState,
1033
- Upgradable: isUpgradable,
1034
- });
1035
- }
1133
+ if (this.listOnlyBattery && isBatteryDevice) {
1134
+ this.listAllDevicesRaw.push({
1135
+ Path: id,
1136
+ Device: deviceName,
1137
+ adapterID: adapterID,
1138
+ Adapter: adapter,
1139
+ isBatteryDevice: isBatteryDevice,
1140
+ Battery: batteryHealth,
1141
+ BatteryRaw: batteryHealthRaw,
1142
+ batteryDP: deviceBatteryStateDP,
1143
+ LowBat: lowBatIndicator,
1144
+ LowBatDP: isLowBatDP,
1145
+ SignalStrengthDP: deviceQualityDP,
1146
+ SignalStrength: linkQuality,
1147
+ UnreachState: deviceUnreachState,
1148
+ UnreachDP: unreachDP,
1149
+ DeviceStateSelectorDP: deviceStateSelectorDP,
1150
+ rssiPeerSelectorDP: rssiPeerSelectorDP,
1151
+ LastContact: lastContactString,
1152
+ Status: deviceState,
1153
+ UpdateDP: deviceUpdateDP,
1154
+ Upgradable: isUpgradable,
1155
+ });
1036
1156
  } else {
1037
1157
  /* Add all devices */
1038
1158
  this.listAllDevicesRaw.push({
1039
1159
  Path: id,
1040
1160
  Device: deviceName,
1161
+ adapterID: adapterID,
1041
1162
  Adapter: adapter,
1042
1163
  isBatteryDevice: isBatteryDevice,
1043
1164
  Battery: batteryHealth,
1165
+ BatteryRaw: batteryHealthRaw,
1166
+ batteryDP: deviceBatteryStateDP,
1044
1167
  LowBat: lowBatIndicator,
1045
- 'Signal strength': linkQuality,
1046
- 'Last contact': lastContactString,
1168
+ LowBatDP: isLowBatDP,
1169
+ SignalStrengthDP: deviceQualityDP,
1170
+ SignalStrength: linkQuality,
1171
+ UnreachState: deviceUnreachState,
1172
+ UnreachDP: unreachDP,
1173
+ DeviceStateSelectorDP: deviceStateSelectorDP,
1174
+ rssiPeerSelectorDP: rssiPeerSelectorDP,
1175
+ LastContact: lastContactString,
1047
1176
  Status: deviceState,
1177
+ UpdateDP: deviceUpdateDP,
1048
1178
  Upgradable: isUpgradable,
1049
1179
  });
1050
1180
  }
@@ -1053,10 +1183,133 @@ class DeviceWatcher extends utils.Adapter {
1053
1183
  return;
1054
1184
  }
1055
1185
  } // <-- end of loop
1056
- await this.createLists();
1057
- await this.countDevices();
1058
1186
  } // <-- end of createData
1059
1187
 
1188
+ /**
1189
+ * Create Lists
1190
+ */
1191
+ async createLists(adptName) {
1192
+ this.linkQualityDevices = [];
1193
+ this.batteryPowered = [];
1194
+ this.batteryLowPowered = [];
1195
+ this.listAllDevices = [];
1196
+ this.offlineDevices = [];
1197
+ this.batteryLowPoweredRaw = [];
1198
+ this.offlineDevicesRaw = [];
1199
+ this.upgradableList = [];
1200
+
1201
+ if (adptName === undefined) {
1202
+ adptName = '';
1203
+ }
1204
+
1205
+ for (const device of this.listAllDevicesRaw) {
1206
+ if (device.adapterID.includes(adptName)) {
1207
+ /*---------- fill raw lists ----------*/
1208
+ // low bat list
1209
+ if (device.LowBat && device.Status !== 'Offline') {
1210
+ this.batteryLowPoweredRaw.push({
1211
+ Path: device.Path,
1212
+ Device: device.Device,
1213
+ Adapter: device.Adapter,
1214
+ Battery: device.Battery,
1215
+ });
1216
+ }
1217
+ // offline raw list
1218
+ if (device.Status === 'Offline') {
1219
+ this.offlineDevicesRaw.push({
1220
+ Path: device.Path,
1221
+ Device: device.Device,
1222
+ Adapter: device.Adapter,
1223
+ 'Last contact': device.LastContact,
1224
+ });
1225
+ }
1226
+
1227
+ /*---------- fill user lists ----------*/
1228
+ if (!this.blacklistLists.includes(device.Path)) {
1229
+ this.listAllDevices.push({
1230
+ Device: device.Device,
1231
+ Adapter: device.Adapter,
1232
+ Battery: device.Battery,
1233
+ 'Signal strength': device.SignalStrength,
1234
+ 'Last contact': device.LastContact,
1235
+ Status: device.Status,
1236
+ });
1237
+ // LinkQuality lists
1238
+ if (device.SignalStrength != ' - ') {
1239
+ this.linkQualityDevices.push({
1240
+ Device: device.Device,
1241
+ Adapter: device.Adapter,
1242
+ 'Signal strength': device.SignalStrength,
1243
+ });
1244
+ }
1245
+ // Battery lists
1246
+ if (device['isBatteryDevice']) {
1247
+ this.batteryPowered.push({
1248
+ Device: device.Device,
1249
+ Adapter: device.Adapter,
1250
+ Battery: device.Battery,
1251
+ Status: device.Status,
1252
+ });
1253
+ }
1254
+ // Low Bat lists
1255
+ if (device.LowBat && device.Status !== 'Offline') {
1256
+ this.batteryLowPowered.push({
1257
+ Device: device.Device,
1258
+ Adapter: device.Adapter,
1259
+ Battery: device.Battery,
1260
+ });
1261
+ }
1262
+
1263
+ // Offline List
1264
+ if (device.Status === 'Offline') {
1265
+ this.offlineDevices.push({
1266
+ Device: device.Device,
1267
+ Adapter: device.Adapter,
1268
+ 'Last contact': device.LastContact,
1269
+ });
1270
+ }
1271
+
1272
+ // Device update List
1273
+ if (device.Upgradable) {
1274
+ this.upgradableList.push({
1275
+ Device: device.Device,
1276
+ Adapter: device.Adapter,
1277
+ });
1278
+ }
1279
+ }
1280
+ }
1281
+ }
1282
+ await this.countDevices();
1283
+ }
1284
+
1285
+ /**
1286
+ * Count devices for each type
1287
+ */
1288
+ async countDevices() {
1289
+ // Count how many devices with link Quality
1290
+ this.linkQualityCount = this.linkQualityDevices.length;
1291
+
1292
+ // Count how many devcies are offline
1293
+ this.offlineDevicesCount = this.offlineDevices.length;
1294
+
1295
+ // Count how many devices are with battery
1296
+ this.batteryPoweredCount = this.batteryPowered.length;
1297
+
1298
+ // 3d. Count how many devices are with low battery
1299
+ this.lowBatteryPoweredCount = this.batteryLowPowered.length;
1300
+
1301
+ // Count how many devices are exists
1302
+ this.deviceCounter = this.listAllDevices.length;
1303
+
1304
+ // Count how many devices has update available
1305
+ this.upgradableDevicesCount = this.upgradableList.length;
1306
+
1307
+ // raws
1308
+
1309
+ // Count how many devcies are offline
1310
+ this.offlineDevicesCountRaw = this.offlineDevicesRaw.length;
1311
+ }
1312
+
1060
1313
  /**
1061
1314
  * @param {string} adptName - Adapter name
1062
1315
  */
@@ -1064,13 +1317,12 @@ class DeviceWatcher extends utils.Adapter {
1064
1317
  // create Data for each Adapter in own lists
1065
1318
  this.log.debug(`Function started: ${this.createDataForEachAdapter.name}`);
1066
1319
 
1067
- await this.resetVars(); // reset the arrays and counts
1068
-
1069
1320
  try {
1070
- for (let i = 0; i < this.arrDev.length; i++) {
1071
- if (this.arrDev[i].adapterID.includes(adptName)) {
1321
+ for (const device of this.listAllDevicesRaw) {
1322
+ if (device.adapterID.includes(adptName)) {
1072
1323
  // list device only if selected adapter matched with device
1073
- await this.createData(i);
1324
+ await this.createLists(adptName);
1325
+ await this.writeDatapoints(adptName);
1074
1326
  }
1075
1327
  }
1076
1328
 
@@ -1089,22 +1341,10 @@ class DeviceWatcher extends utils.Adapter {
1089
1341
  this.log.debug(`Function started: ${this.createDataOfAllAdapter.name}`);
1090
1342
 
1091
1343
  try {
1092
- await this.resetVars(); // reset the arrays and counts
1093
-
1094
1344
  for (let i = 0; i < this.arrDev.length; i++) {
1095
- if (!isUnloaded) {
1096
- await this.createData(i);
1097
- } else {
1098
- return; // cancel run if unloaded was called.
1099
- }
1345
+ await this.createData(i);
1346
+ await this.createLists();
1100
1347
  }
1101
-
1102
- // send message if new devices are offline
1103
- if (this.config.checkSendOfflineMsg) await this.sendOfflineNotifications();
1104
-
1105
- // send overview of upgradable devices
1106
- if (this.config.checkSendDeviceUpgrade) await this.sendDeviceUpdatesNotification();
1107
-
1108
1348
  await this.writeDatapoints(); // fill the datapoints
1109
1349
  } catch (error) {
1110
1350
  this.errorReporting('[createDataOfAllAdapter]', error);
@@ -1291,12 +1531,12 @@ class DeviceWatcher extends utils.Adapter {
1291
1531
  let deviceList = '';
1292
1532
 
1293
1533
  for (const id of this.batteryLowPoweredRaw) {
1294
- if (!this.blacklistNotify.includes(id['Path'])) {
1534
+ if (!this.blacklistNotify.includes(id.Path)) {
1295
1535
  if (!this.config.showAdapterNameinMsg) {
1296
- deviceList = `${deviceList}\n${id['Device']} (${id['Battery']})`;
1536
+ deviceList = `${deviceList}\n${id.Device} (${id.Battery})`;
1297
1537
  } else {
1298
1538
  // Add adaptername if checkbox is checked true in options by user
1299
- deviceList = `${deviceList}\n${id['Adapter']}: ${id['Device']} (${id['Battery']})`;
1539
+ deviceList = `${deviceList}\n${id.Adapter}: ${id.Device} (${id.Battery})`;
1300
1540
  }
1301
1541
  }
1302
1542
  }
@@ -1313,41 +1553,66 @@ class DeviceWatcher extends utils.Adapter {
1313
1553
  }
1314
1554
  } //<--End of battery notification
1315
1555
 
1556
+ /**
1557
+ * check if device updates are available and send notification
1558
+ * @param {string} deviceName
1559
+ * @param {string} adapter
1560
+ * @param {string} battery
1561
+ **/
1562
+ async sendLowBatNoticiation(deviceName, adapter, battery) {
1563
+ this.log.debug(`Start the function: ${this.sendLowBatNoticiation.name}`);
1564
+
1565
+ try {
1566
+ let msg = '';
1567
+ let deviceList = '';
1568
+
1569
+ if (!this.config.showAdapterNameinMsg) {
1570
+ deviceList = `${deviceList}\n${deviceName} (${battery})`;
1571
+ } else {
1572
+ deviceList = `${deviceList}\n${adapter}: ${deviceName} (${battery})`;
1573
+ }
1574
+ msg = `Gerät mit geringer Batterie erkannt: \n`;
1575
+
1576
+ this.log.info(msg + deviceList);
1577
+ await this.setStateAsync('lastNotification', msg + deviceList, true);
1578
+ await this.sendNotification(msg + deviceList);
1579
+ } catch (error) {
1580
+ this.errorReporting('[sendLowBatNoticiation]', error);
1581
+ }
1582
+ this.log.debug(`Finished the function: ${this.sendLowBatNoticiation.name}`);
1583
+ }
1584
+
1316
1585
  /**
1317
1586
  * send message if an device is offline
1587
+ * @param {string} deviceName
1588
+ * @param {string} adapter
1589
+ * @param {string} status
1590
+ * @param {string} lastContact
1318
1591
  */
1319
- async sendOfflineNotifications() {
1592
+ async sendOfflineNotifications(deviceName, adapter, status, lastContact) {
1320
1593
  this.log.debug(`Start the function: ${this.sendOfflineNotifications.name}`);
1321
1594
 
1322
1595
  try {
1323
1596
  let msg = '';
1324
1597
  let deviceList = '';
1325
1598
 
1326
- for (const id of this.offlineDevicesRaw) {
1327
- if (!this.blacklistNotify.includes(id['Path'])) {
1328
- if (!this.config.showAdapterNameinMsg) {
1329
- deviceList = `${deviceList}\n${id['Device']} (${id['Last contact']})`;
1330
- } else {
1331
- deviceList = `${deviceList}\n${id['Adapter']}: ${id['Device']} (${id['Last contact']})`;
1332
- }
1333
- }
1599
+ if (!this.config.showAdapterNameinMsg) {
1600
+ deviceList = `${deviceList}\n${deviceName} (${lastContact})`;
1601
+ } else {
1602
+ deviceList = `${deviceList}\n${adapter}: ${deviceName} (${lastContact})`;
1334
1603
  }
1335
- if (deviceList.length !== this.offlineDevicesCountRawOld) {
1336
- if (deviceList.length === 0) {
1337
- msg = 'Alle Geräte sind Online.';
1338
- } else if (deviceList.length === 1) {
1339
- // make singular if it is only one device
1340
- msg = 'Folgendes Gerät ist seit einiger Zeit nicht erreichbar: \n';
1341
- } else if (deviceList.length >= 2) {
1342
- //make plural if it is more than one device
1343
- msg = `Folgende Geräte sind seit einiger Zeit nicht erreichbar: \n`;
1344
- }
1345
1604
 
1346
- this.log.info(msg + deviceList);
1347
- this.offlineDevicesCountRawOld = deviceList.length;
1348
- await this.setStateAsync('lastNotification', msg + deviceList, true);
1349
- await this.sendNotification(msg + deviceList);
1605
+ if (status === 'Online') {
1606
+ // make singular if it is only one device
1607
+ msg = 'Folgendes Gerät ist wieder erreichbar: \n';
1608
+ } else if (status === 'Offline') {
1609
+ //make plural if it is more than one device
1610
+ msg = `Folgendes Gerät ist seit einiger Zeit nicht erreichbar: \n`;
1350
1611
  }
1612
+
1613
+ this.log.info(msg + deviceList);
1614
+ await this.setStateAsync('lastNotification', msg + deviceList, true);
1615
+ await this.sendNotification(msg + deviceList);
1351
1616
  } catch (error) {
1352
1617
  this.errorReporting('[sendOfflineMessage]', error);
1353
1618
  }
@@ -1386,11 +1651,11 @@ class DeviceWatcher extends utils.Adapter {
1386
1651
  let deviceList = '';
1387
1652
 
1388
1653
  for (const id of this.offlineDevicesRaw) {
1389
- if (!this.blacklistNotify.includes(id['Path'])) {
1654
+ if (!this.blacklistNotify.includes(id.Path)) {
1390
1655
  if (!this.config.showAdapterNameinMsg) {
1391
- deviceList = `${deviceList}\n${id['Device']} (${id['Last contact']})`;
1656
+ deviceList = `${deviceList}\n${id.Device} (${id.LastContact})`;
1392
1657
  } else {
1393
- deviceList = `${deviceList}\n${id['Adapter']}: ${id['Device']} (${id['Last contact']})`;
1658
+ deviceList = `${deviceList}\n${id.Adapter}: ${id.Device} (${id.LastContact})`;
1394
1659
  }
1395
1660
  }
1396
1661
  }
@@ -1408,69 +1673,29 @@ class DeviceWatcher extends utils.Adapter {
1408
1673
  }
1409
1674
  } //<--End of daily offline notification
1410
1675
 
1411
- /**
1412
- * check if adapter updates are available and send notification
1413
- * @param {string} id
1414
- * @param {ioBroker.State | null | undefined} state
1415
- */
1416
- /*
1417
- async sendAdapterUpdatesNotification(id, state) {
1418
- this.log.debug(`Start the function: ${this.sendAdapterUpdatesNotification.name}`);
1419
-
1420
- try {
1421
- if (state && state !== undefined) {
1422
- const list = await this.parseData(state.val);
1423
- let msg = '';
1424
- let adapterList = '';
1425
-
1426
- for (const [id] of Object.entries(list)) {
1427
- adapterList = `${adapterList}\n${this.capitalize(id)} - Version: ${list[id].availableVersion}`;
1428
- }
1429
- if (adapterList.length !== 0) {
1430
- msg = `Neue Adapter Updates vorhanden: \n`;
1431
-
1432
- this.log.info(msg + adapterList);
1433
- await this.setStateAsync('lastNotification', msg + adapterList, true);
1434
- await this.sendNotification(msg + adapterList);
1435
- }
1436
- }
1437
- } catch (error) {
1438
- this.errorReporting('[sendAdapterUpdatesNotification]', error);
1439
- }
1440
- this.log.debug(`Finished the function: ${this.sendAdapterUpdatesNotification.name}`);
1441
- }*/
1442
-
1443
1676
  /**
1444
1677
  * check if device updates are available and send notification
1678
+ * @param {string} deviceName
1679
+ * @param {string} adapter
1445
1680
  **/
1446
- async sendDeviceUpdatesNotification() {
1681
+ async sendDeviceUpdatesNotification(deviceName, adapter) {
1447
1682
  this.log.debug(`Start the function: ${this.sendDeviceUpdatesNotification.name}`);
1448
1683
 
1449
1684
  try {
1450
1685
  let msg = '';
1451
1686
  let deviceList = '';
1452
1687
 
1453
- for (const id of this.upgradableList) {
1454
- if (!this.blacklistNotify.includes(id['Path'])) {
1455
- if (!this.config.showAdapterNameinMsg) {
1456
- deviceList = `${deviceList}\n${id['Device']}`;
1457
- } else {
1458
- deviceList = `${deviceList}\n${id['Adapter']}: ${id['Device']}`;
1459
- }
1460
- }
1461
- }
1462
- if (deviceList.length !== this.upgradableDevicesCountRawOld) {
1463
- if (deviceList.length >= 1) {
1464
- msg = `Neue Geräte Updates vorhanden: \n`;
1465
-
1466
- this.log.info(msg + deviceList);
1467
- this.upgradableDevicesCountRawOld = deviceList.length;
1468
- await this.setStateAsync('lastNotification', msg + deviceList, true);
1469
- await this.sendNotification(msg + deviceList);
1470
- } else {
1471
- this.upgradableDevicesCountRawOld = deviceList.length;
1472
- }
1688
+ if (!this.config.showAdapterNameinMsg) {
1689
+ deviceList = `${deviceList}\n${deviceName}`;
1690
+ } else {
1691
+ deviceList = `${deviceList}\n${adapter}: ${deviceName}`;
1473
1692
  }
1693
+
1694
+ msg = `Neue Geräte Updates vorhanden: \n`;
1695
+
1696
+ this.log.info(msg + deviceList);
1697
+ await this.setStateAsync('lastNotification', msg + deviceList, true);
1698
+ await this.sendNotification(msg + deviceList);
1474
1699
  } catch (error) {
1475
1700
  this.errorReporting('[sendDeviceUpdatesNotification]', error);
1476
1701
  }
@@ -1478,35 +1703,58 @@ class DeviceWatcher extends utils.Adapter {
1478
1703
  }
1479
1704
 
1480
1705
  /**
1481
- * reset arrays and counts
1706
+ * send shedule message with offline devices
1482
1707
  */
1483
- async resetVars() {
1484
- //Reset all arrays and counts
1485
- this.log.debug(`Function started: ${this.resetVars.name}`);
1708
+ async sendUpgradeNotificationsShedule() {
1709
+ const time = this.config.checkSendUpgradeTime.split(':');
1486
1710
 
1487
- // arrays
1488
- this.offlineDevices = [];
1489
- this.linkQualityDevices = [];
1490
- this.batteryPowered = [];
1491
- this.batteryLowPowered = [];
1492
- this.listAllDevices = [];
1493
- this.listAllDevicesRaw = [];
1711
+ const checkDays = []; // list of selected days
1494
1712
 
1495
- // raws
1496
- this.batteryLowPoweredRaw = [];
1497
- this.offlineDevicesRaw = [];
1498
- this.lowBatteryPoweredCountRaw = 0;
1499
- this.offlineDevicesCountRaw = 0;
1713
+ // push the selected days in list
1714
+ if (this.config.checkUpgradeMonday) checkDays.push(1);
1715
+ if (this.config.checkUpgradeTuesday) checkDays.push(2);
1716
+ if (this.config.checkUpgradeWednesday) checkDays.push(3);
1717
+ if (this.config.checkUpgradeThursday) checkDays.push(4);
1718
+ if (this.config.checkUpgradeFriday) checkDays.push(5);
1719
+ if (this.config.checkUpgradeSaturday) checkDays.push(6);
1720
+ if (this.config.checkUpgradeSunday) checkDays.push(0);
1500
1721
 
1501
- // counts
1502
- this.offlineDevicesCount = 0;
1503
- this.deviceCounter = 0;
1504
- this.linkQualityCount = 0;
1505
- this.batteryPoweredCount = 0;
1506
- this.lowBatteryPoweredCount = 0;
1722
+ if (checkDays.length >= 1) {
1723
+ // check if an day is selected
1724
+ this.log.debug(`Number of selected days for daily Upgrade message: ${checkDays.length}. Send Message on: ${checkDays.join(', ')} ...`);
1725
+ } else {
1726
+ this.log.warn(`No days selected for daily Upgrade message. Please check the instance configuration!`);
1727
+ return; // cancel function if no day is selected
1728
+ }
1729
+
1730
+ if (!isUnloaded) {
1731
+ const cron = '10 ' + time[1] + ' ' + time[0] + ' * * ' + checkDays;
1732
+ schedule.scheduleJob(cron, () => {
1733
+ try {
1734
+ let deviceList = '';
1735
+
1736
+ for (const id of this.upgradableList) {
1737
+ if (!this.blacklistNotify.includes(id.Path)) {
1738
+ if (!this.config.showAdapterNameinMsg) {
1739
+ deviceList = `${deviceList}\n${id.Device}`;
1740
+ } else {
1741
+ deviceList = `${deviceList}\n${id.Adapter}: ${id.Device}`;
1742
+ }
1743
+ }
1744
+ }
1745
+
1746
+ if (deviceList.length > 0) {
1747
+ this.log.info(`Geräte Upgrade: ${deviceList}`);
1748
+ this.setStateAsync('lastNotification', `Geräte Upgrade: ${deviceList}`, true);
1507
1749
 
1508
- this.log.debug(`Function finished: ${this.resetVars.name}`);
1509
- } // <-- end of resetVars
1750
+ this.sendNotification(`Geräte Upgrade:\n${deviceList}`);
1751
+ }
1752
+ } catch (error) {
1753
+ this.errorReporting('[sendUpgradeNotificationsShedule]', error);
1754
+ }
1755
+ });
1756
+ }
1757
+ } //<--End of daily offline notification
1510
1758
 
1511
1759
  /**
1512
1760
  * @param {string} [adptName] - Adaptername
@@ -1529,6 +1777,7 @@ class DeviceWatcher extends utils.Adapter {
1529
1777
  await this.setStateAsync(`${dpSubFolder}countAll`, { val: this.deviceCounter, ack: true });
1530
1778
  await this.setStateAsync(`${dpSubFolder}batteryCount`, { val: this.batteryPoweredCount, ack: true });
1531
1779
  await this.setStateAsync(`${dpSubFolder}lowBatteryCount`, { val: this.lowBatteryPoweredCount, ack: true });
1780
+ await this.setStateAsync(`${dpSubFolder}upgradableCount`, { val: this.upgradableDevicesCount, ack: true });
1532
1781
 
1533
1782
  if (this.deviceCounter === 0) {
1534
1783
  // if no device is count, write the JSON List with default value
@@ -1545,12 +1794,6 @@ class DeviceWatcher extends utils.Adapter {
1545
1794
  val: JSON.stringify(this.linkQualityDevices),
1546
1795
  ack: true,
1547
1796
  });
1548
- //write HTML list
1549
- if (this.config.createHtmlList)
1550
- await this.setStateAsync(`${dpSubFolder}linkQualityListHTML`, {
1551
- val: await this.creatLinkQualityListHTML(this.linkQualityDevices, this.linkQualityCount),
1552
- ack: true,
1553
- });
1554
1797
 
1555
1798
  if (this.offlineDevicesCount === 0) {
1556
1799
  // if no device is count, write the JSON List with default value
@@ -1561,12 +1804,16 @@ class DeviceWatcher extends utils.Adapter {
1561
1804
  val: JSON.stringify(this.offlineDevices),
1562
1805
  ack: true,
1563
1806
  });
1564
- //write HTML list
1565
- if (this.config.createHtmlList)
1566
- await this.setStateAsync(`${dpSubFolder}offlineListHTML`, {
1567
- val: await this.createOfflineListHTML(this.offlineDevices, this.offlineDevicesCount),
1568
- ack: true,
1569
- });
1807
+
1808
+ if (this.upgradableDevicesCount === 0) {
1809
+ // if no device is count, write the JSON List with default value
1810
+ this.upgradableList = [{ Device: '--none--', Adapter: '', 'Last contact': '' }];
1811
+ }
1812
+ //write JSON list
1813
+ await this.setStateAsync(`${dpSubFolder}upgradableList`, {
1814
+ val: JSON.stringify(this.upgradableList),
1815
+ ack: true,
1816
+ });
1570
1817
 
1571
1818
  if (this.batteryPoweredCount === 0) {
1572
1819
  // if no device is count, write the JSON List with default value
@@ -1577,12 +1824,6 @@ class DeviceWatcher extends utils.Adapter {
1577
1824
  val: JSON.stringify(this.batteryPowered),
1578
1825
  ack: true,
1579
1826
  });
1580
- //write HTML list
1581
- if (this.config.createHtmlList)
1582
- await this.setStateAsync(`${dpSubFolder}batteryListHTML`, {
1583
- val: await this.createBatteryListHTML(this.batteryPowered, this.batteryPoweredCount, false),
1584
- ack: true,
1585
- });
1586
1827
 
1587
1828
  if (this.lowBatteryPoweredCount === 0) {
1588
1829
  // if no device is count, write the JSON List with default value
@@ -1593,12 +1834,26 @@ class DeviceWatcher extends utils.Adapter {
1593
1834
  val: JSON.stringify(this.batteryLowPowered),
1594
1835
  ack: true,
1595
1836
  });
1837
+
1596
1838
  //write HTML list
1597
- if (this.config.createHtmlList)
1839
+ if (this.config.createHtmlList) {
1840
+ await this.setStateAsync(`${dpSubFolder}linkQualityListHTML`, {
1841
+ val: await this.creatLinkQualityListHTML(this.linkQualityDevices, this.linkQualityCount),
1842
+ ack: true,
1843
+ });
1844
+ await this.setStateAsync(`${dpSubFolder}offlineListHTML`, {
1845
+ val: await this.createOfflineListHTML(this.offlineDevices, this.offlineDevicesCount),
1846
+ ack: true,
1847
+ });
1848
+ await this.setStateAsync(`${dpSubFolder}batteryListHTML`, {
1849
+ val: await this.createBatteryListHTML(this.batteryPowered, this.batteryPoweredCount, false),
1850
+ ack: true,
1851
+ });
1598
1852
  await this.setStateAsync(`${dpSubFolder}lowBatteryListHTML`, {
1599
1853
  val: await this.createBatteryListHTML(this.batteryLowPowered, this.lowBatteryPoweredCount, true),
1600
1854
  ack: true,
1601
1855
  });
1856
+ }
1602
1857
 
1603
1858
  // create timestamp of last run
1604
1859
  const lastCheck = this.formatDate(new Date(), 'DD.MM.YYYY') + ' - ' + this.formatDate(new Date(), 'hh:mm:ss');
@@ -1637,7 +1892,7 @@ class DeviceWatcher extends utils.Adapter {
1637
1892
  html += `<tr>
1638
1893
  <td><font>${device.Device}</font></td>
1639
1894
  <td align=center><font>${device.Adapter}</font></td>
1640
- <td align=right><font>${device['Signal strength']}</font></td>
1895
+ <td align=right><font>${device.SignalStrength}</font></td>
1641
1896
  </tr>`;
1642
1897
  }
1643
1898
 
@@ -1673,7 +1928,7 @@ class DeviceWatcher extends utils.Adapter {
1673
1928
  html += `<tr>
1674
1929
  <td><font>${device.Device}</font></td>
1675
1930
  <td align=center><font>${device.Adapter}</font></td>
1676
- <td align=center><font color=orange>${device['Last contact']}</font></td>
1931
+ <td align=center><font color=orange>${device.LastContact}</font></td>
1677
1932
  </tr>`;
1678
1933
  }
1679
1934
 
@@ -1941,6 +2196,54 @@ class DeviceWatcher extends utils.Adapter {
1941
2196
  },
1942
2197
  native: {},
1943
2198
  });
2199
+
2200
+ await this.setObjectNotExistsAsync(`${adptName}.upgradableCount`, {
2201
+ type: 'state',
2202
+ common: {
2203
+ name: {
2204
+ en: 'Number of devices with available updates ',
2205
+ de: 'Anzahl der Geräte mit verfügbaren Updates',
2206
+ ru: 'Количество устройств с доступными обновлениями',
2207
+ pt: 'Número de dispositivos com atualizações disponíveis',
2208
+ nl: 'Nummer van apparatuur met beschikbare updates',
2209
+ fr: 'Nombre de dispositifs avec mises à jour disponibles',
2210
+ it: 'Numero di dispositivi con aggiornamenti disponibili',
2211
+ es: 'Número de dispositivos con actualizaciones disponibles',
2212
+ pl: 'Liczba urządzeń z dostępną aktualizacją',
2213
+ uk: 'Кількість пристроїв з доступними оновленнями',
2214
+ 'zh-cn': '现有更新的装置数目',
2215
+ },
2216
+ type: 'number',
2217
+ role: 'value',
2218
+ read: true,
2219
+ write: false,
2220
+ },
2221
+ native: {},
2222
+ });
2223
+
2224
+ await this.setObjectNotExistsAsync(`${adptName}.upgradableList`, {
2225
+ type: 'state',
2226
+ common: {
2227
+ name: {
2228
+ en: 'JSON List of devices with available updates ',
2229
+ de: 'JSON Liste der Geräte mit verfügbaren Updates',
2230
+ ru: 'ДЖСОН Список устройств с доступными обновлениями',
2231
+ pt: 'J. Lista de dispositivos com atualizações disponíveis',
2232
+ nl: 'JSON List van apparatuur met beschikbare updates',
2233
+ fr: 'JSON Liste des appareils avec mises à jour disponibles',
2234
+ it: 'JSON Elenco dei dispositivi con aggiornamenti disponibili',
2235
+ es: 'JSON Lista de dispositivos con actualizaciones disponibles',
2236
+ pl: 'JSON Lista urządzeń korzystających z aktualizacji',
2237
+ uk: 'Сонце Перелік пристроїв з доступними оновленнями',
2238
+ 'zh-cn': '附 件 现有最新设备清单',
2239
+ },
2240
+ type: 'array',
2241
+ role: 'json',
2242
+ read: true,
2243
+ write: false,
2244
+ },
2245
+ native: {},
2246
+ });
1944
2247
  }
1945
2248
 
1946
2249
  /**