iobroker.device-watcher 2.0.3 → 2.2.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
@@ -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;
@@ -33,23 +33,20 @@ class DeviceWatcher extends utils.Adapter {
33
33
  this.blacklistNotify = [];
34
34
  this.arrDev = [];
35
35
  this.adapterSelected = [];
36
+ this.upgradableList = [];
36
37
 
37
38
  // raw arrays
38
39
  this.listAllDevicesRaw = [];
39
40
  this.batteryLowPoweredRaw = [];
40
41
  this.offlineDevicesRaw = [];
41
42
 
42
- // raw counts
43
- this.offlineDevicesCountRaw = 0;
44
- this.offlineDevicesCountRawOld = 0;
45
- this.lowBatteryPoweredCountRaw = 0;
46
-
47
43
  // counts
48
44
  this.offlineDevicesCount = 0;
49
45
  this.deviceCounter = 0;
50
46
  this.linkQualityCount = 0;
51
47
  this.batteryPoweredCount = 0;
52
48
  this.lowBatteryPoweredCount = 0;
49
+ this.upgradableDevicesCount = 0;
53
50
 
54
51
  // Interval timer
55
52
  this.refreshDataTimeout = null;
@@ -74,6 +71,7 @@ class DeviceWatcher extends utils.Adapter {
74
71
 
75
72
  this.supAdapter = {
76
73
  alexa2: this.config.alexa2Devices,
74
+ apcups: this.config.apcupsDevices,
77
75
  ble: this.config.bleDevices,
78
76
  deconz: this.config.deconzDevices,
79
77
  enocean: this.config.enoceanDevices,
@@ -94,6 +92,9 @@ class DeviceWatcher extends utils.Adapter {
94
92
  mihome: this.config.mihomeDevices,
95
93
  mihomeGW: this.config.mihomeDevices,
96
94
  mihomeVacuum: this.config.mihomeVacuumDevices,
95
+ mqttClientZigbee2Mqtt: this.config.mqttClientZigbee2MqttDevices,
96
+ mqttNuki: this.config.mqttNukiDevices,
97
+ musiccast: this.config.musiccastDevices,
97
98
  netatmo: this.config.netatmoDevices,
98
99
  nukiExt: this.config.nukiExtDevices,
99
100
  nut: this.config.nutDevices,
@@ -117,6 +118,7 @@ class DeviceWatcher extends utils.Adapter {
117
118
 
118
119
  this.maxMinutes = {
119
120
  alexa2: this.config.alexa2MaxMinutes,
121
+ apcups: this.config.apcupsMaxMinutes,
120
122
  ble: this.config.bleMaxMinutes,
121
123
  deconz: this.config.deconzMaxMinutes,
122
124
  enocean: this.config.enoceanMaxMinutes,
@@ -137,6 +139,9 @@ class DeviceWatcher extends utils.Adapter {
137
139
  mihome: this.config.mihomeMaxMinutes,
138
140
  mihomeGW: this.config.mihomeMaxMinutes,
139
141
  mihomeVacuum: this.config.mihomeVacuumMaxMinutes,
142
+ mqttClientZigbee2Mqtt: this.config.mqttClientZigbee2MqttMaxMinutes,
143
+ mqttNuki: this.config.mqttNukiMaxMinutes,
144
+ musiccast: this.config.musiccastMaxMinutes,
140
145
  netatmo: this.config.netatmoMaxMinutes,
141
146
  nukiExt: this.config.nukiextendMaxMinutes,
142
147
  nut: this.config.nutMaxMinutes,
@@ -188,35 +193,41 @@ class DeviceWatcher extends utils.Adapter {
188
193
  }
189
194
 
190
195
  //create and fill datapoints for each adapter if selected
191
- try {
192
- for (const [id] of Object.entries(arrApart)) {
193
- if (!isUnloaded) {
194
- if (this.supAdapter !== undefined && this.supAdapter[id]) {
195
- if (this.config.createOwnFolder) {
196
+ if (this.config.createOwnFolder) {
197
+ try {
198
+ for (const [id] of Object.entries(arrApart)) {
199
+ if (!isUnloaded) {
200
+ if (this.supAdapter !== undefined && this.supAdapter[id]) {
196
201
  await this.createDPsForEachAdapter(id);
197
202
  if (this.config.createHtmlList) await this.createHtmlListDatapoints(id);
198
- this.log.debug(`Created datapoints for ${await this.capitalize(id)}`);
203
+ this.log.debug(`Created datapoints for ${this.capitalize(id)}`);
199
204
  }
205
+ } else {
206
+ return; // cancel run if unloaded was called.
200
207
  }
201
- } else {
202
- return; // cancel run if unloaded was called.
203
208
  }
209
+ } catch (error) {
210
+ this.errorReporting('[onReady - create and fill datapoints for each adapter]', error);
204
211
  }
205
- } catch (error) {
206
- this.errorReporting('[onReady - create and fill datapoints for each adapter]', error);
207
212
  }
208
213
 
209
214
  // create HTML list
210
215
  if (this.config.createHtmlList) await this.createHtmlListDatapoints();
211
216
 
212
- // update data in interval
217
+ //read data first at start
218
+ await this.main();
219
+
220
+ // update last contact data in interval
213
221
  await this.refreshData();
214
222
 
215
223
  // send overview for low battery devices
216
- if (this.config.checkSendBatteryMsg) await this.sendBatteryNotifyShedule();
224
+ if (this.config.checkSendBatteryMsgDaily) await this.sendBatteryNotifyShedule();
217
225
 
218
226
  // send overview of offline devices
219
227
  if (this.config.checkSendOfflineMsgDaily) await this.sendOfflineNotificationsShedule();
228
+
229
+ // send overview of upgradeable devices
230
+ if (this.config.checkSendUpgradeMsgDaily) await this.sendUpgradeNotificationsShedule();
220
231
  } catch (error) {
221
232
  this.errorReporting('[onReady]', error);
222
233
  this.terminate ? this.terminate(15) : process.exit(15);
@@ -228,13 +239,106 @@ class DeviceWatcher extends utils.Adapter {
228
239
  * @param {string} id
229
240
  * @param {ioBroker.State | null | undefined} state
230
241
  */
231
- onStateChange(id, state) {
232
- if (state) {
233
- // The state was changed
234
- this.log.warn(`state ${id} changed: ${state.val} (ack = ${state.ack})`);
235
- } else {
236
- // The state was deleted
237
- this.log.warn(`state ${id} deleted`);
242
+ async onStateChange(id, state) {
243
+ // Admin JSON for Adapter updates
244
+ if (id && state) {
245
+ this.log.debug(`State changed: ${id} changed ${state.val}`);
246
+ let batteryData;
247
+ let oldLowBatState;
248
+ let contactData;
249
+ let oldStatus;
250
+ let oldSignalStrength;
251
+
252
+ for (const i of this.listAllDevicesRaw) {
253
+ // On statechange update available datapoint
254
+ switch (id) {
255
+ case i.UpdateDP:
256
+ if (state.val) {
257
+ i.Upgradable = state.val;
258
+
259
+ await this.createLists();
260
+
261
+ await this.writeDatapoints();
262
+ await this.sendDeviceUpdatesNotification(i.Device, i.Adapter, i.Path);
263
+ }
264
+ break;
265
+
266
+ case i.SignalStrengthDP:
267
+ oldSignalStrength = i.SignalStrength;
268
+ i.SignalStrength = await this.calculateSignalStrength(state, i.adapterID);
269
+ if (oldSignalStrength !== i.SignalStrength) {
270
+ await this.createLists();
271
+
272
+ await this.writeDatapoints();
273
+ }
274
+ break;
275
+
276
+ case i.batteryDP:
277
+ oldLowBatState = i.LowBat;
278
+ batteryData = await this.getBatteryData(state.val, oldLowBatState, i.adapterID);
279
+
280
+ i.Battery = batteryData[0];
281
+ i.BatteryRaw = batteryData[2];
282
+ i.LowBat = await this.setLowbatIndicator(state.val, undefined, i.LowBatDP);
283
+
284
+ if (i.LowBat && oldLowBatState !== i.LowBat) {
285
+ await this.createLists();
286
+
287
+ await this.writeDatapoints();
288
+ if (this.config.checkSendBatteryMsg) {
289
+ await this.sendLowBatNoticiation(i.Device, i.Adapter, i.Battery, i.Path);
290
+ }
291
+ } else if (!i.LowBat && oldLowBatState !== i.LowBat) {
292
+ await this.createLists();
293
+
294
+ await this.writeDatapoints();
295
+ }
296
+ break;
297
+
298
+ case i.LowBatDP:
299
+ oldLowBatState = i.LowBat;
300
+ batteryData = await this.getBatteryData(i.BatteryRaw, state.val, i.adapterID);
301
+ i.Battery = batteryData[0];
302
+ i.BatteryRaw = batteryData[2];
303
+ i.LowBat = await this.setLowbatIndicator(i.BatteryRaw, state.val, i.LowBatDP);
304
+
305
+ if (i.LowBat && oldLowBatState !== i.LowBat) {
306
+ await this.createLists();
307
+
308
+ await this.writeDatapoints();
309
+ if (this.config.checkSendBatteryMsg) {
310
+ await this.sendLowBatNoticiation(i.Device, i.Adapter, i.Battery, i.Path);
311
+ }
312
+ } else if (!i.LowBat && oldLowBatState !== i.LowBat) {
313
+ await this.createLists();
314
+
315
+ await this.writeDatapoints();
316
+ }
317
+ break;
318
+ case i.UnreachDP:
319
+ case i.DeviceStateSelectorDP:
320
+ case i.rssiPeerSelectorDP:
321
+ case i.Path:
322
+ oldStatus = i.Status;
323
+ i.UnreachState = await this.getInitValue(i.UnreachDP);
324
+ contactData = await this.getOnlineState(i.Path, i.adapterID, i.UnreachDP, i.SignalStrength, i.UnreachState, i.DeviceStateSelectorDP, i.rssiPeerSelectorDP);
325
+ if (contactData !== undefined) {
326
+ i.LastContact = contactData[0];
327
+ i.Status = contactData[1];
328
+ i.SignalStrength = contactData[2];
329
+ }
330
+ if (i.Status !== oldStatus) {
331
+ await this.createLists();
332
+
333
+ await this.writeDatapoints();
334
+ }
335
+
336
+ if (i.Status && oldStatus !== i.Status && this.config.checkSendOfflineMsg) {
337
+ await this.sendOfflineNotifications(i.Device, i.Adapter, i.Status, i.LastContact, i.Path);
338
+ }
339
+ break;
340
+ }
341
+ }
238
342
  }
239
343
  }
240
344
 
@@ -274,11 +378,12 @@ class DeviceWatcher extends utils.Adapter {
274
378
 
275
379
  /**
276
380
  * refresh data with interval
381
+ * is neccessary to refresh lastContact data, especially of devices without state changes
277
382
  */
278
383
  async refreshData() {
279
384
  const nextTimeout = this.config.updateinterval * 1000;
280
385
 
281
- await this.main();
386
+ await this.checkLastContact();
282
387
 
283
388
  // Clear existing timeout
284
389
  if (this.refreshDataTimeout) {
@@ -303,25 +408,6 @@ class DeviceWatcher extends utils.Adapter {
303
408
  async main() {
304
409
  this.log.debug(`Function started: ${this.main.name}`);
305
410
 
306
- // fill datapoints for each adapter if selected
307
- try {
308
- for (const [id] of Object.entries(arrApart)) {
309
- if (!isUnloaded) {
310
- if (this.supAdapter !== undefined && this.supAdapter[id]) {
311
- if (this.config.createOwnFolder) {
312
- await this.createDataForEachAdapter(id);
313
- this.log.debug(`Created and filled data for ${await this.capitalize(id)}`);
314
- }
315
- }
316
- } else {
317
- this.log.warn('broke up');
318
- return; // cancel run if unloaded was called.
319
- }
320
- }
321
- } catch (error) {
322
- this.errorReporting('[main - create and fill datapoints for each adapter]', error);
323
- }
324
-
325
411
  // fill counts and lists of all selected adapter
326
412
  try {
327
413
  await this.createDataOfAllAdapter();
@@ -330,6 +416,20 @@ class DeviceWatcher extends utils.Adapter {
330
416
  this.errorReporting('[main - create data of all adapter]', error);
331
417
  }
332
418
 
419
+ // fill datapoints for each adapter if selected
420
+ if (this.config.createOwnFolder) {
421
+ try {
422
+ for (const [id] of Object.entries(arrApart)) {
423
+ if (this.supAdapter !== undefined && this.supAdapter[id]) {
424
+ await this.createDataForEachAdapter(id);
425
+ this.log.debug(`Created and filled data for ${this.capitalize(id)}`);
426
+ }
427
+ }
428
+ } catch (error) {
429
+ this.errorReporting('[main - create and fill datapoints for each adapter]', error);
430
+ }
431
+ }
432
+
333
433
  this.log.debug(`Function finished: ${this.main.name}`);
334
434
  } //<--End of main function
335
435
 
@@ -426,16 +526,22 @@ class DeviceWatcher extends utils.Adapter {
426
526
  const shortDeviceObject = await this.getForeignObjectAsync(shortCurrDeviceString);
427
527
  const shortshortDeviceObject = await this.getForeignObjectAsync(shortshortCurrDeviceString);
428
528
  let deviceName;
529
+ let folderName;
530
+ let deviceID;
429
531
 
430
532
  // Get ID with currDeviceString from datapoint
431
533
  switch (this.arrDev[i].adapterID) {
432
534
  // Get ID for Switchbot and ESPHome Devices
433
535
  case 'switchbotBle':
434
536
  case 'esphome':
435
- case 'fullybrowser':
537
+ case 'apcups':
436
538
  deviceName = await this.getInitValue(currDeviceString + this.arrDev[i].id);
437
539
  break;
438
540
 
541
+ case 'fullybrowser':
542
+ deviceName = (await this.getInitValue(currDeviceString + this.arrDev[i].id)) + ' ' + (await this.getInitValue(currDeviceString + this.arrDev[i].id2));
543
+ break;
544
+
439
545
  // Get ID with short currDeviceString from objectjson
440
546
  case 'hueExt':
441
547
  case 'hmrpc':
@@ -456,7 +562,9 @@ class DeviceWatcher extends utils.Adapter {
456
562
  // Get ID with short currDeviceString from datapoint
457
563
  case 'mihomeVacuum':
458
564
  case 'roomba':
459
- deviceName = await this.getInitValue(shortCurrDeviceString + this.arrDev[i].id);
565
+ folderName = shortCurrDeviceString.slice(shortCurrDeviceString.lastIndexOf('.') + 1);
566
+ deviceID = await this.getInitValue(shortCurrDeviceString + this.arrDev[i].id);
567
+ deviceName = `I${folderName} ${deviceID}`;
460
568
  break;
461
569
 
462
570
  //Get ID of foldername
@@ -495,6 +603,163 @@ class DeviceWatcher extends utils.Adapter {
495
603
  }
496
604
  }
497
605
 
606
+ /**
607
+ * calculate Signalstrength
608
+ * @param {object} deviceQualityState - State value
609
+ * @param {object} adapterID - adapter name
610
+ */
611
+ async calculateSignalStrength(deviceQualityState, adapterID) {
612
+ let linkQuality;
613
+ let mqttNukiValue;
614
+
615
+ if (deviceQualityState != null) {
616
+ switch (typeof deviceQualityState.val) {
617
+ case 'number':
618
+ if (this.config.trueState) {
619
+ linkQuality = deviceQualityState.val;
620
+ } else {
621
+ switch (adapterID) {
622
+ case 'roomba':
623
+ case 'sonoff':
624
+ linkQuality = deviceQualityState.val + '%'; // If quality state is already an percent value
625
+ break;
626
+ case 'lupusec':
627
+ linkQuality = deviceQualityState.val;
628
+ break;
629
+
630
+ default:
631
+ // If quality state is an RSSI value calculate in percent:
632
+ if (deviceQualityState.val == -255) {
633
+ linkQuality = ' - ';
634
+ } else if (deviceQualityState.val < 0) {
635
+ linkQuality = Math.min(Math.max(2 * (deviceQualityState.val + 100), 0), 100) + '%';
636
+ // If Quality State is an value between 0-255 (zigbee) calculate in percent:
637
+ } else if (deviceQualityState.val >= 0) {
638
+ linkQuality = parseFloat(((100 / 255) * deviceQualityState.val).toFixed(0)) + '%';
639
+ }
640
+ break;
641
+ }
642
+ }
643
+ break;
644
+
645
+ case 'string':
646
+ switch (adapterID) {
647
+ case 'netatmo':
648
+ // for Netatmo devices
649
+ linkQuality = deviceQualityState.val;
650
+ break;
651
+ case 'nukiExt':
652
+ linkQuality = ' - ';
653
+ break;
654
+ case 'mqttNuki':
655
+ linkQuality = deviceQualityState.val;
656
+ mqttNukiValue = parseInt(linkQuality);
657
+ if (this.config.trueState) {
658
+ linkQuality = deviceQualityState.val;
659
+ } else if (mqttNukiValue < 0) {
660
+ linkQuality = Math.min(Math.max(2 * (mqttNukiValue + 100), 0), 100) + '%';
661
+ // If Quality State is an value between 0-255 (zigbee) calculate in percent:
662
+ }
663
+ }
664
+ break;
665
+ }
666
+ } else {
667
+ linkQuality = ' - ';
668
+ }
669
+ return linkQuality;
670
+ }
671
+
672
+ /**
673
+ * get battery data
674
+ * @param {object} deviceBatteryState - State value
675
+ * @param {object} deviceLowBatState - State value
676
+ * @param {object} adapterID - adapter name
677
+ */
678
+ async getBatteryData(deviceBatteryState, deviceLowBatState, adapterID) {
679
+ let batteryHealthRaw;
680
+ let batteryHealth;
681
+ let isBatteryDevice;
682
+
683
+ if (deviceBatteryState === undefined) {
684
+ if (deviceLowBatState !== undefined) {
685
+ switch (deviceLowBatState) {
686
+ case 'none':
687
+ break;
688
+ default:
689
+ if (deviceLowBatState !== true || deviceLowBatState === 'NORMAL' || deviceLowBatState === 1) {
690
+ batteryHealth = 'ok';
691
+ isBatteryDevice = true;
692
+ } else {
693
+ batteryHealth = 'low';
694
+ isBatteryDevice = true;
695
+ }
696
+ break;
697
+ }
698
+ } else {
699
+ batteryHealth = ' - ';
700
+ }
701
+ } else {
702
+ switch (adapterID) {
703
+ case 'hmrpc':
704
+ if (deviceBatteryState === 0 || (deviceBatteryState && deviceBatteryState >= 6)) {
705
+ batteryHealth = ' - ';
706
+ } else {
707
+ batteryHealth = deviceBatteryState + 'V';
708
+ batteryHealthRaw = deviceBatteryState;
709
+ isBatteryDevice = true;
710
+ }
711
+ break;
712
+ default:
713
+ batteryHealth = deviceBatteryState + '%';
714
+ batteryHealthRaw = deviceBatteryState;
715
+ isBatteryDevice = true;
716
+ break;
717
+ }
718
+ }
719
+ return [batteryHealth, isBatteryDevice, batteryHealthRaw];
720
+ }
721
+
722
+ /**
723
+ *set low bat indicator
724
+ * @param {object} deviceBatteryState
725
+ * @param {object} deviceLowBatState
726
+ * @param {object} isLowBatDP
727
+ */
728
+
729
+ async setLowbatIndicator(deviceBatteryState, deviceLowBatState, isLowBatDP) {
730
+ let lowBatIndicator = false;
731
+ /*=============================================
732
+ = Set Lowbat indicator =
733
+ =============================================*/
734
+ if (deviceLowBatState !== null && isLowBatDP !== 'none') {
735
+ switch (typeof deviceLowBatState) {
736
+ case 'number':
737
+ if (deviceLowBatState === 0) {
738
+ lowBatIndicator = true;
739
+ }
740
+ break;
741
+
742
+ case 'string':
743
+ if (deviceLowBatState !== 'NORMAL') {
744
+ // Tado devices
745
+ lowBatIndicator = true;
746
+ }
747
+ break;
748
+
749
+ case 'boolean':
750
+ if (deviceLowBatState) {
751
+ lowBatIndicator = true;
752
+ }
753
+ break;
754
+ }
755
+ } else {
756
+ if (deviceBatteryState < this.config.minWarnBatterie) {
757
+ lowBatIndicator = true;
758
+ }
759
+ }
760
+ return lowBatIndicator;
761
+ }
762
+
498
763
  /**
499
764
  * get Last Contact
500
765
  * @param {object} selector - Selector
@@ -514,109 +779,167 @@ class DeviceWatcher extends utils.Adapter {
514
779
  }
515
780
 
516
781
  /**
517
- * Create Lists
782
+ * get online state and time
783
+ *
518
784
  */
519
- async createLists() {
520
- this.linkQualityDevices = [];
521
- this.batteryPowered = [];
522
- this.batteryLowPowered = [];
523
- this.listAllDevices = [];
524
- this.offlineDevices = [];
525
- this.batteryLowPoweredRaw = [];
526
- this.offlineDevicesRaw = [];
785
+ async getOnlineState(id, adapterID, unreachDP, linkQuality, deviceUnreachState, deviceStateSelectorDP, rssiPeerSelectorDP) {
786
+ let lastContactString;
787
+ let deviceState = 'Online';
527
788
 
528
- for (const device of this.listAllDevicesRaw) {
529
- /*---------- fill raw lists ----------*/
530
- // low bat list
531
- if (device['LowBat'] && device['Status'] !== 'Offline') {
532
- this.batteryLowPoweredRaw.push({
533
- Path: device['Path'],
534
- Device: device['Device'],
535
- Adapter: device['Adapter'],
536
- Battery: device['Battery'],
537
- });
538
- }
539
- // offline raw list
540
- if (device['Status'] === 'Offline') {
541
- this.offlineDevicesRaw.push({
542
- Path: device['Path'],
543
- Device: device['Device'],
544
- Adapter: device['Adapter'],
545
- 'Last contact': device['Last contact'],
546
- });
547
- }
789
+ try {
790
+ const deviceMainSelector = await this.getForeignStateAsync(id);
791
+ if (deviceMainSelector) {
792
+ const deviceUnreachSelector = await this.getForeignStateAsync(unreachDP);
793
+ const deviceStateSelector = await this.getForeignStateAsync(deviceStateSelectorDP); // for hmrpc devices
794
+ const rssiPeerSelector = await this.getForeignStateAsync(rssiPeerSelectorDP);
795
+ const lastContact = await this.getTimestamp(deviceMainSelector.ts);
796
+ const lastDeviceUnreachStateChange = deviceUnreachSelector != undefined ? await this.getTimestamp(deviceUnreachSelector.lc) : await this.getTimestamp(deviceMainSelector.ts);
797
+ // If there is no contact since user sets minutes add device in offline list
798
+ // calculate to days after 48 hours
799
+ switch (unreachDP) {
800
+ case 'none':
801
+ lastContactString = await this.getLastContact(deviceMainSelector.ts);
802
+ break;
548
803
 
549
- /*---------- fill user lists ----------*/
550
- if (!this.blacklistLists.includes(device['Path'])) {
551
- this.listAllDevices.push({
552
- Device: device['Device'],
553
- Adapter: device['Adapter'],
554
- Battery: device['Battery'],
555
- 'Signal strength': device['Signal strength'],
556
- 'Last contact': device['Last contact'],
557
- Status: device['Status'],
558
- });
559
- // LinkQuality lists
560
- if (device['Signal strength'] != ' - ') {
561
- this.linkQualityDevices.push({
562
- Device: device['Device'],
563
- Adapter: device['Adapter'],
564
- 'Signal strength': device['Signal strength'],
565
- });
566
- }
567
- // Battery lists
568
- if (device['isBatteryDevice']) {
569
- this.batteryPowered.push({
570
- Device: device['Device'],
571
- Adapter: device['Adapter'],
572
- Battery: device['Battery'],
573
- Status: device['Status'],
574
- });
575
- }
576
- // Low Bat lists
577
- if (device['LowBat'] && device['Status'] !== 'Offline') {
578
- this.batteryLowPowered.push({
579
- Device: device['Device'],
580
- Adapter: device['Adapter'],
581
- Battery: device['Battery'],
582
- });
804
+ default:
805
+ //State changed
806
+ if (adapterID === 'hmrpc') {
807
+ if (linkQuality !== ' - ') {
808
+ if (deviceUnreachState) {
809
+ lastContactString = await this.getLastContact(deviceMainSelector.lc);
810
+ } else {
811
+ lastContactString = await this.getLastContact(deviceMainSelector.ts);
812
+ }
813
+ } else {
814
+ if (deviceStateSelector) {
815
+ // because old hm devices don't send rssi states
816
+ lastContactString = await this.getLastContact(deviceStateSelector.ts);
817
+ } else if (rssiPeerSelector) {
818
+ // because old hm sensors don't send rssi/state values
819
+ lastContactString = await this.getLastContact(rssiPeerSelector.ts);
820
+ }
821
+ }
822
+ } else {
823
+ if (!deviceUnreachState) {
824
+ lastContactString = await this.getLastContact(deviceMainSelector.lc);
825
+ } else {
826
+ lastContactString = await this.getLastContact(deviceMainSelector.ts);
827
+ }
828
+ break;
829
+ }
583
830
  }
584
831
 
585
- // Offline List
586
- if (device['Status'] === 'Offline') {
587
- this.offlineDevices.push({
588
- Device: device['Device'],
589
- Adapter: device['Adapter'],
590
- 'Last contact': device['Last contact'],
591
- });
832
+ /*=============================================
833
+ = Set Online Status =
834
+ =============================================*/
835
+ if (this.maxMinutes !== undefined) {
836
+ switch (adapterID) {
837
+ case 'hmrpc':
838
+ case 'hmiP':
839
+ case 'maxcube':
840
+ if (this.maxMinutes[adapterID] <= 0) {
841
+ if (deviceUnreachState) {
842
+ deviceState = 'Offline'; //set online state to offline
843
+ linkQuality = '0%'; // set linkQuality to nothing
844
+ }
845
+ } else if (lastDeviceUnreachStateChange > this.maxMinutes[adapterID] && deviceUnreachState) {
846
+ deviceState = 'Offline'; //set online state to offline
847
+ linkQuality = '0%'; // set linkQuality to nothing
848
+ }
849
+ break;
850
+ case 'apcups':
851
+ case 'hue':
852
+ case 'hueExt':
853
+ case 'ping':
854
+ case 'deconz':
855
+ case 'shelly':
856
+ case 'sonoff':
857
+ case 'unifi':
858
+ case 'zigbee':
859
+ case 'zigbee2MQTT':
860
+ if (this.maxMinutes[adapterID] <= 0) {
861
+ if (!deviceUnreachState) {
862
+ deviceState = 'Offline'; //set online state to offline
863
+ linkQuality = '0%'; // set linkQuality to nothing
864
+ }
865
+ } else if (!deviceUnreachState && lastDeviceUnreachStateChange > this.maxMinutes[adapterID]) {
866
+ deviceState = 'Offline'; //set online state to offline
867
+ linkQuality = '0%'; // set linkQuality to nothing
868
+ }
869
+ break;
870
+ case 'mqttClientZigbee2Mqtt':
871
+ if (this.maxMinutes[adapterID] <= 0) {
872
+ if (deviceUnreachState !== 'online') {
873
+ deviceState = 'Offline'; //set online state to offline
874
+ linkQuality = '0%'; // set linkQuality to nothing
875
+ }
876
+ } else if (deviceUnreachState !== 'online' && lastDeviceUnreachStateChange > this.maxMinutes[adapterID]) {
877
+ deviceState = 'Offline'; //set online state to offline
878
+ linkQuality = '0%'; // set linkQuality to nothing
879
+ }
880
+ break;
881
+ case 'mihome':
882
+ if (deviceUnreachState !== undefined) {
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 (lastContact > this.maxMinutes[adapterID]) {
889
+ deviceState = 'Offline'; //set online state to offline
890
+ linkQuality = '0%'; // set linkQuality to nothing
891
+ }
892
+ } else {
893
+ if (this.config.mihomeMaxMinutes <= 0) {
894
+ if (this.maxMinutes[adapterID] <= 0) {
895
+ deviceState = 'Offline'; //set online state to offline
896
+ linkQuality = '0%'; // set linkQuality to nothing
897
+ }
898
+ } else if (lastContact > this.maxMinutes[adapterID]) {
899
+ deviceState = 'Offline'; //set online state to offline
900
+ linkQuality = '0%'; // set linkQuality to nothing
901
+ }
902
+ }
903
+ break;
904
+ default:
905
+ if (this.maxMinutes[adapterID] <= 0) {
906
+ if (!deviceUnreachState) {
907
+ deviceState = 'Offline'; //set online state to offline
908
+ linkQuality = '0%'; // set linkQuality to nothing
909
+ }
910
+ } else if (lastContact > this.maxMinutes[adapterID]) {
911
+ deviceState = 'Offline'; //set online state to offline
912
+ linkQuality = '0%'; // set linkQuality to nothing
913
+ }
914
+ break;
915
+ }
592
916
  }
593
917
  }
918
+ return [lastContactString, deviceState, linkQuality];
919
+ } catch (error) {
920
+ this.errorReporting('[getLastContact]', error);
594
921
  }
595
922
  }
596
923
 
597
924
  /**
598
- * Count devices for each type
925
+ * when was last contact of device
599
926
  */
600
- async countDevices() {
601
- // Count how many devices with link Quality
602
- this.linkQualityCount = this.linkQualityDevices.length;
603
-
604
- // Count how many devcies are offline
605
- this.offlineDevicesCount = this.offlineDevices.length;
606
-
607
- // Count how many devices are with battery
608
- this.batteryPoweredCount = this.batteryPowered.length;
609
-
610
- // 3d. Count how many devices are with low battery
611
- this.lowBatteryPoweredCount = this.batteryLowPowered.length;
612
-
613
- // Count how many devices are exists
614
- this.deviceCounter = this.listAllDevices.length;
615
-
616
- // raws
617
-
618
- // Count how many devcies are offline
619
- this.offlineDevicesCountRaw = this.offlineDevicesRaw.length;
927
+ async checkLastContact() {
928
+ for (const i of this.listAllDevicesRaw) {
929
+ const oldContactState = i.Status;
930
+ i.UnreachState = await this.getInitValue(i.UnreachDP);
931
+ const contactData = await this.getOnlineState(i.Path, i.adapterID, i.UnreachDP, i.SignalStrength, i.UnreachState, i.DeviceStateSelectorDP, i.rssiPeerSelectorDP);
932
+ if (contactData !== undefined) {
933
+ i.LastContact = contactData[0];
934
+ i.Status = contactData[1];
935
+ i.linkQuality = contactData[2];
936
+ }
937
+ if (oldContactState !== i.Status) {
938
+ await this.sendOfflineNotifications(i.Device, i.Adapter, i.Status, i.LastContact, i.Path);
939
+ }
940
+ }
941
+ await this.createLists();
942
+ await this.writeDatapoints();
620
943
  }
621
944
 
622
945
  /**
@@ -648,327 +971,127 @@ class DeviceWatcher extends utils.Adapter {
648
971
  /*=============================================
649
972
  = Get signal strength =
650
973
  =============================================*/
974
+ let deviceQualityDP = currDeviceString + this.arrDev[i].rssiState;
651
975
  let deviceQualityState;
652
- let linkQuality;
653
976
 
654
977
  switch (adapterID) {
655
978
  case 'mihomeVacuum':
656
- deviceQualityState = await this.getForeignStateAsync(shortCurrDeviceString + this.arrDev[i].rssiState);
979
+ deviceQualityDP = shortCurrDeviceString + this.arrDev[i].rssiState;
980
+ deviceQualityState = await this.getForeignStateAsync(deviceQualityDP);
657
981
  break;
658
982
 
659
983
  case 'netatmo':
660
- deviceQualityState = await this.getForeignStateAsync(currDeviceString + this.arrDev[i].rssiState);
984
+ deviceQualityState = await this.getForeignStateAsync(deviceQualityDP);
661
985
  if (!deviceQualityState) {
662
- deviceQualityState = await this.getForeignStateAsync(currDeviceString + this.arrDev[i].rfState);
986
+ deviceQualityDP = currDeviceString + this.arrDev[i].rfState;
987
+ deviceQualityState = await this.getForeignStateAsync(deviceQualityDP);
663
988
  }
664
989
  break;
665
990
 
666
991
  default:
667
- deviceQualityState = await this.getForeignStateAsync(currDeviceString + this.arrDev[i].rssiState);
992
+ deviceQualityState = await this.getForeignStateAsync(deviceQualityDP);
668
993
  break;
669
994
  }
995
+ //subscribe to states
996
+ this.subscribeForeignStatesAsync(deviceQualityDP);
670
997
 
671
- if (deviceQualityState != null) {
672
- switch (typeof deviceQualityState.val) {
673
- case 'number':
674
- if (this.config.trueState) {
675
- linkQuality = deviceQualityState.val;
676
- } else {
677
- switch (adapterID) {
678
- case 'roomba':
679
- case 'sonoff':
680
- linkQuality = deviceQualityState.val + '%'; // If quality state is already an percent value
681
- break;
682
- case 'lupusec':
683
- linkQuality = deviceQualityState.val;
684
- break;
685
-
686
- default:
687
- // If quality state is an RSSI value calculate in percent:
688
- if (deviceQualityState.val == -255) {
689
- linkQuality = ' - ';
690
- } else if (deviceQualityState.val < 0) {
691
- linkQuality = Math.min(Math.max(2 * (deviceQualityState.val + 100), 0), 100) + '%';
692
- // If Quality State is an value between 0-255 (zigbee) calculate in percent:
693
- } else if (deviceQualityState.val >= 0) {
694
- linkQuality = parseFloat(((100 / 255) * deviceQualityState.val).toFixed(0)) + '%';
695
- }
696
- break;
697
- }
698
- }
699
- break;
700
-
701
- case 'string':
702
- switch (adapterID) {
703
- case 'netatmo':
704
- // for Netatmo devices
705
- linkQuality = deviceQualityState.val;
706
- break;
707
- case 'nukiExt':
708
- linkQuality = ' - ';
709
- break;
710
- }
711
- break;
712
- }
713
- } else {
714
- linkQuality = ' - ';
715
- }
998
+ let linkQuality = await this.calculateSignalStrength(deviceQualityState, adapterID);
716
999
 
717
1000
  /*=============================================
718
1001
  = Get battery data =
719
1002
  =============================================*/
720
- let batteryHealth;
721
- let lowBatIndicator;
722
- let isBatteryDevice;
723
-
1003
+ let deviceBatteryStateDP;
1004
+ let deviceBatteryState;
724
1005
  // Get battery states
725
- let deviceBatteryState = await this.getInitValue(currDeviceString + this.arrDev[i].battery);
726
- if (deviceBatteryState === undefined) {
727
- deviceBatteryState = await this.getInitValue(currDeviceString + this.arrDev[i].battery2);
728
- }
729
-
730
- // Get battery states with short path
731
- let shortDeviceBatteryState = await this.getInitValue(shortCurrDeviceString + this.arrDev[i].battery);
732
- if (shortDeviceBatteryState === undefined) {
733
- shortDeviceBatteryState = await this.getInitValue(shortCurrDeviceString + this.arrDev[i].battery2);
734
- }
735
-
736
- // Get low bat states
737
- let deviceLowBatState = await this.getInitValue(currDeviceString + this.arrDev[i].isLowBat);
738
- if (deviceLowBatState === undefined) {
739
- deviceLowBatState = await this.getInitValue(currDeviceString + this.arrDev[i].isLowBat2);
740
- }
741
-
742
- if (!deviceBatteryState && !shortDeviceBatteryState) {
743
- if (deviceLowBatState !== undefined) {
744
- switch (this.arrDev[i].isLowBat || this.arrDev[i].isLowBat2) {
745
- case 'none':
746
- batteryHealth = ' - ';
747
- break;
748
- default:
749
- if (deviceLowBatState === false || deviceLowBatState === 'NORMAL' || deviceLowBatState === 1) {
750
- batteryHealth = 'ok';
751
- isBatteryDevice = true;
752
- } else {
753
- batteryHealth = 'low';
754
- isBatteryDevice = true;
755
- }
756
- break;
757
- }
758
- } else {
759
- batteryHealth = ' - ';
760
- }
761
- } else {
762
- switch (adapterID) {
763
- case 'hmrpc':
764
- if (deviceBatteryState === 0 || (deviceBatteryState && deviceBatteryState >= 6)) {
765
- batteryHealth = ' - ';
766
- } else {
767
- batteryHealth = deviceBatteryState + 'V';
768
- isBatteryDevice = true;
769
- }
770
- break;
771
-
772
- case 'hueExt':
773
- case 'mihomeVacuum':
774
- if (shortDeviceBatteryState) {
775
- batteryHealth = shortDeviceBatteryState + '%';
776
- isBatteryDevice = true;
777
- }
778
- break;
779
-
780
- default:
781
- batteryHealth = deviceBatteryState + '%';
782
- isBatteryDevice = true;
783
- }
784
- }
785
-
786
- /*=============================================
787
- = Set Lowbat indicator =
788
- =============================================*/
789
- switch (typeof deviceLowBatState) {
790
- case 'number':
791
- if (deviceLowBatState === 0) {
792
- lowBatIndicator = true;
793
- }
794
- break;
795
-
796
- case 'string':
797
- if (deviceLowBatState !== 'NORMAL') {
798
- // Tado devices
799
- lowBatIndicator = true;
800
- }
801
- break;
802
-
803
- case 'boolean':
804
- if (deviceLowBatState) {
805
- lowBatIndicator = true;
1006
+ switch (adapterID) {
1007
+ case 'hueExt':
1008
+ case 'mihomeVacuum':
1009
+ case 'mqttNuki':
1010
+ deviceBatteryStateDP = shortCurrDeviceString + this.arrDev[i].battery;
1011
+ deviceBatteryState = await this.getInitValue(deviceBatteryStateDP);
1012
+ if (deviceBatteryState === undefined) {
1013
+ deviceBatteryStateDP = shortCurrDeviceString + this.arrDev[i].battery2;
1014
+ deviceBatteryState = await this.getInitValue(deviceBatteryStateDP);
806
1015
  }
807
1016
  break;
808
-
809
- default: // if the battery state is under the set limit
810
- if (deviceBatteryState && deviceBatteryState < this.config.minWarnBatterie) {
811
- lowBatIndicator = true;
1017
+ default:
1018
+ deviceBatteryStateDP = currDeviceString + this.arrDev[i].battery;
1019
+ deviceBatteryState = await this.getInitValue(deviceBatteryStateDP);
1020
+ if (deviceBatteryState === undefined) {
1021
+ deviceBatteryStateDP = currDeviceString + this.arrDev[i].battery2;
1022
+ deviceBatteryState = await this.getInitValue(deviceBatteryStateDP);
812
1023
  }
813
1024
  break;
814
1025
  }
815
1026
 
816
- /*=============================================
817
- = Get last contact of device =
818
- =============================================*/
819
- let lastContactString;
820
- let deviceState = 'Online';
821
-
822
- const deviceMainSelector = await this.getForeignStateAsync(id);
823
- const deviceUnreachSelector = await this.getForeignStateAsync(currDeviceString + this.arrDev[i].reach);
824
- const deviceStateSelector = await this.getForeignStateAsync(shortCurrDeviceString + this.arrDev[i].stateValue); // for hmrpc devices
825
- const rssiPeerSelector = await this.getForeignStateAsync(currDeviceString + this.arrDev[i].rssiPeerState);
826
-
827
- if (deviceMainSelector) {
828
- try {
829
- const lastContact = await this.getTimestamp(deviceMainSelector.ts);
830
- const deviceUnreachState = await this.getInitValue(currDeviceString + this.arrDev[i].reach);
831
- const lastDeviceUnreachStateChange = deviceUnreachSelector != undefined ? await this.getTimestamp(deviceUnreachSelector.lc) : await this.getTimestamp(deviceMainSelector.ts);
832
- const shortDeviceUnreachState = await this.getForeignStateAsync(shortCurrDeviceString + this.arrDev[i].reach);
833
-
834
- // If there is no contact since user sets minutes add device in offline list
835
- // calculate to days after 48 hours
836
- switch (this.arrDev[i].reach) {
837
- case 'none':
838
- lastContactString = await this.getLastContact(deviceMainSelector.ts);
839
- break;
1027
+ // Get low bat states
1028
+ let isLowBatDP = currDeviceString + this.arrDev[i].isLowBat;
1029
+ let deviceLowBatState = await this.getInitValue(isLowBatDP);
1030
+ if (deviceLowBatState === undefined) {
1031
+ isLowBatDP = currDeviceString + this.arrDev[i].isLowBat2;
1032
+ deviceLowBatState = await this.getInitValue(isLowBatDP);
1033
+ }
1034
+ if (deviceLowBatState === undefined) isLowBatDP = 'none';
840
1035
 
841
- default:
842
- //State changed
843
- if (adapterID === 'hmrpc') {
844
- if (linkQuality !== ' - ') {
845
- if (deviceUnreachState) {
846
- lastContactString = await this.getLastContact(deviceMainSelector.lc);
847
- } else {
848
- lastContactString = await this.getLastContact(deviceMainSelector.ts);
849
- }
850
- } else {
851
- if (deviceStateSelector) {
852
- // because old hm devices don't send rssi states
853
- lastContactString = await this.getLastContact(deviceStateSelector.ts);
854
- } else if (rssiPeerSelector) {
855
- // because old hm sensors don't send rssi/state values
856
- lastContactString = await this.getLastContact(rssiPeerSelector.ts);
857
- }
858
- }
859
- } else {
860
- if (!deviceUnreachState) {
861
- lastContactString = await this.getLastContact(deviceMainSelector.lc);
862
- } else {
863
- lastContactString = await this.getLastContact(deviceMainSelector.ts);
864
- }
865
- break;
866
- }
867
- }
1036
+ //subscribe to states
1037
+ this.subscribeForeignStatesAsync(deviceBatteryStateDP);
1038
+ this.subscribeForeignStatesAsync(isLowBatDP);
868
1039
 
869
- /*=============================================
870
- = Set Online Status =
871
- =============================================*/
872
- if (this.maxMinutes !== undefined) {
873
- switch (adapterID) {
874
- case 'hmrpc':
875
- case 'hmiP':
876
- case 'maxcube':
877
- if (this.maxMinutes[adapterID] <= 0) {
878
- if (deviceUnreachState) {
879
- deviceState = 'Offline'; //set online state to offline
880
- linkQuality = '0%'; // set linkQuality to nothing
881
- }
882
- } else if (lastDeviceUnreachStateChange > this.maxMinutes[adapterID] && deviceUnreachState) {
883
- deviceState = 'Offline'; //set online state to offline
884
- linkQuality = '0%'; // set linkQuality to nothing
885
- }
886
- break;
887
- case 'ping':
888
- case 'deconz':
889
- if (this.maxMinutes[adapterID] <= 0) {
890
- if (!deviceUnreachState) {
891
- deviceState = 'Offline'; //set online state to offline
892
- linkQuality = '0%'; // set linkQuality to nothing
893
- }
894
- } else if (lastDeviceUnreachStateChange > this.maxMinutes[adapterID] && !deviceUnreachState) {
895
- deviceState = 'Offline'; //set online state to offline
896
- linkQuality = '0%'; // set linkQuality to nothing
897
- }
898
- break;
899
- case 'unifi':
900
- if (this.maxMinutes[adapterID] <= 0) {
901
- if (deviceUnreachState === 0) {
902
- deviceState = 'Offline'; //set online state to offline
903
- linkQuality = '0%'; // set linkQuality to nothing
904
- }
905
- } else if (this.maxMinutes !== undefined && lastContact > this.maxMinutes[adapterID]) {
906
- deviceState = 'Offline'; //set online state to offline
907
- linkQuality = '0%'; // set linkQuality to nothing
908
- }
909
- break;
910
- case 'shelly':
911
- case 'sonoff':
912
- if (this.maxMinutes[adapterID] <= 0) {
913
- if (!deviceUnreachState) {
914
- deviceState = 'Offline'; //set online state to offline
915
- linkQuality = '0%'; // set linkQuality to nothing
916
- }
917
- } else if (!deviceUnreachState && lastDeviceUnreachStateChange > this.maxMinutes[adapterID]) {
918
- deviceState = 'Offline'; //set online state to offline
919
- linkQuality = '0%'; // set linkQuality to nothing
920
- }
921
- break;
922
- case 'mihomeVacuum':
923
- if (this.maxMinutes[adapterID] <= 0) {
924
- if (!shortDeviceUnreachState) {
925
- deviceState = 'Offline'; //set online state to offline
926
- linkQuality = '0%'; // set linkQuality to nothing
927
- }
928
- } else if (lastContact > this.maxMinutes[adapterID]) {
929
- deviceState = 'Offline'; //set online state to offline
930
- linkQuality = '0%'; // set linkQuality to nothing
931
- }
932
- break;
933
- case 'mihome':
934
- if (this.arrDev[i].battery === 'none') {
935
- if (this.maxMinutes[adapterID] <= 0) {
936
- if (!deviceUnreachState) {
937
- deviceState = 'Offline'; //set online state to offline
938
- linkQuality = '0%'; // set linkQuality to nothing
939
- }
940
- } else if (lastContact > this.maxMinutes[adapterID]) {
941
- deviceState = 'Offline'; //set online state to offline
942
- linkQuality = '0%'; // set linkQuality to nothing
943
- }
944
- } else {
945
- if (this.config.mihomeMaxMinutes <= 0) {
946
- if (this.maxMinutes[adapterID] <= 0) {
947
- deviceState = 'Offline'; //set online state to offline
948
- linkQuality = '0%'; // set linkQuality to nothing
949
- }
950
- } else if (lastContact > this.maxMinutes[adapterID]) {
951
- deviceState = 'Offline'; //set online state to offline
952
- linkQuality = '0%'; // set linkQuality to nothing
953
- }
954
- }
955
- break;
956
- default:
957
- if (this.maxMinutes[adapterID] <= 0) {
958
- if (!deviceUnreachState) {
959
- deviceState = 'Offline'; //set online state to offline
960
- linkQuality = '0%'; // set linkQuality to nothing
961
- }
962
- } else if (lastContact > this.maxMinutes[adapterID]) {
963
- deviceState = 'Offline'; //set online state to offline
964
- linkQuality = '0%'; // set linkQuality to nothing
965
- }
966
- break;
967
- }
968
- }
969
- } catch (error) {
970
- this.errorReporting('[getLastContact]', error);
1040
+ const batteryData = await this.getBatteryData(deviceBatteryState, deviceLowBatState, adapterID);
1041
+ const batteryHealth = batteryData[0];
1042
+ const batteryHealthRaw = batteryData[2];
1043
+ const isBatteryDevice = batteryData[1];
1044
+ let lowBatIndicator;
1045
+
1046
+ if (isBatteryDevice) {
1047
+ lowBatIndicator = await this.setLowbatIndicator(deviceBatteryState, deviceLowBatState, isLowBatDP);
1048
+ }
1049
+
1050
+ /*=============================================
1051
+ = Get last contact of device =
1052
+ =============================================*/
1053
+ let unreachDP = currDeviceString + this.arrDev[i].reach;
1054
+ const deviceStateSelectorDP = shortCurrDeviceString + this.arrDev[i].stateValue;
1055
+ const rssiPeerSelectorDP = currDeviceString + this.arrDev[i].rssiPeerState;
1056
+
1057
+ let deviceUnreachState = await this.getInitValue(unreachDP);
1058
+ if (deviceUnreachState === undefined) {
1059
+ unreachDP = shortCurrDeviceString + this.arrDev[i].reach;
1060
+ deviceUnreachState = await this.getInitValue(shortCurrDeviceString + this.arrDev[i].reach);
1061
+ }
1062
+
1063
+ // subscribe to states
1064
+ this.subscribeForeignStatesAsync(id);
1065
+ this.subscribeForeignStatesAsync(unreachDP);
1066
+ this.subscribeForeignStatesAsync(deviceStateSelectorDP);
1067
+ this.subscribeForeignStatesAsync(rssiPeerSelectorDP);
1068
+
1069
+ const onlineState = await this.getOnlineState(id, adapterID, unreachDP, linkQuality, deviceUnreachState, deviceStateSelectorDP, rssiPeerSelectorDP);
1070
+ let deviceState;
1071
+ let lastContactString;
1072
+
1073
+ if (onlineState) {
1074
+ lastContactString = onlineState[0];
1075
+ deviceState = onlineState[1];
1076
+ linkQuality = onlineState[2];
1077
+ }
1078
+
1079
+ /*=============================================
1080
+ = Get update data =
1081
+ =============================================*/
1082
+ const deviceUpdateDP = currDeviceString + this.arrDev[i].upgrade;
1083
+ let isUpgradable;
1084
+
1085
+ if (this.config.checkSendDeviceUpgrade) {
1086
+ const deviceUpdateSelector = await this.getInitValue(deviceUpdateDP);
1087
+
1088
+ if (deviceUpdateSelector) {
1089
+ isUpgradable = true;
1090
+ } else if (!deviceUpdateSelector) {
1091
+ isUpgradable = false;
971
1092
  }
1093
+ // subscribe to states
1094
+ this.subscribeForeignStatesAsync(deviceUpdateDP);
972
1095
  }
973
1096
 
974
1097
  /*=============================================
@@ -976,32 +1099,52 @@ class DeviceWatcher extends utils.Adapter {
976
1099
  =============================================*/
977
1100
 
978
1101
  /* Add only devices with battery in the rawlist */
979
- if (this.listOnlyBattery) {
980
- if (isBatteryDevice) {
981
- this.listAllDevicesRaw.push({
982
- Path: id,
983
- Device: deviceName,
984
- Adapter: adapter,
985
- isBatteryDevice: isBatteryDevice,
986
- Battery: batteryHealth,
987
- LowBat: lowBatIndicator,
988
- 'Signal strength': linkQuality,
989
- 'Last contact': lastContactString,
990
- Status: deviceState,
991
- });
992
- }
1102
+ if (this.listOnlyBattery && isBatteryDevice) {
1103
+ this.listAllDevicesRaw.push({
1104
+ Path: id,
1105
+ Device: deviceName,
1106
+ adapterID: adapterID,
1107
+ Adapter: adapter,
1108
+ isBatteryDevice: isBatteryDevice,
1109
+ Battery: batteryHealth,
1110
+ BatteryRaw: batteryHealthRaw,
1111
+ batteryDP: deviceBatteryStateDP,
1112
+ LowBat: lowBatIndicator,
1113
+ LowBatDP: isLowBatDP,
1114
+ SignalStrengthDP: deviceQualityDP,
1115
+ SignalStrength: linkQuality,
1116
+ UnreachState: deviceUnreachState,
1117
+ UnreachDP: unreachDP,
1118
+ DeviceStateSelectorDP: deviceStateSelectorDP,
1119
+ rssiPeerSelectorDP: rssiPeerSelectorDP,
1120
+ LastContact: lastContactString,
1121
+ Status: deviceState,
1122
+ UpdateDP: deviceUpdateDP,
1123
+ Upgradable: isUpgradable,
1124
+ });
993
1125
  } else {
994
1126
  /* Add all devices */
995
1127
  this.listAllDevicesRaw.push({
996
1128
  Path: id,
997
1129
  Device: deviceName,
1130
+ adapterID: adapterID,
998
1131
  Adapter: adapter,
999
1132
  isBatteryDevice: isBatteryDevice,
1000
1133
  Battery: batteryHealth,
1134
+ BatteryRaw: batteryHealthRaw,
1135
+ batteryDP: deviceBatteryStateDP,
1001
1136
  LowBat: lowBatIndicator,
1002
- 'Signal strength': linkQuality,
1003
- 'Last contact': lastContactString,
1137
+ LowBatDP: isLowBatDP,
1138
+ SignalStrengthDP: deviceQualityDP,
1139
+ SignalStrength: linkQuality,
1140
+ UnreachState: deviceUnreachState,
1141
+ UnreachDP: unreachDP,
1142
+ DeviceStateSelectorDP: deviceStateSelectorDP,
1143
+ rssiPeerSelectorDP: rssiPeerSelectorDP,
1144
+ LastContact: lastContactString,
1004
1145
  Status: deviceState,
1146
+ UpdateDP: deviceUpdateDP,
1147
+ Upgradable: isUpgradable,
1005
1148
  });
1006
1149
  }
1007
1150
  } else {
@@ -1010,9 +1153,127 @@ class DeviceWatcher extends utils.Adapter {
1010
1153
  }
1011
1154
  } // <-- end of loop
1012
1155
  await this.createLists();
1013
- await this.countDevices();
1014
1156
  } // <-- end of createData
1015
1157
 
1158
+ /**
1159
+ * Create Lists
1160
+ */
1161
+ async createLists() {
1162
+ this.linkQualityDevices = [];
1163
+ this.batteryPowered = [];
1164
+ this.batteryLowPowered = [];
1165
+ this.listAllDevices = [];
1166
+ this.offlineDevices = [];
1167
+ this.batteryLowPoweredRaw = [];
1168
+ this.offlineDevicesRaw = [];
1169
+ this.upgradableList = [];
1170
+
1171
+ for (const device of this.listAllDevicesRaw) {
1172
+ /*---------- fill raw lists ----------*/
1173
+ // low bat list
1174
+ if (device.LowBat && device.Status !== 'Offline') {
1175
+ this.batteryLowPoweredRaw.push({
1176
+ Path: device.Path,
1177
+ Device: device.Device,
1178
+ Adapter: device.Adapter,
1179
+ Battery: device.Battery,
1180
+ });
1181
+ }
1182
+ // offline raw list
1183
+ if (device.Status === 'Offline') {
1184
+ this.offlineDevicesRaw.push({
1185
+ Path: device.Path,
1186
+ Device: device.Device,
1187
+ Adapter: device.Adapter,
1188
+ 'Last contact': device.LastContact,
1189
+ });
1190
+ }
1191
+
1192
+ /*---------- fill user lists ----------*/
1193
+ if (!this.blacklistLists.includes(device.Path)) {
1194
+ this.listAllDevices.push({
1195
+ Device: device.Device,
1196
+ Adapter: device.Adapter,
1197
+ Battery: device.Battery,
1198
+ 'Signal strength': device.SignalStrength,
1199
+ 'Last contact': device.LastContact,
1200
+ Status: device.Status,
1201
+ });
1202
+ // LinkQuality lists
1203
+ if (device.SignalStrength != ' - ') {
1204
+ this.linkQualityDevices.push({
1205
+ Device: device.Device,
1206
+ Adapter: device.Adapter,
1207
+ 'Signal strength': device.SignalStrength,
1208
+ });
1209
+ }
1210
+ // Battery lists
1211
+ if (device['isBatteryDevice']) {
1212
+ this.batteryPowered.push({
1213
+ Device: device.Device,
1214
+ Adapter: device.Adapter,
1215
+ Battery: device.Battery,
1216
+ Status: device.Status,
1217
+ });
1218
+ }
1219
+ // Low Bat lists
1220
+ if (device.LowBat && device.Status !== 'Offline') {
1221
+ this.batteryLowPowered.push({
1222
+ Device: device.Device,
1223
+ Adapter: device.Adapter,
1224
+ Battery: device.Battery,
1225
+ });
1226
+ }
1227
+
1228
+ // Offline List
1229
+ if (device.Status === 'Offline') {
1230
+ this.offlineDevices.push({
1231
+ Device: device.Device,
1232
+ Adapter: device.Adapter,
1233
+ 'Last contact': device.LastContact,
1234
+ });
1235
+ }
1236
+
1237
+ // Device update List
1238
+ if (device.Upgradable) {
1239
+ this.upgradableList.push({
1240
+ Device: device.Device,
1241
+ Adapter: device.Adapter,
1242
+ });
1243
+ }
1244
+ }
1245
+ }
1246
+ await this.countDevices();
1247
+ }
1248
+
1249
+ /**
1250
+ * Count devices for each type
1251
+ */
1252
+ async countDevices() {
1253
+ // Count how many devices with link Quality
1254
+ this.linkQualityCount = this.linkQualityDevices.length;
1255
+
1256
+ // Count how many devcies are offline
1257
+ this.offlineDevicesCount = this.offlineDevices.length;
1258
+
1259
+ // Count how many devices are with battery
1260
+ this.batteryPoweredCount = this.batteryPowered.length;
1261
+
1262
+ // 3d. Count how many devices are with low battery
1263
+ this.lowBatteryPoweredCount = this.batteryLowPowered.length;
1264
+
1265
+ // Count how many devices are exists
1266
+ this.deviceCounter = this.listAllDevices.length;
1267
+
1268
+ // Count how many devices has update available
1269
+ this.upgradableDevicesCount = this.upgradableList.length;
1270
+
1271
+ // raws
1272
+
1273
+ // Count how many devcies are offline
1274
+ this.offlineDevicesCountRaw = this.offlineDevicesRaw.length;
1275
+ }
1276
+
1016
1277
  /**
1017
1278
  * @param {string} adptName - Adapter name
1018
1279
  */
@@ -1023,10 +1284,11 @@ class DeviceWatcher extends utils.Adapter {
1023
1284
  await this.resetVars(); // reset the arrays and counts
1024
1285
 
1025
1286
  try {
1026
- for (let i = 0; i < this.arrDev.length; i++) {
1027
- if (this.arrDev[i].adapterID.includes(adptName)) {
1287
+ for (const i of this.listAllDevicesRaw) {
1288
+ if (i.adapterID.includes(adptName)) {
1028
1289
  // list device only if selected adapter matched with device
1029
- await this.createData(i);
1290
+ await this.createLists();
1291
+ await this.writeDatapoints();
1030
1292
  }
1031
1293
  }
1032
1294
 
@@ -1048,16 +1310,8 @@ class DeviceWatcher extends utils.Adapter {
1048
1310
  await this.resetVars(); // reset the arrays and counts
1049
1311
 
1050
1312
  for (let i = 0; i < this.arrDev.length; i++) {
1051
- if (!isUnloaded) {
1052
- await this.createData(i);
1053
- } else {
1054
- return; // cancel run if unloaded was called.
1055
- }
1313
+ await this.createData(i);
1056
1314
  }
1057
-
1058
- // send message if new devices are offline
1059
- if (this.config.checkSendOfflineMsg) await this.sendOfflineNotifications();
1060
-
1061
1315
  await this.writeDatapoints(); // fill the datapoints
1062
1316
  } catch (error) {
1063
1317
  this.errorReporting('[createDataOfAllAdapter]', error);
@@ -1190,6 +1444,26 @@ class DeviceWatcher extends utils.Adapter {
1190
1444
  } catch (error) {
1191
1445
  this.errorReporting('[sendNotification Lovelace]', error);
1192
1446
  }
1447
+
1448
+ // Synochat Notification
1449
+ try {
1450
+ if (this.config.instanceSynochat) {
1451
+ //first check if instance is living
1452
+ const synochatAliveState = await this.getInitValue('system.adapter.' + this.config.instanceSynochat + '.alive');
1453
+
1454
+ if (!synochatAliveState) {
1455
+ this.log.warn('Synochat instance is not running. Message could not be sent. Please check your instance configuration.');
1456
+ } else {
1457
+ if (this.config.channelSynochat !== undefined) {
1458
+ await this.setForeignStateAsync(`${this.config.instanceSynochat}.${this.config.channelSynochat}.message`, text);
1459
+ } else {
1460
+ this.log.warn('Synochat channel is not set. Message could not be sent. Please check your instance configuration.');
1461
+ }
1462
+ }
1463
+ }
1464
+ } catch (error) {
1465
+ this.errorReporting('[sendNotification Synochat]', error);
1466
+ }
1193
1467
  } // <-- End of sendNotification function
1194
1468
 
1195
1469
  /**
@@ -1224,15 +1498,20 @@ class DeviceWatcher extends utils.Adapter {
1224
1498
  let deviceList = '';
1225
1499
 
1226
1500
  for (const id of this.batteryLowPoweredRaw) {
1227
- if (!this.blacklistNotify.includes(id['Path'])) {
1228
- deviceList = `${deviceList}\n${id['Device']} (${id['Battery']})`;
1501
+ if (!this.blacklistNotify.includes(id.Path)) {
1502
+ if (!this.config.showAdapterNameinMsg) {
1503
+ deviceList = `${deviceList}\n${id.Device} (${id.Battery})`;
1504
+ } else {
1505
+ // Add adaptername if checkbox is checked true in options by user
1506
+ deviceList = `${deviceList}\n${id.Adapter}: ${id.Device} (${id.Battery})`;
1507
+ }
1229
1508
  }
1230
1509
  }
1231
1510
  if (deviceList.length > 0) {
1232
1511
  this.log.info(`Niedrige Batteriezustände: ${deviceList}`);
1233
1512
  this.setStateAsync('lastNotification', `Niedrige Batteriezustände: ${deviceList}`, true);
1234
1513
 
1235
- this.sendNotification(`Niedriege Batteriezustände: ${deviceList}`);
1514
+ this.sendNotification(`Niedrige Batteriezustände: ${deviceList}`);
1236
1515
  }
1237
1516
  } catch (error) {
1238
1517
  this.errorReporting('[sendBatteryNotifyShedule]', error);
@@ -1241,37 +1520,66 @@ class DeviceWatcher extends utils.Adapter {
1241
1520
  }
1242
1521
  } //<--End of battery notification
1243
1522
 
1523
+ /**
1524
+ * check if device updates are available and send notification
1525
+ * @param {string} deviceName
1526
+ * @param {string} adapter
1527
+ * @param {string} battery
1528
+ * @param {string} devicePath
1529
+ **/
1530
+ async sendLowBatNoticiation(deviceName, adapter, battery, devicePath) {
1531
+ this.log.debug(`Start the function: ${this.sendLowBatNoticiation.name}`);
1532
+
1533
+ try {
1534
+ let msg = '';
1535
+ let deviceList = '';
1536
+
1537
+ if (!this.blacklistNotify.includes(devicePath)) {
1538
+ if (!this.config.showAdapterNameinMsg) {
1539
+ deviceList = `${deviceList}\n${deviceName} (${battery})`;
1540
+ } else {
1541
+ deviceList = `${deviceList}\n${adapter}: ${deviceName} (${battery})`;
1542
+ }
1543
+ }
1544
+ msg = `Gerät mit geringer Batterie erkannt: \n`;
1545
+
1546
+ this.log.info(msg + deviceList);
1547
+ await this.setStateAsync('lastNotification', msg + deviceList, true);
1548
+ await this.sendNotification(msg + deviceList);
1549
+ } catch (error) {
1550
+ this.errorReporting('[sendLowBatNoticiation]', error);
1551
+ }
1552
+ this.log.debug(`Finished the function: ${this.sendLowBatNoticiation.name}`);
1553
+ }
1554
+
1244
1555
  /**
1245
1556
  * send message if an device is offline
1246
1557
  */
1247
- async sendOfflineNotifications() {
1558
+ async sendOfflineNotifications(deviceName, adapter, status, lastContact, devicePath) {
1248
1559
  this.log.debug(`Start the function: ${this.sendOfflineNotifications.name}`);
1249
1560
 
1250
1561
  try {
1251
1562
  let msg = '';
1252
1563
  let deviceList = '';
1253
1564
 
1254
- for (const id of this.offlineDevicesRaw) {
1255
- if (!this.blacklistNotify.includes(id['Path'])) {
1256
- deviceList = `${deviceList}\n${id['Device']} (${id['Last contact']})`;
1565
+ if (!this.blacklistNotify.includes(devicePath)) {
1566
+ if (!this.config.showAdapterNameinMsg) {
1567
+ deviceList = `${deviceList}\n${deviceName} (${lastContact})`;
1568
+ } else {
1569
+ deviceList = `${deviceList}\n${adapter}: ${deviceName} (${lastContact})`;
1257
1570
  }
1258
1571
  }
1259
- if (deviceList.length !== this.offlineDevicesCountRawOld) {
1260
- if (deviceList.length == 0) {
1261
- msg = 'Alle Geräte sind Online.';
1262
- } else if (deviceList.length == 1) {
1263
- // make singular if it is only one device
1264
- msg = 'Folgendes Gerät ist seit einiger Zeit nicht erreichbar: \n';
1265
- } else if (deviceList.length >= 2) {
1266
- //make plural if it is more than one device
1267
- msg = `Folgende Geräte sind seit einiger Zeit nicht erreichbar: \n`;
1268
- }
1269
-
1270
- this.log.info(msg + deviceList);
1271
- this.offlineDevicesCountRawOld = deviceList.length;
1272
- await this.setStateAsync('lastNotification', msg + deviceList, true);
1273
- await this.sendNotification(msg + deviceList);
1572
+ if (status === 'Online') {
1573
+ // make singular if it is only one device
1574
+ msg = 'Folgendes Gerät ist wieder erreichbar: \n';
1575
+ } else if (status === 'Offline') {
1576
+ //make plural if it is more than one device
1577
+ msg = `Folgendes Gerät ist seit einiger Zeit nicht erreichbar: \n`;
1274
1578
  }
1579
+
1580
+ this.log.info(msg + deviceList);
1581
+ await this.setStateAsync('lastNotification', msg + deviceList, true);
1582
+ await this.sendNotification(msg + deviceList);
1275
1583
  } catch (error) {
1276
1584
  this.errorReporting('[sendOfflineMessage]', error);
1277
1585
  }
@@ -1310,8 +1618,12 @@ class DeviceWatcher extends utils.Adapter {
1310
1618
  let deviceList = '';
1311
1619
 
1312
1620
  for (const id of this.offlineDevicesRaw) {
1313
- if (!this.blacklistNotify.includes(id['Path'])) {
1314
- deviceList = `${deviceList}\n${id['Device']} (${id['Last contact']})`;
1621
+ if (!this.blacklistNotify.includes(id.Path)) {
1622
+ if (!this.config.showAdapterNameinMsg) {
1623
+ deviceList = `${deviceList}\n${id.Device} (${id.LastContact})`;
1624
+ } else {
1625
+ deviceList = `${deviceList}\n${id.Adapter}: ${id.Device} (${id.LastContact})`;
1626
+ }
1315
1627
  }
1316
1628
  }
1317
1629
 
@@ -1328,6 +1640,91 @@ class DeviceWatcher extends utils.Adapter {
1328
1640
  }
1329
1641
  } //<--End of daily offline notification
1330
1642
 
1643
+ /**
1644
+ * check if device updates are available and send notification
1645
+ * @param {string} deviceName
1646
+ * @param {string} adapter
1647
+ * @param {string} devicePath
1648
+ **/
1649
+ async sendDeviceUpdatesNotification(deviceName, adapter, devicePath) {
1650
+ this.log.debug(`Start the function: ${this.sendDeviceUpdatesNotification.name}`);
1651
+
1652
+ try {
1653
+ let msg = '';
1654
+ let deviceList = '';
1655
+
1656
+ if (!this.blacklistNotify.includes(devicePath)) {
1657
+ if (!this.config.showAdapterNameinMsg) {
1658
+ deviceList = `${deviceList}\n${deviceName}`;
1659
+ } else {
1660
+ deviceList = `${deviceList}\n${adapter}: ${deviceName}`;
1661
+ }
1662
+ }
1663
+ msg = `Neue Geräte Updates vorhanden: \n`;
1664
+
1665
+ this.log.info(msg + deviceList);
1666
+ await this.setStateAsync('lastNotification', msg + deviceList, true);
1667
+ await this.sendNotification(msg + deviceList);
1668
+ } catch (error) {
1669
+ this.errorReporting('[sendDeviceUpdatesNotification]', error);
1670
+ }
1671
+ this.log.debug(`Finished the function: ${this.sendDeviceUpdatesNotification.name}`);
1672
+ }
1673
+
1674
+ /**
1675
+ * send shedule message with offline devices
1676
+ */
1677
+ async sendUpgradeNotificationsShedule() {
1678
+ const time = this.config.checkSendUpgradeTime.split(':');
1679
+
1680
+ const checkDays = []; // list of selected days
1681
+
1682
+ // push the selected days in list
1683
+ if (this.config.checkUpgradeMonday) checkDays.push(1);
1684
+ if (this.config.checkUpgradeTuesday) checkDays.push(2);
1685
+ if (this.config.checkUpgradeWednesday) checkDays.push(3);
1686
+ if (this.config.checkUpgradeThursday) checkDays.push(4);
1687
+ if (this.config.checkUpgradeFriday) checkDays.push(5);
1688
+ if (this.config.checkUpgradeSaturday) checkDays.push(6);
1689
+ if (this.config.checkUpgradeSunday) checkDays.push(0);
1690
+
1691
+ if (checkDays.length >= 1) {
1692
+ // check if an day is selected
1693
+ this.log.debug(`Number of selected days for daily Upgrade message: ${checkDays.length}. Send Message on: ${checkDays.join(', ')} ...`);
1694
+ } else {
1695
+ this.log.warn(`No days selected for daily Upgrade message. Please check the instance configuration!`);
1696
+ return; // cancel function if no day is selected
1697
+ }
1698
+
1699
+ if (!isUnloaded) {
1700
+ const cron = '10 ' + time[1] + ' ' + time[0] + ' * * ' + checkDays;
1701
+ schedule.scheduleJob(cron, () => {
1702
+ try {
1703
+ let deviceList = '';
1704
+
1705
+ for (const id of this.upgradableList) {
1706
+ if (!this.blacklistNotify.includes(id.Path)) {
1707
+ if (!this.config.showAdapterNameinMsg) {
1708
+ deviceList = `${deviceList}\n${id.Device}`;
1709
+ } else {
1710
+ deviceList = `${deviceList}\n${id.Adapter}: ${id.Device}`;
1711
+ }
1712
+ }
1713
+ }
1714
+
1715
+ if (deviceList.length > 0) {
1716
+ this.log.info(`Geräte Upgrade: ${deviceList}`);
1717
+ this.setStateAsync('lastNotification', `Geräte Upgrade: ${deviceList}`, true);
1718
+
1719
+ this.sendNotification(`Geräte Upgrade:\n${deviceList}`);
1720
+ }
1721
+ } catch (error) {
1722
+ this.errorReporting('[sendUpgradeNotificationsShedule]', error);
1723
+ }
1724
+ });
1725
+ }
1726
+ } //<--End of daily offline notification
1727
+
1331
1728
  /**
1332
1729
  * reset arrays and counts
1333
1730
  */
@@ -1341,13 +1738,11 @@ class DeviceWatcher extends utils.Adapter {
1341
1738
  this.batteryPowered = [];
1342
1739
  this.batteryLowPowered = [];
1343
1740
  this.listAllDevices = [];
1344
- this.listAllDevicesRaw = [];
1741
+ //this.listAllDevicesRaw = [];
1345
1742
 
1346
1743
  // raws
1347
1744
  this.batteryLowPoweredRaw = [];
1348
1745
  this.offlineDevicesRaw = [];
1349
- this.lowBatteryPoweredCountRaw = 0;
1350
- this.offlineDevicesCountRaw = 0;
1351
1746
 
1352
1747
  // counts
1353
1748
  this.offlineDevicesCount = 0;
@@ -1355,6 +1750,7 @@ class DeviceWatcher extends utils.Adapter {
1355
1750
  this.linkQualityCount = 0;
1356
1751
  this.batteryPoweredCount = 0;
1357
1752
  this.lowBatteryPoweredCount = 0;
1753
+ this.upgradableDevicesCount = 0;
1358
1754
 
1359
1755
  this.log.debug(`Function finished: ${this.resetVars.name}`);
1360
1756
  } // <-- end of resetVars
@@ -1380,14 +1776,15 @@ class DeviceWatcher extends utils.Adapter {
1380
1776
  await this.setStateAsync(`${dpSubFolder}countAll`, { val: this.deviceCounter, ack: true });
1381
1777
  await this.setStateAsync(`${dpSubFolder}batteryCount`, { val: this.batteryPoweredCount, ack: true });
1382
1778
  await this.setStateAsync(`${dpSubFolder}lowBatteryCount`, { val: this.lowBatteryPoweredCount, ack: true });
1779
+ await this.setStateAsync(`${dpSubFolder}upgradableCount`, { val: this.upgradableDevicesCount, ack: true });
1383
1780
 
1384
- if (this.deviceCounter == 0) {
1781
+ if (this.deviceCounter === 0) {
1385
1782
  // if no device is count, write the JSON List with default value
1386
1783
  this.listAllDevices = [{ Device: '--none--', Adapter: '', Battery: '', 'Last contact': '', 'Signal strength': '' }];
1387
1784
  }
1388
1785
  await this.setStateAsync(`${dpSubFolder}listAll`, { val: JSON.stringify(this.listAllDevices), ack: true });
1389
1786
 
1390
- if (this.linkQualityCount == 0) {
1787
+ if (this.linkQualityCount === 0) {
1391
1788
  // if no device is count, write the JSON List with default value
1392
1789
  this.linkQualityDevices = [{ Device: '--none--', Adapter: '', 'Signal strength': '' }];
1393
1790
  }
@@ -1396,14 +1793,8 @@ class DeviceWatcher extends utils.Adapter {
1396
1793
  val: JSON.stringify(this.linkQualityDevices),
1397
1794
  ack: true,
1398
1795
  });
1399
- //write HTML list
1400
- if (this.config.createHtmlList)
1401
- await this.setStateAsync(`${dpSubFolder}linkQualityListHTML`, {
1402
- val: await this.creatLinkQualityListHTML(this.linkQualityDevices, this.linkQualityCount),
1403
- ack: true,
1404
- });
1405
1796
 
1406
- if (this.offlineDevicesCount == 0) {
1797
+ if (this.offlineDevicesCount === 0) {
1407
1798
  // if no device is count, write the JSON List with default value
1408
1799
  this.offlineDevices = [{ Device: '--none--', Adapter: '', 'Last contact': '' }];
1409
1800
  }
@@ -1412,14 +1803,18 @@ class DeviceWatcher extends utils.Adapter {
1412
1803
  val: JSON.stringify(this.offlineDevices),
1413
1804
  ack: true,
1414
1805
  });
1415
- //write HTML list
1416
- if (this.config.createHtmlList)
1417
- await this.setStateAsync(`${dpSubFolder}offlineListHTML`, {
1418
- val: await this.createOfflineListHTML(this.offlineDevices, this.offlineDevicesCount),
1419
- ack: true,
1420
- });
1421
1806
 
1422
- if (this.batteryPoweredCount == 0) {
1807
+ if (this.upgradableDevicesCount === 0) {
1808
+ // if no device is count, write the JSON List with default value
1809
+ this.upgradableList = [{ Device: '--none--', Adapter: '', 'Last contact': '' }];
1810
+ }
1811
+ //write JSON list
1812
+ await this.setStateAsync(`${dpSubFolder}upgradableList`, {
1813
+ val: JSON.stringify(this.upgradableList),
1814
+ ack: true,
1815
+ });
1816
+
1817
+ if (this.batteryPoweredCount === 0) {
1423
1818
  // if no device is count, write the JSON List with default value
1424
1819
  this.batteryPowered = [{ Device: '--none--', Adapter: '', Battery: '' }];
1425
1820
  }
@@ -1428,14 +1823,8 @@ class DeviceWatcher extends utils.Adapter {
1428
1823
  val: JSON.stringify(this.batteryPowered),
1429
1824
  ack: true,
1430
1825
  });
1431
- //write HTML list
1432
- if (this.config.createHtmlList)
1433
- await this.setStateAsync(`${dpSubFolder}batteryListHTML`, {
1434
- val: await this.createBatteryListHTML(this.batteryPowered, this.batteryPoweredCount, false),
1435
- ack: true,
1436
- });
1437
1826
 
1438
- if (this.lowBatteryPoweredCount == 0) {
1827
+ if (this.lowBatteryPoweredCount === 0) {
1439
1828
  // if no device is count, write the JSON List with default value
1440
1829
  this.batteryLowPowered = [{ Device: '--none--', Adapter: '', Battery: '' }];
1441
1830
  }
@@ -1444,12 +1833,26 @@ class DeviceWatcher extends utils.Adapter {
1444
1833
  val: JSON.stringify(this.batteryLowPowered),
1445
1834
  ack: true,
1446
1835
  });
1836
+
1447
1837
  //write HTML list
1448
- if (this.config.createHtmlList)
1838
+ if (this.config.createHtmlList) {
1839
+ await this.setStateAsync(`${dpSubFolder}linkQualityListHTML`, {
1840
+ val: await this.creatLinkQualityListHTML(this.linkQualityDevices, this.linkQualityCount),
1841
+ ack: true,
1842
+ });
1843
+ await this.setStateAsync(`${dpSubFolder}offlineListHTML`, {
1844
+ val: await this.createOfflineListHTML(this.offlineDevices, this.offlineDevicesCount),
1845
+ ack: true,
1846
+ });
1847
+ await this.setStateAsync(`${dpSubFolder}batteryListHTML`, {
1848
+ val: await this.createBatteryListHTML(this.batteryPowered, this.batteryPoweredCount, false),
1849
+ ack: true,
1850
+ });
1449
1851
  await this.setStateAsync(`${dpSubFolder}lowBatteryListHTML`, {
1450
1852
  val: await this.createBatteryListHTML(this.batteryLowPowered, this.lowBatteryPoweredCount, true),
1451
1853
  ack: true,
1452
1854
  });
1855
+ }
1453
1856
 
1454
1857
  // create timestamp of last run
1455
1858
  const lastCheck = this.formatDate(new Date(), 'DD.MM.YYYY') + ' - ' + this.formatDate(new Date(), 'hh:mm:ss');
@@ -1488,7 +1891,7 @@ class DeviceWatcher extends utils.Adapter {
1488
1891
  html += `<tr>
1489
1892
  <td><font>${device.Device}</font></td>
1490
1893
  <td align=center><font>${device.Adapter}</font></td>
1491
- <td align=right><font>${device['Signal strength']}</font></td>
1894
+ <td align=right><font>${device.SignalStrength}</font></td>
1492
1895
  </tr>`;
1493
1896
  }
1494
1897
 
@@ -1507,7 +1910,7 @@ class DeviceWatcher extends utils.Adapter {
1507
1910
  return a.localeCompare(b);
1508
1911
  });
1509
1912
  let html = `<center>
1510
- <b>Offline Devices: <font color=${deviceCount == 0 ? '#3bcf0e' : 'orange'}>${deviceCount}</b><small></small></font>
1913
+ <b>Offline Devices: <font color=${deviceCount === 0 ? '#3bcf0e' : 'orange'}>${deviceCount}</b><small></small></font>
1511
1914
  <p></p>
1512
1915
  </center>
1513
1916
  <table width=100%>
@@ -1524,7 +1927,7 @@ class DeviceWatcher extends utils.Adapter {
1524
1927
  html += `<tr>
1525
1928
  <td><font>${device.Device}</font></td>
1526
1929
  <td align=center><font>${device.Adapter}</font></td>
1527
- <td align=center><font color=orange>${device['Last contact']}</font></td>
1930
+ <td align=center><font color=orange>${device.LastContact}</font></td>
1528
1931
  </tr>`;
1529
1932
  }
1530
1933
 
@@ -1544,7 +1947,7 @@ class DeviceWatcher extends utils.Adapter {
1544
1947
  return a.localeCompare(b);
1545
1948
  });
1546
1949
  let html = `<center>
1547
- <b>${isLowBatteryList == true ? 'Schwache ' : ''}Batterie Devices: <font color=${isLowBatteryList == true ? (deviceCount > 0 ? 'orange' : '#3bcf0e') : ''}>${deviceCount}</b></font>
1950
+ <b>${isLowBatteryList === true ? 'Schwache ' : ''}Batterie Devices: <font color=${isLowBatteryList === true ? (deviceCount > 0 ? 'orange' : '#3bcf0e') : ''}>${deviceCount}</b></font>
1548
1951
  <p></p>
1549
1952
  </center>
1550
1953
  <table width=100%>
@@ -1792,6 +2195,54 @@ class DeviceWatcher extends utils.Adapter {
1792
2195
  },
1793
2196
  native: {},
1794
2197
  });
2198
+
2199
+ await this.setObjectNotExistsAsync(`${adptName}.upgradableCount`, {
2200
+ type: 'state',
2201
+ common: {
2202
+ name: {
2203
+ en: 'Number of devices with available updates ',
2204
+ de: 'Anzahl der Geräte mit verfügbaren Updates',
2205
+ ru: 'Количество устройств с доступными обновлениями',
2206
+ pt: 'Número de dispositivos com atualizações disponíveis',
2207
+ nl: 'Nummer van apparatuur met beschikbare updates',
2208
+ fr: 'Nombre de dispositifs avec mises à jour disponibles',
2209
+ it: 'Numero di dispositivi con aggiornamenti disponibili',
2210
+ es: 'Número de dispositivos con actualizaciones disponibles',
2211
+ pl: 'Liczba urządzeń z dostępną aktualizacją',
2212
+ uk: 'Кількість пристроїв з доступними оновленнями',
2213
+ 'zh-cn': '现有更新的装置数目',
2214
+ },
2215
+ type: 'number',
2216
+ role: 'value',
2217
+ read: true,
2218
+ write: false,
2219
+ },
2220
+ native: {},
2221
+ });
2222
+
2223
+ await this.setObjectNotExistsAsync(`${adptName}.upgradableList`, {
2224
+ type: 'state',
2225
+ common: {
2226
+ name: {
2227
+ en: 'JSON List of devices with available updates ',
2228
+ de: 'JSON Liste der Geräte mit verfügbaren Updates',
2229
+ ru: 'ДЖСОН Список устройств с доступными обновлениями',
2230
+ pt: 'J. Lista de dispositivos com atualizações disponíveis',
2231
+ nl: 'JSON List van apparatuur met beschikbare updates',
2232
+ fr: 'JSON Liste des appareils avec mises à jour disponibles',
2233
+ it: 'JSON Elenco dei dispositivi con aggiornamenti disponibili',
2234
+ es: 'JSON Lista de dispositivos con actualizaciones disponibles',
2235
+ pl: 'JSON Lista urządzeń korzystających z aktualizacji',
2236
+ uk: 'Сонце Перелік пристроїв з доступними оновленнями',
2237
+ 'zh-cn': '附 件 现有最新设备清单',
2238
+ },
2239
+ type: 'array',
2240
+ role: 'json',
2241
+ read: true,
2242
+ write: false,
2243
+ },
2244
+ native: {},
2245
+ });
1795
2246
  }
1796
2247
 
1797
2248
  /**