iobroker.device-watcher 0.3.0 → 1.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/main.js CHANGED
@@ -19,12 +19,6 @@ class DeviceWatcher extends utils.Adapter {
19
19
  useFormatDate: true,
20
20
  });
21
21
 
22
- this.on('ready', this.onReady.bind(this));
23
- //this.on('stateChange', this.onStateChange.bind(this));
24
- // this.on('objectChange', this.onObjectChange.bind(this));
25
- // this.on('message', this.onMessage.bind(this));
26
- this.on('unload', this.onUnload.bind(this));
27
-
28
22
  // arrays
29
23
  this.offlineDevices = [];
30
24
  this.linkQualityDevices = [];
@@ -44,6 +38,9 @@ class DeviceWatcher extends utils.Adapter {
44
38
 
45
39
  this.deviceReachable = '';
46
40
 
41
+ // Interval timer
42
+ this.refreshDataTimeout = null;
43
+
47
44
  // arrays of supported adapters
48
45
  this.arrApart = {
49
46
  alexa2: {
@@ -89,6 +86,13 @@ class DeviceWatcher extends utils.Adapter {
89
86
  'reach': '.present',
90
87
  'isLowBat': '.batterylow'
91
88
  },
89
+ harmony: {
90
+ 'Selektor': 'harmony.*.hubConnected',
91
+ 'adapter': 'harmony',
92
+ 'battery': 'none',
93
+ 'reach': '.hubConnected',
94
+ 'isLowBat': 'none'
95
+ },
92
96
  homematic: {
93
97
  'Selektor': 'hm-rpc.*.UNREACH',
94
98
  'adapter': 'homematic',
@@ -112,6 +116,13 @@ class DeviceWatcher extends utils.Adapter {
112
116
  'reach': '.reachable',
113
117
  'isLowBat': 'none'
114
118
  },
119
+ jeelink: {
120
+ 'Selektor': 'jeelink.*.lowBatt',
121
+ 'adapter': 'jeelink',
122
+ 'battery': 'none',
123
+ 'reach': 'none',
124
+ 'isLowBat': '.lowBatt'
125
+ },
115
126
  mihome: {
116
127
  'Selektor': 'mihome.*.percent',
117
128
  'adapter': 'miHome',
@@ -127,18 +138,20 @@ class DeviceWatcher extends utils.Adapter {
127
138
  'isLowBat': 'none'
128
139
  },
129
140
  mihomeVacuum: {
130
- 'Selektor': 'mihome-vacuum.*.wifi_signal',
141
+ 'Selektor': 'mihome-vacuum.*.connection',
131
142
  'adapter': 'mihomeVacuum',
132
- 'rssiState': '.wifi_signal',
143
+ 'rssiState': '.deviceInfo.wifi_signal',
133
144
  'battery': '.info.battery',
134
145
  'battery2': '.control.battary_life',
135
- 'reach': '.connection',
136
- 'isLowBat': 'none'
146
+ 'reach': '.info.connection',
147
+ 'isLowBat': 'none',
148
+ 'id': '.deviceInfo.model'
137
149
  },
138
150
  nukiExt: {
139
- 'Selektor': 'nuki-extended.*.batteryCritical',
151
+ 'Selektor': 'nuki-extended.*.lastDataUpdate',
140
152
  'adapter': 'nuki-extended',
141
- 'battery': '.batteryCharge',
153
+ 'rssiState': 'none',
154
+ 'battery': '.batteryChargeState',
142
155
  'reach': 'none',
143
156
  'isLowBat': '.batteryCritical'
144
157
  },
@@ -192,27 +205,28 @@ class DeviceWatcher extends utils.Adapter {
192
205
  'battery': '.Battery.level',
193
206
  'reach': '.ready',
194
207
  'isLowBat': '.Battery.isLow'
208
+ },
209
+ test: { // Only for Dev
210
+ 'Selektor': '0_userdata.*.UNREACH',
211
+ 'adapter': 'homematic',
212
+ 'rssiState': '.RSSI_DEVICE',
213
+ 'battery': '.OPERATING_VOLTAGE',
214
+ 'reach': '.UNREACH',
215
+ 'isLowBat': '.LOW_BAT',
216
+ 'isLowBat2': '.LOWBAT'
195
217
  }
196
218
  };
197
- }
198
219
 
199
- async onReady() {
200
- this.log.debug(`Adapter ${adapterName} was started`);
220
+ this.on('ready', this.onReady.bind(this));
221
+ this.on('stateChange', this.onStateChange.bind(this));
222
+ // this.on('objectChange', this.onObjectChange.bind(this));
223
+ // this.on('message', this.onMessage.bind(this));
224
+ this.on('unload', this.onUnload.bind(this));
201
225
 
202
- try {
203
- await this.main();
204
- await this.writeDatapoints();
205
- this.log.debug('all done, exiting');
206
- this.terminate ? this.terminate('Everything done. Going to terminate till next schedule', 11) : process.exit(0);
207
- } catch (error) {
208
- this.errorReporting('[onReady]', error);
209
- this.terminate ? this.terminate(15) : process.exit(15);
210
- }
211
226
  }
212
227
 
213
-
214
- async main() {
215
- this.log.debug(`Function started: ${this.main.name}`);
228
+ async onReady() {
229
+ this.log.debug(`Adapter ${adapterName} was started`);
216
230
 
217
231
  try {
218
232
  this.supAdapter = {
@@ -222,9 +236,11 @@ class DeviceWatcher extends utils.Adapter {
222
236
  enocean: this.config.enoceanDevices,
223
237
  esphome: this.config.esphomeDevices,
224
238
  fritzdect: this.config.fritzdectDevices,
239
+ harmony: this.config.harmonyDevices,
225
240
  homematic: this.config.homematicDevices,
226
241
  hue: this.config.hueDevices,
227
242
  hueExt: this.config.hueExtDevices,
243
+ jeelink: this.config.jeelinkDevices,
228
244
  mihome: this.config.mihomeDevices,
229
245
  mihomeGW: this.config.mihomeDevices,
230
246
  mihomeVacuum: this.config.mihomeVacuumDevices,
@@ -236,28 +252,90 @@ class DeviceWatcher extends utils.Adapter {
236
252
  switchbotBle: this.config.switchbotBleDevices,
237
253
  zigbee: this.config.zigbeeDevices,
238
254
  zwave: this.config.zwaveDevices,
255
+ test: false // Only for Dev
239
256
  };
240
257
 
241
258
  for (const [id] of Object.entries(this.arrApart)) {
242
259
  if (this.supAdapter[id]) {
243
260
  this.arrDev.push(this.arrApart[id]);
244
261
  this.adapterSelected.push(await this.capitalize(id));
245
- this.log.debug(JSON.stringify(this.arrDev));
246
262
  }
247
263
  }
248
264
 
249
265
  //Check if an Adapter is selected.
250
266
  if (this.adapterSelected.length >= 1) {
267
+ // show list in debug log
268
+ this.log.debug(JSON.stringify(this.arrDev));
269
+
251
270
  this.log.info(`Number of selected adapters: ${this.adapterSelected.length}. Loading data from: ${(this.adapterSelected).join(', ')} ...`);
252
271
  } else {
253
272
  this.log.warn(`No adapter selected. Please check the instance configuration!`);
254
273
  return; // cancel run if no adapter is selected
255
274
  }
256
275
 
276
+ //create Blacklist
277
+ try {
278
+ await this.createBlacklist();
279
+ } catch (error) {
280
+ this.errorReporting('[onReady - create blacklist]', error);
281
+ }
282
+
283
+ // update data now
284
+ await this.main();
285
+
286
+ // update data in interval
287
+ await this.refreshData();
288
+
289
+ } catch (error) {
290
+ this.errorReporting('[onReady]', error);
291
+ this.terminate ? this.terminate(15) : process.exit(15);
292
+ }
293
+ }
294
+
295
+ /**
296
+ * Is called if a subscribed state changes
297
+ * @param {string} id
298
+ * @param {ioBroker.State | null | undefined} state
299
+ */
300
+ async onStateChange(id, state) {
301
+ if (state) {
302
+ // The state was changed
303
+ this.log.debug(`state ${id} changed: ${state.val} (ack = ${state.ack})`);
304
+ await this.main();
305
+ } else {
306
+ // The state was deleted
307
+ this.log.debug(`state ${id} deleted`);
308
+ }
309
+ }
310
+
311
+ async refreshData() {
312
+ const nextTimeout = this.config.updateinterval * 1000;
313
+
314
+ await this.main();
315
+
316
+ // Clear existing timeout
317
+ if (this.refreshDataTimeout) {
318
+ this.log.debug('clearing old refresh timeout');
319
+ this.clearTimeout(this.refreshDataTimeout);
320
+ }
321
+
322
+ this.refreshDataTimeout = this.setTimeout(() => {
323
+ this.log.debug('Updating Data');
324
+
325
+ this.refreshDataTimeout = null;
326
+ this.refreshData();
327
+ }, nextTimeout);
328
+ }
329
+
330
+ async main() {
331
+ this.log.debug(`Function started: ${this.main.name}`);
332
+
333
+ try {
334
+
257
335
  //create and fill datapoints for each adapter if selected
258
336
  try {
259
337
  for (const [id] of Object.entries(this.arrApart)) {
260
- if (this.supAdapter[id]) {
338
+ if ((this.supAdapter !== undefined) && (this.supAdapter[id])) {
261
339
 
262
340
  if (this.config.createOwnFolder) {
263
341
  await this.createDPsForEachAdapter(id);
@@ -290,7 +368,7 @@ class DeviceWatcher extends utils.Adapter {
290
368
 
291
369
 
292
370
  /**
293
- * @param {string} [sentence] - Word which should be capitalize
371
+ * @param {string} sentence - Word which should be capitalize
294
372
  **/
295
373
  async capitalize(sentence) {
296
374
  //make the first letter uppercase
@@ -298,7 +376,7 @@ class DeviceWatcher extends utils.Adapter {
298
376
  }
299
377
 
300
378
  /**
301
- * @param {object} [obj] - State of datapoint
379
+ * @param {object} obj - State of datapoint
302
380
  **/
303
381
  async getInitValue(obj) {
304
382
  //state can be null or undefinded
@@ -307,7 +385,7 @@ class DeviceWatcher extends utils.Adapter {
307
385
  }
308
386
 
309
387
  /**
310
- * @param {object} [obj] - State of own datapoint
388
+ * @param {object} obj - State of own datapoint
311
389
  **/
312
390
  async getOwnInitValue(obj) {
313
391
  //state can be null or undefinded for own states
@@ -317,7 +395,7 @@ class DeviceWatcher extends utils.Adapter {
317
395
 
318
396
  //create datapoints for each adapter
319
397
  /**
320
- * @param {object} [adptName] - Adaptername of devices
398
+ * @param {object} adptName - Adaptername of devices
321
399
  **/
322
400
  async createDPsForEachAdapter(adptName) {
323
401
 
@@ -643,19 +721,24 @@ class DeviceWatcher extends utils.Adapter {
643
721
  });
644
722
  }
645
723
 
646
- /**
647
- * @param {object} [i] - Device Object
648
- **/
649
- async createData(i) {
650
- const devices = await this.getForeignStatesAsync(this.arrDev[i].Selektor);
651
- const deviceAdapterName = await this.capitalize(this.arrDev[i].adapter);
724
+ async createBlacklist() {
725
+ this.log.debug(`Function started: ${this.createBlacklist.name}`);
726
+
652
727
  const myBlacklist = this.config.tableBlacklist;
653
728
 
654
- /*---------- Loop for blacklist ----------*/
655
729
  for (const i in myBlacklist) {
656
730
  this.blacklistArr.push(myBlacklist[i].device);
657
- this.log.debug(`Found items on the blacklist: ${this.blacklistArr}`);
658
731
  }
732
+ this.log.info(`Found items on the blacklist: ${this.blacklistArr}`);
733
+ this.log.debug(`Function finished: ${this.createBlacklist.name}`);
734
+ }
735
+
736
+ /**
737
+ * @param {object} i - Device Object
738
+ **/
739
+ async createData(i) {
740
+ const devices = await this.getForeignStatesAsync(this.arrDev[i].Selektor);
741
+ const deviceAdapterName = await this.capitalize(this.arrDev[i].adapter);
659
742
 
660
743
  /*---------- Start of second main loop ----------*/
661
744
  for (const [id] of Object.entries(devices)) {
@@ -664,25 +747,29 @@ class DeviceWatcher extends utils.Adapter {
664
747
  const currDeviceString = id.slice(0, (id.lastIndexOf('.') + 1) - 1);
665
748
  const shortCurrDeviceString = currDeviceString.slice(0, (currDeviceString.lastIndexOf('.') + 1) - 1);
666
749
 
667
- //Get device name
750
+ // Get device name
668
751
  const deviceObject = await this.getForeignObjectAsync(currDeviceString);
669
752
  const shortDeviceObject = await this.getForeignObjectAsync(shortCurrDeviceString);
670
753
  let deviceName;
671
754
 
672
755
  switch (this.arrDev[i].adapter) {
673
- case 'switchbotBle': //Get ID for Switchbot and ESPHome Devices
756
+ case 'switchbotBle': // Get ID for Switchbot and ESPHome Devices
674
757
  case 'esphome':
675
758
  deviceName = await this.getInitValue(currDeviceString + this.arrDev[i].id);
676
759
  break;
677
760
 
678
761
  case 'hue-extended':
679
- case 'mihomeVacuum':
680
762
  case 'homematic':
763
+ case 'nuki-extended':
681
764
  if (shortDeviceObject && typeof shortDeviceObject === 'object') {
682
765
  deviceName = shortDeviceObject.common.name;
683
766
  }
684
767
  break;
685
768
 
769
+ case 'mihomeVacuum':
770
+ deviceName = await this.getInitValue(shortCurrDeviceString + this.arrDev[i].id);
771
+ break;
772
+
686
773
  default:
687
774
  if (deviceObject && typeof deviceObject === 'object') {
688
775
  deviceName = deviceObject.common.name;
@@ -692,23 +779,28 @@ class DeviceWatcher extends utils.Adapter {
692
779
 
693
780
  const deviceMainSelector = await this.getForeignStateAsync(id);
694
781
 
695
- // 3. Get battery states
782
+ // Get battery states
696
783
  const deviceBatteryState = await this.getInitValue(currDeviceString + this.arrDev[i].battery);
697
784
  const shortDeviceBatteryState = await this.getInitValue(shortCurrDeviceString + this.arrDev[i].battery);
698
785
  const shortDeviceBatteryState2 = await this.getInitValue(shortCurrDeviceString + this.arrDev[i].battery2);
699
786
 
700
- // 1. Get link quality
787
+ // Get link quality
701
788
  let deviceQualityState;
702
789
  let linkQuality;
703
790
 
704
791
  switch (this.arrDev[i].adapter) {
705
792
  case 'sonoff':
706
- case 'mihomeVacuum':
707
793
  case 'homematic':
708
794
  deviceQualityState = await this.getForeignStateAsync(currDeviceString + this.arrDev[i].rssiState);
709
795
  break;
796
+
797
+ case 'mihomeVacuum':
798
+ deviceQualityState = await this.getForeignStateAsync(shortCurrDeviceString + this.arrDev[i].rssiState);
799
+ break;
800
+
710
801
  default:
711
802
  deviceQualityState = await this.getForeignStateAsync(id);
803
+ break;
712
804
  }
713
805
 
714
806
  if ((deviceQualityState) && (typeof deviceQualityState.val === 'number')) {
@@ -741,14 +833,13 @@ class DeviceWatcher extends utils.Adapter {
741
833
  );
742
834
  }
743
835
  } else {
744
- // no linkQuality available for powered devices
745
- linkQuality = ' - ';
836
+ linkQuality = ' - '; // no linkQuality available for powered devices
746
837
  }
747
838
 
748
- // 1b. Count how many devices with link Quality
839
+ // Count how many devices with link Quality
749
840
  this.linkQualityCount = this.linkQualityDevices.length;
750
841
 
751
- // 2. When was the last contact to the device?
842
+ // When was the last contact to the device?
752
843
  let lastContactString;
753
844
 
754
845
  let deviceState = 'Online';
@@ -758,6 +849,7 @@ class DeviceWatcher extends utils.Adapter {
758
849
  const lastContact = Math.round((time.getTime() - deviceMainSelector.ts) / 1000 / 60);
759
850
  const lastStateChange = Math.round((time.getTime() - deviceMainSelector.lc) / 1000 / 60);
760
851
  const deviceUnreachState = await this.getInitValue(currDeviceString + this.arrDev[i].reach);
852
+ const shortDeviceUnreachState = await this.getInitValue(shortCurrDeviceString + this.arrDev[i].reach);
761
853
 
762
854
  const getLastContact = async () => {
763
855
  lastContactString = this.formatDate(new Date((deviceMainSelector.ts)), 'hh:mm') + ' Uhr';
@@ -781,21 +873,29 @@ class DeviceWatcher extends utils.Adapter {
781
873
  return lastContactString;
782
874
  };
783
875
 
784
- // 2b. wenn seit X Minuten kein Kontakt mehr besteht, nimm Gerät in Liste auf
785
- //Rechne auf Tage um, wenn mehr als 48 Stunden seit letztem Kontakt vergangen sind
786
- switch (this.arrDev[i].adapter) {
787
- case 'ping':
788
- //State changed
789
- if (!deviceUnreachState) {
790
- await getLastStateChange();
791
- } else {
792
- await getLastContact();
793
- }
876
+ // If there is no contact since user sets minutes add device in offline list
877
+ // calculate to days after 48 hours
878
+ switch (this.arrDev[i].reach) {
879
+ case 'none':
880
+ await getLastContact();
794
881
  break;
795
882
 
796
883
  default:
797
- await getLastContact();
798
- break;
884
+ //State changed
885
+ if (this.arrDev[i].adapter == 'homematic') {
886
+ if (deviceUnreachState) {
887
+ await getLastStateChange();
888
+ } else {
889
+ await getLastContact();
890
+ }
891
+ } else {
892
+ if ((!deviceUnreachState)) {
893
+ await getLastStateChange();
894
+ } else {
895
+ await getLastContact();
896
+ }
897
+ break;
898
+ }
799
899
  }
800
900
 
801
901
  const pushOfflineDevice = async () => {
@@ -820,6 +920,8 @@ class DeviceWatcher extends utils.Adapter {
820
920
  }
821
921
  };
822
922
 
923
+ // await this.subscribeForeignStatesAsync(currDeviceString + this.arrDev[i].reach);
924
+
823
925
  switch (this.arrDev[i].adapter) {
824
926
  case 'alexa2':
825
927
  if (this.config.alexa2MaxMinutes === -1) {
@@ -827,7 +929,7 @@ class DeviceWatcher extends utils.Adapter {
827
929
  deviceState = 'Offline'; //set online state to offline
828
930
  await pushOfflineDevice();
829
931
  }
830
- } else if (lastContact > this.config.alexa2MaxMinutes) {
932
+ } else if ((lastStateChange > this.config.alexa2MaxMinutes) && (!deviceUnreachState)) {
831
933
  deviceState = 'Offline'; //set online state to offline
832
934
  await pushOfflineDevice();
833
935
  }
@@ -849,7 +951,7 @@ class DeviceWatcher extends utils.Adapter {
849
951
  deviceState = 'Offline'; //set online state to offline
850
952
  await pushOfflineDevice();
851
953
  }
852
- } else if (lastContact > this.config.deconzMaxMinutes) {
954
+ } else if ((lastStateChange > this.config.deconzMaxMinutes) && (!deviceUnreachState)) {
853
955
  deviceState = 'Offline'; //set online state to offline
854
956
  await pushOfflineDevice();
855
957
  }
@@ -871,7 +973,7 @@ class DeviceWatcher extends utils.Adapter {
871
973
  deviceState = 'Offline'; //set online state to offline
872
974
  await pushOfflineDevice();
873
975
  }
874
- } else if (lastContact > this.config.esphomeMaxMinutes) {
976
+ } else if ((lastStateChange > this.config.esphomeMaxMinutes) && (!deviceUnreachState)) {
875
977
  deviceState = 'Offline'; //set online state to offline
876
978
  await pushOfflineDevice();
877
979
  }
@@ -882,7 +984,18 @@ class DeviceWatcher extends utils.Adapter {
882
984
  deviceState = 'Offline'; //set online state to offline
883
985
  await pushOfflineDevice();
884
986
  }
885
- } else if (lastContact > this.config.fritzdectMaxMinutes) {
987
+ } else if ((lastStateChange > this.config.fritzdectMaxMinutes) && (!deviceUnreachState)) {
988
+ deviceState = 'Offline'; //set online state to offline
989
+ await pushOfflineDevice();
990
+ }
991
+ break;
992
+ case 'harmony':
993
+ if (this.config.harmonyMaxMinutes === -1) {
994
+ if (!deviceUnreachState) {
995
+ deviceState = 'Offline'; //set online state to offline
996
+ await pushOfflineDevice();
997
+ }
998
+ } else if ((lastStateChange > this.config.harmonyMaxMinutes) && (!deviceUnreachState)) {
886
999
  deviceState = 'Offline'; //set online state to offline
887
1000
  await pushOfflineDevice();
888
1001
  }
@@ -893,7 +1006,7 @@ class DeviceWatcher extends utils.Adapter {
893
1006
  deviceState = 'Offline'; //set online state to offline
894
1007
  await pushOfflineDevice();
895
1008
  }
896
- } else if (lastContact > this.config.homematicMaxMinutes) {
1009
+ } else if ((lastStateChange > this.config.homematicMaxMinutes) && (deviceUnreachState)) {
897
1010
  deviceState = 'Offline'; //set online state to offline
898
1011
  await pushOfflineDevice();
899
1012
  }
@@ -904,7 +1017,7 @@ class DeviceWatcher extends utils.Adapter {
904
1017
  deviceState = 'Offline'; //set online state to offline
905
1018
  await pushOfflineDevice();
906
1019
  }
907
- } else if (lastContact > this.config.hueMaxMinutes) {
1020
+ } else if ((lastStateChange > this.config.hueMaxMinutes) && (!deviceUnreachState)) {
908
1021
  deviceState = 'Offline'; //set online state to offline
909
1022
  await pushOfflineDevice();
910
1023
  }
@@ -915,7 +1028,18 @@ class DeviceWatcher extends utils.Adapter {
915
1028
  deviceState = 'Offline'; //set online state to offline
916
1029
  await pushOfflineDevice();
917
1030
  }
918
- } else if (lastContact > this.config.hueextMaxMinutes) {
1031
+ } else if ((lastStateChange > this.config.hueextMaxMinutes) && (!deviceUnreachState)) {
1032
+ deviceState = 'Offline'; //set online state to offline
1033
+ await pushOfflineDevice();
1034
+ }
1035
+ break;
1036
+ case 'jeelink':
1037
+ if (this.config.jeelinkMaxMinutes === -1) {
1038
+ if (!deviceUnreachState) {
1039
+ deviceState = 'Offline'; //set online state to offline
1040
+ await pushOfflineDevice();
1041
+ }
1042
+ } else if (lastContact > this.config.jeelinkMaxMinutes) {
919
1043
  deviceState = 'Offline'; //set online state to offline
920
1044
  await pushOfflineDevice();
921
1045
  }
@@ -926,18 +1050,18 @@ class DeviceWatcher extends utils.Adapter {
926
1050
  deviceState = 'Offline'; //set online state to offline
927
1051
  await pushOfflineDevice();
928
1052
  }
929
- } else if (lastContact > this.config.mihomeMaxMinutes) {
1053
+ } else if ((lastStateChange > this.config.mihomeMaxMinutes) && (!deviceUnreachState)) {
930
1054
  deviceState = 'Offline'; //set online state to offline
931
1055
  await pushOfflineDevice();
932
1056
  }
933
1057
  break;
934
1058
  case 'mihomeVacuum':
935
1059
  if (this.config.mihomeVacuumMaxMinutes === -1) {
936
- if (!deviceUnreachState) {
1060
+ if (!shortDeviceUnreachState) {
937
1061
  deviceState = 'Offline'; //set online state to offline
938
1062
  await pushOfflineDevice();
939
1063
  }
940
- } else if (lastContact > this.config.mihomeVacuumMaxMinutes) {
1064
+ } else if ((lastStateChange > this.config.mihomeVacuumMaxMinutes) && (!shortDeviceUnreachState)) {
941
1065
  deviceState = 'Offline'; //set online state to offline
942
1066
  await pushOfflineDevice();
943
1067
  }
@@ -970,7 +1094,7 @@ class DeviceWatcher extends utils.Adapter {
970
1094
  deviceState = 'Offline'; //set online state to offline
971
1095
  await pushOfflineDevice();
972
1096
  }
973
- } else if (lastContact > this.config.shellyMaxMinutes) {
1097
+ } else if ((lastStateChange > this.config.shellyMaxMinutes) && (!deviceUnreachState)) {
974
1098
  deviceState = 'Offline'; //set online state to offline
975
1099
  await pushOfflineDevice();
976
1100
  }
@@ -981,7 +1105,7 @@ class DeviceWatcher extends utils.Adapter {
981
1105
  deviceState = 'Offline'; //set online state to offline
982
1106
  await pushOfflineDevice();
983
1107
  }
984
- } else if (lastContact > this.config.sonoffMaxMinutes) {
1108
+ } else if ((lastStateChange > this.config.sonoffMaxMinutes) && (!deviceUnreachState)) {
985
1109
  deviceState = 'Offline'; //set online state to offline
986
1110
  await pushOfflineDevice();
987
1111
  }
@@ -992,7 +1116,7 @@ class DeviceWatcher extends utils.Adapter {
992
1116
  deviceState = 'Offline'; //set online state to offline
993
1117
  await pushOfflineDevice();
994
1118
  }
995
- } else if (lastContact > this.config.sonosMaxMinutes) {
1119
+ } else if ((lastStateChange > this.config.sonosMaxMinutes) && (!deviceUnreachState)) {
996
1120
  deviceState = 'Offline'; //set online state to offline
997
1121
  await pushOfflineDevice();
998
1122
  }
@@ -1014,7 +1138,7 @@ class DeviceWatcher extends utils.Adapter {
1014
1138
  deviceState = 'Offline'; //set online state to offline
1015
1139
  await pushOfflineDevice();
1016
1140
  }
1017
- } else if (lastContact > this.config.zigbeeMaxMinutes) {
1141
+ } else if ((lastStateChange > this.config.zigbeeMaxMinutes) && (!deviceUnreachState)) {
1018
1142
  deviceState = 'Offline'; //set online state to offline
1019
1143
  await pushOfflineDevice();
1020
1144
  }
@@ -1025,7 +1149,7 @@ class DeviceWatcher extends utils.Adapter {
1025
1149
  deviceState = 'Offline'; //set online state to offline
1026
1150
  await pushOfflineDevice();
1027
1151
  }
1028
- } else if (lastContact > this.config.zwaveMaxMinutes) {
1152
+ } else if ((lastStateChange > this.config.zwaveMaxMinutes) && (!deviceUnreachState)) {
1029
1153
  deviceState = 'Offline'; //set online state to offline
1030
1154
  await pushOfflineDevice();
1031
1155
  }
@@ -1038,16 +1162,51 @@ class DeviceWatcher extends utils.Adapter {
1038
1162
 
1039
1163
 
1040
1164
 
1041
- // 2c. Count how many devcies are offline
1165
+ // Count how many devcies are offline
1042
1166
  this.offlineDevicesCount = this.offlineDevices.length;
1043
1167
 
1044
- // 3. Get battery states
1168
+ // Get battery states
1045
1169
  let batteryHealth;
1170
+ const deviceLowBatState = await this.getInitValue(currDeviceString + this.arrDev[i].isLowBat);
1171
+ const deviceLowBatStateHM = await this.getInitValue(currDeviceString + this.arrDev[i].isLowBat2);
1046
1172
 
1047
- if ((!deviceBatteryState) && (!shortDeviceBatteryState)) {
1048
- batteryHealth = ' - ';
1173
+ if ((!deviceBatteryState) && (!shortDeviceBatteryState) && (!shortDeviceBatteryState2)) {
1174
+ if ((deviceLowBatState !== undefined) || (deviceLowBatState !== undefined) || (deviceLowBatStateHM !== undefined)) {
1175
+ switch (this.arrDev[i].isLowBat) {
1176
+ case 'none':
1177
+ batteryHealth = ' - ';
1178
+ break;
1179
+ default:
1180
+ if (!deviceLowBatState) {
1181
+ batteryHealth = 'ok';
1182
+ } else {
1183
+ batteryHealth = 'low';
1184
+ }
1185
+ break;
1186
+ }
1187
+ switch (this.arrDev[i].isLowBat2) {
1188
+ case 'none':
1189
+ batteryHealth = ' - ';
1190
+ break;
1191
+ default:
1192
+ if (!deviceLowBatState) {
1193
+ batteryHealth = 'ok';
1194
+ } else {
1195
+ batteryHealth = 'low';
1196
+ }
1197
+ break;
1198
+ }
1199
+ this.batteryPowered.push(
1200
+ {
1201
+ 'Device': deviceName,
1202
+ 'Adapter': deviceAdapterName,
1203
+ 'Battery': batteryHealth
1204
+ }
1205
+ );
1206
+ } else {
1207
+ batteryHealth = ' - ';
1208
+ }
1049
1209
  } else {
1050
-
1051
1210
  switch (this.arrDev[i].adapter) {
1052
1211
  case 'homematic':
1053
1212
  if (deviceBatteryState === 0) {
@@ -1109,16 +1268,15 @@ class DeviceWatcher extends utils.Adapter {
1109
1268
  }
1110
1269
  }
1111
1270
 
1112
- // 3b. Count how many devices are with battery
1271
+ // Count how many devices are with battery
1113
1272
  this.batteryPoweredCount = this.batteryPowered.length;
1114
1273
 
1115
- // 3c. Count how many devices are with low battery
1274
+ // Count how many devices are with low battery
1116
1275
  const batteryWarningMin = this.config.minWarnBatterie;
1117
- const deviceLowBatState = await this.getInitValue(currDeviceString + this.arrDev[i].isLowBat);
1118
- const deviceLowBatStateHM = await this.getInitValue(currDeviceString + this.arrDev[i].isLowBat2);
1119
1276
 
1277
+ // fill list with low battery devices
1120
1278
  switch (this.arrDev[i].adapter) {
1121
- case 'homematic':
1279
+ case 'homematic': // there are differnt low bat states between hm and hmIp devices
1122
1280
  if (deviceLowBatState || deviceLowBatStateHM) {
1123
1281
  this.batteryLowPowered.push(
1124
1282
  {
@@ -1130,7 +1288,7 @@ class DeviceWatcher extends utils.Adapter {
1130
1288
  }
1131
1289
  break;
1132
1290
 
1133
- default:
1291
+ default: // for all other devices with low bat states
1134
1292
  if (deviceLowBatState) {
1135
1293
  this.batteryLowPowered.push(
1136
1294
  {
@@ -1139,7 +1297,7 @@ class DeviceWatcher extends utils.Adapter {
1139
1297
  'Battery': batteryHealth
1140
1298
  }
1141
1299
  );
1142
- } else if (deviceBatteryState && (deviceBatteryState < batteryWarningMin)) {
1300
+ } else if (deviceBatteryState && (deviceBatteryState < batteryWarningMin)) { // if the battery state is under the set limit
1143
1301
  this.batteryLowPowered.push(
1144
1302
  {
1145
1303
  'Device': deviceName,
@@ -1188,7 +1346,7 @@ class DeviceWatcher extends utils.Adapter {
1188
1346
 
1189
1347
 
1190
1348
  /**
1191
- * @param {string} [adptName] - Adapter name
1349
+ * @param {string} adptName - Adapter name
1192
1350
  */
1193
1351
  async createDataForEachAdapter(adptName) {
1194
1352
  // create Data for each Adapter in own lists
@@ -1225,6 +1383,7 @@ class DeviceWatcher extends utils.Adapter {
1225
1383
  }
1226
1384
 
1227
1385
  if (this.config.checkSendOfflineMsg) await this.sendOfflineNotifications(); // send message if new devices are offline
1386
+ if (this.config.checkSendOfflineMsgDaily) await this.sendDailyOfflineNotifications(); // send daily overview of offline devices
1228
1387
  if (this.config.checkSendBatteryMsg) await this.sendBatteryNotifications(); // send message for low battery devices
1229
1388
  await this.writeDatapoints(); // fill the datapoints
1230
1389
  } catch (error) {
@@ -1237,7 +1396,7 @@ class DeviceWatcher extends utils.Adapter {
1237
1396
 
1238
1397
  /**
1239
1398
  * Notification service
1240
- * @param {string} [text] - Text which should be send
1399
+ * @param {string} text - Text which should be send
1241
1400
  **/
1242
1401
  async sendNotification(text) {
1243
1402
 
@@ -1366,8 +1525,10 @@ class DeviceWatcher extends utils.Adapter {
1366
1525
  let msg = '';
1367
1526
  const offlineDevicesCountOld = await this.getOwnInitValue('offlineCount');
1368
1527
 
1369
- if ((this.offlineDevicesCount != offlineDevicesCountOld)) {
1370
- if (this.offlineDevicesCount == 1) { // make singular if it is only one device
1528
+ if ((this.offlineDevicesCount !== offlineDevicesCountOld)) {
1529
+ if (this.offlineDevicesCount == 0) {
1530
+ msg = 'Alle Geräte sind Online.';
1531
+ } else if (this.offlineDevicesCount == 1) { // make singular if it is only one device
1371
1532
  msg = 'Folgendes Gerät ist seit einiger Zeit nicht erreichbar: \n';
1372
1533
  } else if (this.offlineDevicesCount >= 2) { //make plural if it is more than one device
1373
1534
  msg = `Folgende ${this.offlineDevicesCount} Geräte sind seit einiger Zeit nicht erreichbar: \n`;
@@ -1376,6 +1537,7 @@ class DeviceWatcher extends utils.Adapter {
1376
1537
  for (const id of this.offlineDevices) {
1377
1538
  msg = `${msg} \n ${id['Device']} (${id['Last contact']})`;
1378
1539
  }
1540
+
1379
1541
  this.log.info(msg);
1380
1542
  await this.setStateAsync('lastNotification', msg, true);
1381
1543
  await this.sendNotification(msg);
@@ -1383,9 +1545,76 @@ class DeviceWatcher extends utils.Adapter {
1383
1545
  } catch (error) {
1384
1546
  this.errorReporting('[sendOfflineMessage]', error);
1385
1547
  }
1548
+
1549
+ try {
1550
+ // send daily an overview with offline devices
1551
+ if (this.config.checkSendOfflineMsgDaily) {
1552
+ // Check if the daily message for offline devices was already sent today
1553
+ const lastOfflineNotifyIndicator = await this.getOwnInitValue('info.lastOfflineNotification');
1554
+ const now = new Date(); // get date
1555
+
1556
+ // set indicator for send message first to 'false', after sending to 'true'
1557
+ if (now.getHours() < 11) await this.setStateAsync('info.lastOfflineNotification', false, true);
1558
+
1559
+ // if time is > 11 (12:00 pm create message for offline devices devices)
1560
+ if ((now.getHours() > 11) && (!lastOfflineNotifyIndicator)) {
1561
+ let msg = '';
1562
+
1563
+ for (const id of this.offlineDevices) {
1564
+ msg = `${msg} \n ${id['Device']} (${id['Last contact']})`;
1565
+ }
1566
+
1567
+ if (this.offlineDevicesCount > 0) {
1568
+ this.log.info(`Geräte Offline: ${msg}`);
1569
+ await this.setStateAsync('lastNotification', `Geräte Offline: ${msg}`, true);
1570
+
1571
+ await this.sendNotification(`Geräte Offline: ${msg}`);
1572
+
1573
+ await this.setStateAsync('info.lastOfflineNotification', true, true);
1574
+ }
1575
+ }
1576
+ }
1577
+ } catch (error) {
1578
+ this.errorReporting('[sendOfflineMessage - daily message]', error);
1579
+ }
1386
1580
  this.log.debug(`Finished the function: ${this.sendOfflineNotifications.name}`);
1387
1581
  }//<--End of offline notification
1388
1582
 
1583
+ async sendDailyOfflineNotifications() {
1584
+ // send daily an overview with offline devices
1585
+
1586
+ this.log.debug(`Start the function: ${this.sendDailyOfflineNotifications.name}`);
1587
+
1588
+ try {
1589
+ // Check if the daily message for offline devices was already sent today
1590
+ const lastOfflineNotifyIndicator = await this.getOwnInitValue('info.lastOfflineNotification');
1591
+ const now = new Date(); // get date
1592
+
1593
+ // set indicator for send message first to 'false', after sending to 'true'
1594
+ if (now.getHours() < 11) await this.setStateAsync('info.lastOfflineNotification', false, true);
1595
+
1596
+ // if time is > 11 (12:00 pm create message for offline devices devices)
1597
+ if ((now.getHours() > 11) && (!lastOfflineNotifyIndicator)) {
1598
+ let msg = '';
1599
+
1600
+ for (const id of this.offlineDevices) {
1601
+ msg = `${msg} \n ${id['Device']} (${id['Last contact']})`;
1602
+ }
1603
+
1604
+ if (this.offlineDevicesCount > 0) {
1605
+ this.log.info(`Geräte Offline: ${msg}`);
1606
+ await this.setStateAsync('lastNotification', `Geräte Offline: ${msg}`, true);
1607
+
1608
+ await this.sendNotification(`Geräte Offline: ${msg}`);
1609
+
1610
+ await this.setStateAsync('info.lastOfflineNotification', true, true);
1611
+ }
1612
+ }
1613
+ } catch (error) {
1614
+ this.errorReporting('[sendDailyOfflineNotifications]', error);
1615
+ }
1616
+ this.log.debug(`Finished the function: ${this.sendDailyOfflineNotifications.name}`);
1617
+ }//<--End of daily offline notification
1389
1618
 
1390
1619
  async sendBatteryNotifications() {
1391
1620
  // send message for low battery devices
@@ -1426,7 +1655,7 @@ class DeviceWatcher extends utils.Adapter {
1426
1655
  const lastBatteryNotifyIndicator = await this.getOwnInitValue('info.lastBatteryNotification');
1427
1656
 
1428
1657
  // set indicator for send message first to 'false', after sending to 'true'
1429
- if (now.getHours() < 11) { await this.setStateAsync('info.lastBatteryNotification', false, true); }
1658
+ if (now.getHours() < 11) await this.setStateAsync('info.lastBatteryNotification', false, true);
1430
1659
 
1431
1660
  // if time is > 11 (12:00 pm create message for low battery devices)
1432
1661
  if ((now.getHours() > 11) && (!lastBatteryNotifyIndicator) && (checkToday != undefined)) {
@@ -1458,7 +1687,7 @@ class DeviceWatcher extends utils.Adapter {
1458
1687
  this.log.debug(`Function started: ${this.resetVars.name}`);
1459
1688
 
1460
1689
  // arrays
1461
- this.offlineDevices = [],
1690
+ this.offlineDevices = [];
1462
1691
  this.linkQualityDevices = [];
1463
1692
  this.batteryPowered = [];
1464
1693
  this.batteryLowPowered = [];
@@ -1553,8 +1782,8 @@ class DeviceWatcher extends utils.Adapter {
1553
1782
  }//<--End of writing Datapoints
1554
1783
 
1555
1784
  /**
1556
- * @param {object} [devices] - Device
1557
- * @param {number} [deviceCount] - Counted devices
1785
+ * @param {object} devices - Device
1786
+ * @param {number} deviceCount - Counted devices
1558
1787
  */
1559
1788
  async creatLinkQualityListHTML(devices, deviceCount) {
1560
1789
  devices = devices.sort((a, b) => { return a.Device.localeCompare(b.Device); });
@@ -1585,8 +1814,8 @@ class DeviceWatcher extends utils.Adapter {
1585
1814
  }
1586
1815
 
1587
1816
  /**
1588
- * @param {object} [devices] - Device
1589
- * @param {number} [deviceCount] - Counted devices
1817
+ * @param {object} devices - Device
1818
+ * @param {number} deviceCount - Counted devices
1590
1819
  */
1591
1820
  async createOfflineListHTML(devices, deviceCount) {
1592
1821
  devices = devices.sort((a, b) => { return a.Device.localeCompare(b.Device); });
@@ -1643,9 +1872,9 @@ class DeviceWatcher extends utils.Adapter {
1643
1872
  <td align=center><font>${device.Adapter}</font></td>`;
1644
1873
 
1645
1874
  if (isLowBatteryList) {
1646
- html += `<td align=center><font color=orange>${device.Battery == ' - ' ? 'schwach' : device.Battery}</font></td>`;
1875
+ html += `<td align=center><font color=orange>${device.Battery}</font></td>`;
1647
1876
  } else {
1648
- html += `<td align=right><font color=#3bcf0e>${device.Battery == ' - ' ? 'ok' : device.Battery}</font></td>`;
1877
+ html += `<td align=right><font color=#3bcf0e>${device.Battery}</font></td>`;
1649
1878
  }
1650
1879
 
1651
1880
  html += `</tr>`;
@@ -1656,8 +1885,8 @@ class DeviceWatcher extends utils.Adapter {
1656
1885
  }
1657
1886
 
1658
1887
  /**
1659
- * @param {string} [codePart] - Message Prefix
1660
- * @param {object} [error] - Sentry message
1888
+ * @param {string} codePart - Message Prefix
1889
+ * @param {object} error - Sentry message
1661
1890
  */
1662
1891
  errorReporting(codePart, error) {
1663
1892
  const msg = `[${codePart}] error: ${error.message}`;
@@ -1675,9 +1904,18 @@ class DeviceWatcher extends utils.Adapter {
1675
1904
  } // <-- end of errorReporting
1676
1905
 
1677
1906
 
1907
+ /**
1908
+ * @param {() => void} callback
1909
+ */
1678
1910
  onUnload(callback) {
1679
1911
  try {
1680
1912
  this.log.info('cleaned everything up...');
1913
+
1914
+ if (this.refreshDataTimeout) {
1915
+ this.log.debug('clearing refresh timeout');
1916
+ this.clearTimeout(this.refreshDataTimeout);
1917
+ }
1918
+
1681
1919
  callback();
1682
1920
  } catch (e) {
1683
1921
  callback();
@@ -1685,6 +1923,7 @@ class DeviceWatcher extends utils.Adapter {
1685
1923
  }
1686
1924
  }
1687
1925
 
1926
+ // @ts-ignore parent is a valid property on module
1688
1927
  if (require.main !== module) {
1689
1928
  // Export the constructor in compact mode
1690
1929
  /**