iobroker.device-watcher 2.15.13 → 2.15.15
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/README.md +6 -7
- package/io-package.json +27 -1
- package/lib/crud.js +84 -121
- package/lib/tools.js +12 -1
- package/main.js +2544 -2460
- package/package.json +1 -1
package/main.js
CHANGED
|
@@ -14,2468 +14,2552 @@ let isUnloaded = false;
|
|
|
14
14
|
const adapterUpdateListDP = 'admin.*.info.updatesJson';
|
|
15
15
|
|
|
16
16
|
class DeviceWatcher extends utils.Adapter {
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
|
|
355
|
-
|
|
356
|
-
|
|
357
|
-
|
|
358
|
-
|
|
359
|
-
|
|
360
|
-
|
|
361
|
-
|
|
362
|
-
|
|
363
|
-
|
|
364
|
-
|
|
365
|
-
|
|
366
|
-
|
|
367
|
-
|
|
368
|
-
|
|
369
|
-
|
|
370
|
-
|
|
371
|
-
|
|
372
|
-
|
|
373
|
-
|
|
374
|
-
|
|
375
|
-
|
|
376
|
-
|
|
377
|
-
|
|
378
|
-
|
|
379
|
-
|
|
380
|
-
|
|
381
|
-
|
|
382
|
-
|
|
383
|
-
|
|
384
|
-
|
|
385
|
-
|
|
386
|
-
|
|
387
|
-
|
|
388
|
-
|
|
389
|
-
|
|
390
|
-
|
|
391
|
-
|
|
392
|
-
|
|
393
|
-
|
|
394
|
-
|
|
395
|
-
|
|
396
|
-
|
|
397
|
-
|
|
398
|
-
|
|
399
|
-
|
|
400
|
-
|
|
401
|
-
|
|
402
|
-
|
|
403
|
-
|
|
404
|
-
|
|
405
|
-
|
|
406
|
-
|
|
407
|
-
|
|
408
|
-
|
|
409
|
-
|
|
410
|
-
|
|
411
|
-
|
|
412
|
-
|
|
413
|
-
|
|
414
|
-
|
|
415
|
-
|
|
416
|
-
|
|
417
|
-
|
|
418
|
-
|
|
419
|
-
|
|
420
|
-
|
|
421
|
-
|
|
422
|
-
|
|
423
|
-
|
|
424
|
-
|
|
425
|
-
|
|
426
|
-
|
|
427
|
-
|
|
428
|
-
|
|
429
|
-
|
|
430
|
-
|
|
431
|
-
|
|
432
|
-
|
|
433
|
-
|
|
434
|
-
|
|
435
|
-
|
|
436
|
-
|
|
437
|
-
|
|
438
|
-
|
|
439
|
-
|
|
440
|
-
|
|
441
|
-
|
|
442
|
-
|
|
443
|
-
|
|
444
|
-
|
|
445
|
-
|
|
446
|
-
|
|
447
|
-
|
|
448
|
-
|
|
449
|
-
|
|
450
|
-
|
|
451
|
-
|
|
452
|
-
|
|
453
|
-
|
|
454
|
-
|
|
455
|
-
|
|
456
|
-
|
|
457
|
-
|
|
458
|
-
|
|
459
|
-
|
|
460
|
-
|
|
461
|
-
|
|
462
|
-
|
|
463
|
-
|
|
464
|
-
|
|
465
|
-
|
|
466
|
-
|
|
467
|
-
|
|
468
|
-
|
|
469
|
-
|
|
470
|
-
|
|
471
|
-
|
|
472
|
-
|
|
473
|
-
|
|
474
|
-
|
|
475
|
-
|
|
476
|
-
|
|
477
|
-
|
|
478
|
-
|
|
479
|
-
|
|
480
|
-
|
|
481
|
-
|
|
482
|
-
|
|
483
|
-
|
|
484
|
-
|
|
485
|
-
|
|
486
|
-
|
|
487
|
-
|
|
488
|
-
|
|
489
|
-
|
|
490
|
-
|
|
491
|
-
|
|
492
|
-
|
|
493
|
-
|
|
494
|
-
|
|
495
|
-
|
|
496
|
-
|
|
497
|
-
|
|
498
|
-
|
|
499
|
-
|
|
500
|
-
|
|
501
|
-
|
|
502
|
-
|
|
503
|
-
|
|
504
|
-
|
|
505
|
-
|
|
506
|
-
|
|
507
|
-
|
|
508
|
-
|
|
509
|
-
|
|
510
|
-
|
|
511
|
-
|
|
512
|
-
|
|
513
|
-
|
|
514
|
-
|
|
515
|
-
|
|
516
|
-
|
|
517
|
-
|
|
518
|
-
|
|
519
|
-
|
|
520
|
-
|
|
521
|
-
|
|
522
|
-
|
|
523
|
-
|
|
524
|
-
|
|
525
|
-
|
|
526
|
-
|
|
527
|
-
|
|
528
|
-
|
|
529
|
-
|
|
530
|
-
|
|
531
|
-
|
|
532
|
-
|
|
533
|
-
|
|
534
|
-
|
|
535
|
-
|
|
536
|
-
|
|
537
|
-
|
|
538
|
-
|
|
539
|
-
|
|
540
|
-
|
|
541
|
-
|
|
542
|
-
|
|
543
|
-
|
|
544
|
-
|
|
545
|
-
|
|
546
|
-
|
|
547
|
-
|
|
548
|
-
|
|
549
|
-
|
|
550
|
-
|
|
551
|
-
|
|
552
|
-
|
|
553
|
-
|
|
554
|
-
|
|
555
|
-
|
|
556
|
-
|
|
557
|
-
|
|
558
|
-
|
|
559
|
-
|
|
560
|
-
|
|
561
|
-
|
|
562
|
-
|
|
563
|
-
|
|
564
|
-
|
|
565
|
-
|
|
566
|
-
|
|
567
|
-
|
|
568
|
-
|
|
569
|
-
|
|
570
|
-
|
|
571
|
-
|
|
572
|
-
|
|
573
|
-
|
|
574
|
-
|
|
575
|
-
|
|
576
|
-
|
|
577
|
-
|
|
578
|
-
|
|
579
|
-
|
|
580
|
-
|
|
581
|
-
|
|
582
|
-
|
|
583
|
-
|
|
584
|
-
|
|
585
|
-
|
|
586
|
-
|
|
587
|
-
|
|
588
|
-
|
|
589
|
-
|
|
590
|
-
|
|
591
|
-
|
|
592
|
-
|
|
593
|
-
|
|
594
|
-
|
|
595
|
-
|
|
596
|
-
|
|
597
|
-
|
|
598
|
-
|
|
599
|
-
|
|
600
|
-
|
|
601
|
-
|
|
602
|
-
|
|
603
|
-
|
|
604
|
-
|
|
605
|
-
|
|
606
|
-
|
|
607
|
-
|
|
608
|
-
|
|
609
|
-
|
|
610
|
-
|
|
611
|
-
|
|
612
|
-
|
|
613
|
-
|
|
614
|
-
|
|
615
|
-
|
|
616
|
-
|
|
617
|
-
|
|
618
|
-
|
|
619
|
-
|
|
620
|
-
|
|
621
|
-
|
|
622
|
-
|
|
623
|
-
|
|
624
|
-
|
|
625
|
-
|
|
626
|
-
|
|
627
|
-
|
|
628
|
-
|
|
629
|
-
|
|
630
|
-
|
|
631
|
-
|
|
632
|
-
|
|
633
|
-
|
|
634
|
-
|
|
635
|
-
|
|
636
|
-
|
|
637
|
-
|
|
638
|
-
|
|
639
|
-
|
|
640
|
-
|
|
641
|
-
|
|
642
|
-
|
|
643
|
-
|
|
644
|
-
|
|
645
|
-
|
|
646
|
-
|
|
647
|
-
|
|
648
|
-
|
|
649
|
-
|
|
650
|
-
|
|
651
|
-
|
|
652
|
-
|
|
653
|
-
|
|
654
|
-
|
|
655
|
-
|
|
656
|
-
|
|
657
|
-
|
|
658
|
-
|
|
659
|
-
|
|
660
|
-
|
|
661
|
-
|
|
662
|
-
|
|
663
|
-
|
|
664
|
-
|
|
665
|
-
|
|
666
|
-
|
|
667
|
-
|
|
668
|
-
|
|
669
|
-
|
|
670
|
-
|
|
671
|
-
|
|
672
|
-
|
|
673
|
-
|
|
674
|
-
|
|
675
|
-
|
|
676
|
-
|
|
677
|
-
|
|
678
|
-
|
|
679
|
-
|
|
680
|
-
|
|
681
|
-
|
|
682
|
-
|
|
683
|
-
|
|
684
|
-
|
|
685
|
-
|
|
686
|
-
|
|
687
|
-
|
|
688
|
-
|
|
689
|
-
|
|
690
|
-
|
|
691
|
-
|
|
692
|
-
|
|
693
|
-
|
|
694
|
-
|
|
695
|
-
|
|
696
|
-
|
|
697
|
-
|
|
698
|
-
|
|
699
|
-
|
|
700
|
-
|
|
701
|
-
|
|
702
|
-
|
|
703
|
-
|
|
704
|
-
|
|
705
|
-
|
|
706
|
-
|
|
707
|
-
|
|
708
|
-
|
|
709
|
-
|
|
710
|
-
|
|
711
|
-
|
|
712
|
-
|
|
713
|
-
|
|
714
|
-
|
|
715
|
-
|
|
716
|
-
|
|
717
|
-
|
|
718
|
-
|
|
719
|
-
|
|
720
|
-
|
|
721
|
-
|
|
722
|
-
|
|
723
|
-
|
|
724
|
-
|
|
725
|
-
|
|
726
|
-
|
|
727
|
-
|
|
728
|
-
|
|
729
|
-
|
|
730
|
-
|
|
731
|
-
|
|
732
|
-
|
|
733
|
-
|
|
734
|
-
|
|
735
|
-
|
|
736
|
-
|
|
737
|
-
|
|
738
|
-
|
|
739
|
-
|
|
740
|
-
|
|
741
|
-
|
|
742
|
-
|
|
743
|
-
|
|
744
|
-
|
|
745
|
-
|
|
746
|
-
|
|
747
|
-
|
|
748
|
-
|
|
749
|
-
|
|
750
|
-
|
|
751
|
-
|
|
752
|
-
|
|
753
|
-
|
|
754
|
-
|
|
755
|
-
|
|
756
|
-
|
|
757
|
-
|
|
758
|
-
|
|
759
|
-
|
|
760
|
-
|
|
761
|
-
|
|
762
|
-
|
|
763
|
-
|
|
764
|
-
|
|
765
|
-
|
|
766
|
-
|
|
767
|
-
|
|
768
|
-
|
|
769
|
-
|
|
770
|
-
|
|
771
|
-
|
|
772
|
-
|
|
773
|
-
|
|
774
|
-
|
|
775
|
-
|
|
776
|
-
|
|
777
|
-
|
|
778
|
-
|
|
779
|
-
|
|
780
|
-
|
|
781
|
-
|
|
782
|
-
|
|
783
|
-
|
|
784
|
-
|
|
785
|
-
|
|
786
|
-
|
|
787
|
-
|
|
788
|
-
|
|
789
|
-
|
|
790
|
-
|
|
791
|
-
|
|
792
|
-
|
|
793
|
-
|
|
794
|
-
|
|
795
|
-
|
|
796
|
-
|
|
797
|
-
|
|
798
|
-
|
|
799
|
-
|
|
800
|
-
|
|
801
|
-
|
|
802
|
-
|
|
803
|
-
|
|
804
|
-
|
|
805
|
-
|
|
806
|
-
|
|
807
|
-
|
|
808
|
-
|
|
809
|
-
|
|
810
|
-
|
|
811
|
-
|
|
812
|
-
|
|
813
|
-
|
|
814
|
-
|
|
815
|
-
|
|
816
|
-
|
|
817
|
-
|
|
818
|
-
|
|
819
|
-
|
|
820
|
-
|
|
821
|
-
|
|
822
|
-
|
|
823
|
-
|
|
824
|
-
|
|
825
|
-
|
|
826
|
-
|
|
827
|
-
|
|
828
|
-
|
|
829
|
-
|
|
830
|
-
|
|
831
|
-
|
|
832
|
-
|
|
833
|
-
|
|
834
|
-
|
|
835
|
-
|
|
836
|
-
|
|
837
|
-
|
|
838
|
-
|
|
839
|
-
|
|
840
|
-
|
|
841
|
-
|
|
842
|
-
|
|
843
|
-
|
|
844
|
-
|
|
845
|
-
|
|
846
|
-
|
|
847
|
-
|
|
848
|
-
|
|
849
|
-
|
|
850
|
-
|
|
851
|
-
|
|
852
|
-
|
|
853
|
-
|
|
854
|
-
|
|
855
|
-
|
|
856
|
-
|
|
857
|
-
|
|
858
|
-
|
|
859
|
-
|
|
860
|
-
|
|
861
|
-
|
|
862
|
-
|
|
863
|
-
|
|
864
|
-
|
|
865
|
-
|
|
866
|
-
|
|
867
|
-
|
|
868
|
-
|
|
869
|
-
|
|
870
|
-
|
|
871
|
-
|
|
872
|
-
|
|
873
|
-
|
|
874
|
-
|
|
875
|
-
|
|
876
|
-
|
|
877
|
-
|
|
878
|
-
|
|
879
|
-
|
|
880
|
-
|
|
881
|
-
|
|
882
|
-
|
|
883
|
-
|
|
884
|
-
|
|
885
|
-
|
|
886
|
-
|
|
887
|
-
|
|
888
|
-
|
|
889
|
-
|
|
890
|
-
|
|
891
|
-
|
|
892
|
-
|
|
893
|
-
|
|
894
|
-
|
|
895
|
-
|
|
896
|
-
|
|
897
|
-
|
|
898
|
-
|
|
899
|
-
|
|
900
|
-
|
|
901
|
-
|
|
902
|
-
|
|
903
|
-
|
|
904
|
-
|
|
905
|
-
|
|
906
|
-
|
|
907
|
-
|
|
908
|
-
|
|
909
|
-
|
|
910
|
-
|
|
911
|
-
|
|
912
|
-
|
|
913
|
-
|
|
914
|
-
|
|
915
|
-
|
|
916
|
-
|
|
917
|
-
|
|
918
|
-
|
|
919
|
-
|
|
920
|
-
|
|
921
|
-
|
|
922
|
-
|
|
923
|
-
|
|
924
|
-
|
|
925
|
-
|
|
926
|
-
|
|
927
|
-
|
|
928
|
-
|
|
929
|
-
|
|
930
|
-
|
|
931
|
-
|
|
932
|
-
|
|
933
|
-
|
|
934
|
-
|
|
935
|
-
|
|
936
|
-
|
|
937
|
-
|
|
938
|
-
|
|
939
|
-
|
|
940
|
-
|
|
941
|
-
|
|
942
|
-
|
|
943
|
-
|
|
944
|
-
|
|
945
|
-
|
|
946
|
-
|
|
947
|
-
|
|
948
|
-
|
|
949
|
-
|
|
950
|
-
|
|
951
|
-
|
|
952
|
-
|
|
953
|
-
|
|
954
|
-
|
|
955
|
-
|
|
956
|
-
|
|
957
|
-
|
|
958
|
-
|
|
959
|
-
|
|
960
|
-
|
|
961
|
-
|
|
962
|
-
|
|
963
|
-
|
|
964
|
-
|
|
965
|
-
|
|
966
|
-
|
|
967
|
-
|
|
968
|
-
|
|
969
|
-
|
|
970
|
-
|
|
971
|
-
|
|
972
|
-
|
|
973
|
-
|
|
974
|
-
|
|
975
|
-
|
|
976
|
-
|
|
977
|
-
|
|
978
|
-
|
|
979
|
-
|
|
980
|
-
|
|
981
|
-
|
|
982
|
-
|
|
983
|
-
|
|
984
|
-
|
|
985
|
-
|
|
986
|
-
|
|
987
|
-
|
|
988
|
-
|
|
989
|
-
|
|
990
|
-
|
|
991
|
-
|
|
992
|
-
|
|
993
|
-
|
|
994
|
-
|
|
995
|
-
|
|
996
|
-
|
|
997
|
-
|
|
998
|
-
|
|
999
|
-
|
|
1000
|
-
|
|
1001
|
-
|
|
1002
|
-
|
|
1003
|
-
|
|
1004
|
-
|
|
1005
|
-
|
|
1006
|
-
|
|
1007
|
-
|
|
1008
|
-
|
|
1009
|
-
|
|
1010
|
-
|
|
1011
|
-
|
|
1012
|
-
|
|
1013
|
-
|
|
1014
|
-
|
|
1015
|
-
|
|
1016
|
-
|
|
1017
|
-
|
|
1018
|
-
|
|
1019
|
-
|
|
1020
|
-
|
|
1021
|
-
|
|
1022
|
-
|
|
1023
|
-
|
|
1024
|
-
|
|
1025
|
-
|
|
1026
|
-
|
|
1027
|
-
|
|
1028
|
-
|
|
1029
|
-
|
|
1030
|
-
|
|
1031
|
-
|
|
1032
|
-
|
|
1033
|
-
|
|
1034
|
-
|
|
1035
|
-
|
|
1036
|
-
|
|
1037
|
-
|
|
1038
|
-
|
|
1039
|
-
|
|
1040
|
-
|
|
1041
|
-
|
|
1042
|
-
|
|
1043
|
-
|
|
1044
|
-
|
|
1045
|
-
|
|
1046
|
-
|
|
1047
|
-
|
|
1048
|
-
|
|
1049
|
-
|
|
1050
|
-
|
|
1051
|
-
|
|
1052
|
-
|
|
1053
|
-
|
|
1054
|
-
|
|
1055
|
-
|
|
1056
|
-
|
|
1057
|
-
|
|
1058
|
-
|
|
1059
|
-
|
|
1060
|
-
|
|
1061
|
-
|
|
1062
|
-
|
|
1063
|
-
|
|
1064
|
-
|
|
1065
|
-
|
|
1066
|
-
|
|
1067
|
-
|
|
1068
|
-
|
|
1069
|
-
|
|
1070
|
-
|
|
1071
|
-
|
|
1072
|
-
|
|
1073
|
-
|
|
1074
|
-
|
|
1075
|
-
|
|
1076
|
-
|
|
1077
|
-
|
|
1078
|
-
|
|
1079
|
-
|
|
1080
|
-
|
|
1081
|
-
|
|
1082
|
-
|
|
1083
|
-
|
|
1084
|
-
|
|
1085
|
-
|
|
1086
|
-
|
|
1087
|
-
|
|
1088
|
-
|
|
1089
|
-
|
|
1090
|
-
|
|
1091
|
-
|
|
1092
|
-
|
|
1093
|
-
|
|
1094
|
-
|
|
1095
|
-
|
|
1096
|
-
|
|
1097
|
-
|
|
1098
|
-
|
|
1099
|
-
|
|
1100
|
-
|
|
1101
|
-
|
|
1102
|
-
|
|
1103
|
-
|
|
1104
|
-
|
|
1105
|
-
|
|
1106
|
-
|
|
1107
|
-
|
|
1108
|
-
|
|
1109
|
-
|
|
1110
|
-
|
|
1111
|
-
|
|
1112
|
-
|
|
1113
|
-
|
|
1114
|
-
|
|
1115
|
-
|
|
1116
|
-
|
|
1117
|
-
|
|
1118
|
-
|
|
1119
|
-
|
|
1120
|
-
|
|
1121
|
-
|
|
1122
|
-
|
|
1123
|
-
|
|
1124
|
-
|
|
1125
|
-
|
|
1126
|
-
|
|
1127
|
-
|
|
1128
|
-
|
|
1129
|
-
|
|
1130
|
-
|
|
1131
|
-
|
|
1132
|
-
|
|
1133
|
-
|
|
1134
|
-
|
|
1135
|
-
|
|
1136
|
-
|
|
1137
|
-
|
|
1138
|
-
|
|
1139
|
-
|
|
1140
|
-
|
|
1141
|
-
|
|
1142
|
-
|
|
1143
|
-
|
|
1144
|
-
|
|
1145
|
-
|
|
1146
|
-
|
|
1147
|
-
|
|
1148
|
-
|
|
1149
|
-
|
|
1150
|
-
|
|
1151
|
-
|
|
1152
|
-
|
|
1153
|
-
|
|
1154
|
-
|
|
1155
|
-
|
|
1156
|
-
|
|
1157
|
-
|
|
1158
|
-
|
|
1159
|
-
|
|
1160
|
-
|
|
1161
|
-
|
|
1162
|
-
|
|
1163
|
-
|
|
1164
|
-
|
|
1165
|
-
|
|
1166
|
-
|
|
1167
|
-
|
|
1168
|
-
|
|
1169
|
-
|
|
1170
|
-
|
|
1171
|
-
|
|
1172
|
-
|
|
1173
|
-
|
|
1174
|
-
|
|
1175
|
-
|
|
1176
|
-
|
|
1177
|
-
|
|
1178
|
-
|
|
1179
|
-
|
|
1180
|
-
|
|
1181
|
-
|
|
1182
|
-
|
|
1183
|
-
|
|
1184
|
-
|
|
1185
|
-
|
|
1186
|
-
|
|
1187
|
-
|
|
1188
|
-
|
|
1189
|
-
|
|
1190
|
-
|
|
1191
|
-
|
|
1192
|
-
|
|
1193
|
-
|
|
1194
|
-
|
|
1195
|
-
|
|
1196
|
-
|
|
1197
|
-
|
|
1198
|
-
|
|
1199
|
-
|
|
1200
|
-
|
|
1201
|
-
|
|
1202
|
-
|
|
1203
|
-
|
|
1204
|
-
|
|
1205
|
-
|
|
1206
|
-
|
|
1207
|
-
|
|
1208
|
-
|
|
1209
|
-
|
|
1210
|
-
|
|
1211
|
-
|
|
1212
|
-
|
|
1213
|
-
|
|
1214
|
-
|
|
1215
|
-
|
|
1216
|
-
|
|
1217
|
-
|
|
1218
|
-
|
|
1219
|
-
|
|
1220
|
-
|
|
1221
|
-
|
|
1222
|
-
|
|
1223
|
-
|
|
1224
|
-
|
|
1225
|
-
|
|
1226
|
-
|
|
1227
|
-
|
|
1228
|
-
|
|
1229
|
-
|
|
1230
|
-
|
|
1231
|
-
|
|
1232
|
-
|
|
1233
|
-
|
|
1234
|
-
|
|
1235
|
-
|
|
1236
|
-
|
|
1237
|
-
|
|
1238
|
-
|
|
1239
|
-
|
|
1240
|
-
|
|
1241
|
-
|
|
1242
|
-
|
|
1243
|
-
|
|
1244
|
-
|
|
1245
|
-
|
|
1246
|
-
|
|
1247
|
-
|
|
1248
|
-
|
|
1249
|
-
|
|
1250
|
-
|
|
1251
|
-
|
|
1252
|
-
|
|
1253
|
-
|
|
1254
|
-
|
|
1255
|
-
|
|
1256
|
-
|
|
1257
|
-
|
|
1258
|
-
|
|
1259
|
-
|
|
1260
|
-
|
|
1261
|
-
|
|
1262
|
-
|
|
1263
|
-
|
|
1264
|
-
|
|
1265
|
-
|
|
1266
|
-
|
|
1267
|
-
|
|
1268
|
-
|
|
1269
|
-
|
|
1270
|
-
|
|
1271
|
-
|
|
1272
|
-
|
|
1273
|
-
|
|
1274
|
-
|
|
1275
|
-
|
|
1276
|
-
|
|
1277
|
-
|
|
1278
|
-
|
|
1279
|
-
|
|
1280
|
-
|
|
1281
|
-
|
|
1282
|
-
|
|
1283
|
-
|
|
1284
|
-
|
|
1285
|
-
|
|
1286
|
-
|
|
1287
|
-
|
|
1288
|
-
|
|
1289
|
-
|
|
1290
|
-
|
|
1291
|
-
|
|
1292
|
-
|
|
1293
|
-
|
|
1294
|
-
|
|
1295
|
-
|
|
1296
|
-
|
|
1297
|
-
|
|
1298
|
-
|
|
1299
|
-
|
|
1300
|
-
|
|
1301
|
-
|
|
1302
|
-
|
|
1303
|
-
|
|
1304
|
-
|
|
1305
|
-
|
|
1306
|
-
|
|
1307
|
-
|
|
1308
|
-
|
|
1309
|
-
|
|
1310
|
-
|
|
1311
|
-
|
|
1312
|
-
|
|
1313
|
-
|
|
1314
|
-
|
|
1315
|
-
|
|
1316
|
-
|
|
1317
|
-
|
|
1318
|
-
|
|
1319
|
-
|
|
1320
|
-
|
|
1321
|
-
|
|
1322
|
-
|
|
1323
|
-
|
|
1324
|
-
|
|
1325
|
-
|
|
1326
|
-
|
|
1327
|
-
|
|
1328
|
-
|
|
1329
|
-
|
|
1330
|
-
|
|
1331
|
-
|
|
1332
|
-
|
|
1333
|
-
|
|
1334
|
-
|
|
1335
|
-
|
|
1336
|
-
|
|
1337
|
-
|
|
1338
|
-
|
|
1339
|
-
|
|
1340
|
-
|
|
1341
|
-
|
|
1342
|
-
|
|
1343
|
-
|
|
1344
|
-
|
|
1345
|
-
|
|
1346
|
-
|
|
1347
|
-
|
|
1348
|
-
|
|
1349
|
-
|
|
1350
|
-
|
|
1351
|
-
|
|
1352
|
-
|
|
1353
|
-
|
|
1354
|
-
|
|
1355
|
-
|
|
1356
|
-
|
|
1357
|
-
|
|
1358
|
-
|
|
1359
|
-
|
|
1360
|
-
|
|
1361
|
-
|
|
1362
|
-
|
|
1363
|
-
|
|
1364
|
-
|
|
1365
|
-
|
|
1366
|
-
|
|
1367
|
-
|
|
1368
|
-
|
|
1369
|
-
|
|
1370
|
-
|
|
1371
|
-
|
|
1372
|
-
|
|
1373
|
-
|
|
1374
|
-
|
|
1375
|
-
|
|
1376
|
-
|
|
1377
|
-
|
|
1378
|
-
|
|
1379
|
-
|
|
1380
|
-
|
|
1381
|
-
|
|
1382
|
-
|
|
1383
|
-
|
|
1384
|
-
|
|
1385
|
-
|
|
1386
|
-
|
|
1387
|
-
|
|
1388
|
-
|
|
1389
|
-
|
|
1390
|
-
|
|
1391
|
-
|
|
1392
|
-
|
|
1393
|
-
|
|
1394
|
-
|
|
1395
|
-
|
|
1396
|
-
|
|
1397
|
-
|
|
1398
|
-
|
|
1399
|
-
|
|
1400
|
-
|
|
1401
|
-
|
|
1402
|
-
|
|
1403
|
-
|
|
1404
|
-
|
|
1405
|
-
|
|
1406
|
-
|
|
1407
|
-
|
|
1408
|
-
|
|
1409
|
-
|
|
1410
|
-
|
|
1411
|
-
|
|
1412
|
-
|
|
1413
|
-
|
|
1414
|
-
|
|
1415
|
-
|
|
1416
|
-
|
|
1417
|
-
|
|
1418
|
-
|
|
1419
|
-
|
|
1420
|
-
|
|
1421
|
-
|
|
1422
|
-
|
|
1423
|
-
|
|
1424
|
-
|
|
1425
|
-
|
|
1426
|
-
|
|
1427
|
-
|
|
1428
|
-
|
|
1429
|
-
|
|
1430
|
-
|
|
1431
|
-
|
|
1432
|
-
|
|
1433
|
-
|
|
1434
|
-
|
|
1435
|
-
|
|
1436
|
-
|
|
1437
|
-
|
|
1438
|
-
|
|
1439
|
-
|
|
1440
|
-
|
|
1441
|
-
|
|
1442
|
-
|
|
1443
|
-
|
|
1444
|
-
|
|
1445
|
-
|
|
1446
|
-
|
|
1447
|
-
|
|
1448
|
-
|
|
1449
|
-
|
|
1450
|
-
|
|
1451
|
-
|
|
1452
|
-
|
|
1453
|
-
|
|
1454
|
-
|
|
1455
|
-
|
|
1456
|
-
|
|
1457
|
-
|
|
1458
|
-
|
|
1459
|
-
|
|
1460
|
-
|
|
1461
|
-
|
|
1462
|
-
|
|
1463
|
-
|
|
1464
|
-
|
|
1465
|
-
|
|
1466
|
-
|
|
1467
|
-
|
|
1468
|
-
|
|
1469
|
-
|
|
1470
|
-
|
|
1471
|
-
|
|
1472
|
-
|
|
1473
|
-
|
|
1474
|
-
|
|
1475
|
-
|
|
1476
|
-
|
|
1477
|
-
|
|
1478
|
-
|
|
1479
|
-
|
|
1480
|
-
|
|
1481
|
-
|
|
1482
|
-
|
|
1483
|
-
|
|
1484
|
-
|
|
1485
|
-
|
|
1486
|
-
|
|
1487
|
-
|
|
1488
|
-
|
|
1489
|
-
|
|
1490
|
-
|
|
1491
|
-
|
|
1492
|
-
|
|
1493
|
-
|
|
1494
|
-
|
|
1495
|
-
|
|
1496
|
-
|
|
1497
|
-
|
|
1498
|
-
|
|
1499
|
-
|
|
1500
|
-
|
|
1501
|
-
|
|
1502
|
-
|
|
1503
|
-
|
|
1504
|
-
|
|
1505
|
-
|
|
1506
|
-
|
|
1507
|
-
|
|
1508
|
-
|
|
1509
|
-
|
|
1510
|
-
|
|
1511
|
-
|
|
1512
|
-
|
|
1513
|
-
|
|
1514
|
-
|
|
1515
|
-
|
|
1516
|
-
|
|
1517
|
-
|
|
1518
|
-
|
|
1519
|
-
|
|
1520
|
-
|
|
1521
|
-
|
|
1522
|
-
|
|
1523
|
-
|
|
1524
|
-
|
|
1525
|
-
|
|
1526
|
-
|
|
1527
|
-
|
|
1528
|
-
|
|
1529
|
-
|
|
1530
|
-
|
|
1531
|
-
|
|
1532
|
-
|
|
1533
|
-
|
|
1534
|
-
|
|
1535
|
-
|
|
1536
|
-
|
|
1537
|
-
|
|
1538
|
-
|
|
1539
|
-
|
|
1540
|
-
|
|
1541
|
-
|
|
1542
|
-
|
|
1543
|
-
|
|
1544
|
-
|
|
1545
|
-
|
|
1546
|
-
|
|
1547
|
-
|
|
1548
|
-
|
|
1549
|
-
|
|
1550
|
-
|
|
1551
|
-
|
|
1552
|
-
|
|
1553
|
-
|
|
1554
|
-
|
|
1555
|
-
|
|
1556
|
-
|
|
1557
|
-
|
|
1558
|
-
|
|
1559
|
-
|
|
1560
|
-
|
|
1561
|
-
|
|
1562
|
-
|
|
1563
|
-
|
|
1564
|
-
|
|
1565
|
-
|
|
1566
|
-
|
|
1567
|
-
|
|
1568
|
-
|
|
1569
|
-
|
|
1570
|
-
|
|
1571
|
-
|
|
1572
|
-
|
|
1573
|
-
|
|
1574
|
-
|
|
1575
|
-
|
|
1576
|
-
|
|
1577
|
-
|
|
1578
|
-
|
|
1579
|
-
|
|
1580
|
-
|
|
1581
|
-
|
|
1582
|
-
|
|
1583
|
-
|
|
1584
|
-
|
|
1585
|
-
|
|
1586
|
-
|
|
1587
|
-
|
|
1588
|
-
|
|
1589
|
-
|
|
1590
|
-
|
|
1591
|
-
|
|
1592
|
-
|
|
1593
|
-
|
|
1594
|
-
|
|
1595
|
-
|
|
1596
|
-
|
|
1597
|
-
|
|
1598
|
-
|
|
1599
|
-
|
|
1600
|
-
|
|
1601
|
-
|
|
1602
|
-
|
|
1603
|
-
|
|
1604
|
-
|
|
1605
|
-
|
|
1606
|
-
|
|
1607
|
-
|
|
1608
|
-
|
|
1609
|
-
|
|
1610
|
-
|
|
1611
|
-
|
|
1612
|
-
|
|
1613
|
-
|
|
1614
|
-
|
|
1615
|
-
|
|
1616
|
-
|
|
1617
|
-
|
|
1618
|
-
|
|
1619
|
-
|
|
1620
|
-
|
|
1621
|
-
|
|
1622
|
-
|
|
1623
|
-
|
|
1624
|
-
|
|
1625
|
-
|
|
1626
|
-
|
|
1627
|
-
|
|
1628
|
-
|
|
1629
|
-
|
|
1630
|
-
|
|
1631
|
-
|
|
1632
|
-
|
|
1633
|
-
|
|
1634
|
-
|
|
1635
|
-
|
|
1636
|
-
|
|
1637
|
-
|
|
1638
|
-
|
|
1639
|
-
|
|
1640
|
-
|
|
1641
|
-
|
|
1642
|
-
|
|
1643
|
-
|
|
1644
|
-
|
|
1645
|
-
|
|
1646
|
-
|
|
1647
|
-
|
|
1648
|
-
|
|
1649
|
-
|
|
1650
|
-
|
|
1651
|
-
|
|
1652
|
-
|
|
1653
|
-
|
|
1654
|
-
|
|
1655
|
-
|
|
1656
|
-
|
|
1657
|
-
|
|
1658
|
-
|
|
1659
|
-
|
|
1660
|
-
|
|
1661
|
-
|
|
1662
|
-
|
|
1663
|
-
|
|
1664
|
-
|
|
1665
|
-
|
|
1666
|
-
|
|
1667
|
-
|
|
1668
|
-
|
|
1669
|
-
|
|
1670
|
-
|
|
1671
|
-
|
|
1672
|
-
|
|
1673
|
-
|
|
1674
|
-
|
|
1675
|
-
|
|
1676
|
-
|
|
1677
|
-
|
|
1678
|
-
|
|
1679
|
-
|
|
1680
|
-
|
|
1681
|
-
|
|
1682
|
-
|
|
1683
|
-
|
|
1684
|
-
|
|
1685
|
-
|
|
1686
|
-
|
|
1687
|
-
|
|
1688
|
-
|
|
1689
|
-
|
|
1690
|
-
|
|
1691
|
-
|
|
1692
|
-
|
|
1693
|
-
|
|
1694
|
-
|
|
1695
|
-
|
|
1696
|
-
|
|
1697
|
-
|
|
1698
|
-
|
|
1699
|
-
|
|
1700
|
-
|
|
1701
|
-
|
|
1702
|
-
|
|
1703
|
-
|
|
1704
|
-
|
|
1705
|
-
|
|
1706
|
-
|
|
1707
|
-
|
|
1708
|
-
|
|
1709
|
-
|
|
1710
|
-
|
|
1711
|
-
|
|
1712
|
-
|
|
1713
|
-
|
|
1714
|
-
|
|
1715
|
-
|
|
1716
|
-
|
|
1717
|
-
|
|
1718
|
-
|
|
1719
|
-
|
|
1720
|
-
|
|
1721
|
-
|
|
1722
|
-
|
|
1723
|
-
|
|
1724
|
-
|
|
1725
|
-
|
|
1726
|
-
|
|
1727
|
-
|
|
1728
|
-
|
|
1729
|
-
|
|
1730
|
-
|
|
1731
|
-
|
|
1732
|
-
|
|
1733
|
-
|
|
1734
|
-
|
|
1735
|
-
|
|
1736
|
-
|
|
1737
|
-
|
|
1738
|
-
|
|
1739
|
-
|
|
1740
|
-
|
|
1741
|
-
|
|
1742
|
-
|
|
1743
|
-
|
|
1744
|
-
|
|
1745
|
-
|
|
1746
|
-
|
|
1747
|
-
|
|
1748
|
-
|
|
1749
|
-
|
|
1750
|
-
|
|
1751
|
-
|
|
1752
|
-
|
|
1753
|
-
|
|
1754
|
-
|
|
1755
|
-
|
|
1756
|
-
|
|
1757
|
-
|
|
1758
|
-
|
|
1759
|
-
|
|
1760
|
-
|
|
1761
|
-
|
|
1762
|
-
|
|
1763
|
-
|
|
1764
|
-
|
|
1765
|
-
|
|
1766
|
-
|
|
1767
|
-
|
|
1768
|
-
|
|
1769
|
-
|
|
1770
|
-
|
|
1771
|
-
|
|
1772
|
-
|
|
1773
|
-
|
|
1774
|
-
|
|
1775
|
-
|
|
1776
|
-
|
|
1777
|
-
|
|
1778
|
-
|
|
1779
|
-
|
|
1780
|
-
|
|
1781
|
-
|
|
1782
|
-
|
|
1783
|
-
|
|
1784
|
-
|
|
1785
|
-
|
|
1786
|
-
|
|
1787
|
-
|
|
1788
|
-
|
|
1789
|
-
|
|
1790
|
-
|
|
1791
|
-
|
|
1792
|
-
|
|
1793
|
-
|
|
1794
|
-
|
|
1795
|
-
|
|
1796
|
-
|
|
1797
|
-
|
|
1798
|
-
|
|
1799
|
-
|
|
1800
|
-
|
|
1801
|
-
|
|
1802
|
-
|
|
1803
|
-
|
|
1804
|
-
|
|
1805
|
-
|
|
1806
|
-
|
|
1807
|
-
|
|
1808
|
-
|
|
1809
|
-
|
|
1810
|
-
|
|
1811
|
-
|
|
1812
|
-
|
|
1813
|
-
|
|
1814
|
-
|
|
1815
|
-
|
|
1816
|
-
|
|
1817
|
-
|
|
1818
|
-
|
|
1819
|
-
|
|
1820
|
-
|
|
1821
|
-
|
|
1822
|
-
|
|
1823
|
-
|
|
1824
|
-
|
|
1825
|
-
|
|
1826
|
-
|
|
1827
|
-
|
|
1828
|
-
|
|
1829
|
-
|
|
1830
|
-
|
|
1831
|
-
|
|
1832
|
-
|
|
1833
|
-
|
|
1834
|
-
|
|
1835
|
-
|
|
1836
|
-
|
|
1837
|
-
|
|
1838
|
-
|
|
1839
|
-
|
|
1840
|
-
|
|
1841
|
-
|
|
1842
|
-
|
|
1843
|
-
|
|
1844
|
-
|
|
1845
|
-
|
|
1846
|
-
|
|
1847
|
-
|
|
1848
|
-
|
|
1849
|
-
|
|
1850
|
-
|
|
1851
|
-
|
|
1852
|
-
|
|
1853
|
-
|
|
1854
|
-
|
|
1855
|
-
|
|
1856
|
-
|
|
1857
|
-
|
|
1858
|
-
|
|
1859
|
-
|
|
1860
|
-
|
|
1861
|
-
|
|
1862
|
-
|
|
1863
|
-
|
|
1864
|
-
|
|
1865
|
-
|
|
1866
|
-
|
|
1867
|
-
|
|
1868
|
-
|
|
1869
|
-
|
|
1870
|
-
|
|
1871
|
-
|
|
1872
|
-
|
|
1873
|
-
|
|
1874
|
-
|
|
1875
|
-
|
|
1876
|
-
|
|
1877
|
-
|
|
1878
|
-
|
|
1879
|
-
|
|
1880
|
-
|
|
1881
|
-
|
|
1882
|
-
|
|
1883
|
-
|
|
1884
|
-
|
|
1885
|
-
|
|
1886
|
-
|
|
1887
|
-
|
|
1888
|
-
|
|
1889
|
-
|
|
1890
|
-
|
|
1891
|
-
|
|
1892
|
-
|
|
1893
|
-
|
|
1894
|
-
|
|
1895
|
-
|
|
1896
|
-
|
|
1897
|
-
|
|
1898
|
-
|
|
1899
|
-
|
|
1900
|
-
|
|
1901
|
-
|
|
1902
|
-
|
|
1903
|
-
|
|
1904
|
-
|
|
1905
|
-
|
|
1906
|
-
|
|
1907
|
-
|
|
1908
|
-
|
|
1909
|
-
|
|
1910
|
-
|
|
1911
|
-
|
|
1912
|
-
|
|
1913
|
-
|
|
1914
|
-
|
|
1915
|
-
|
|
1916
|
-
|
|
1917
|
-
|
|
1918
|
-
|
|
1919
|
-
|
|
1920
|
-
|
|
1921
|
-
|
|
1922
|
-
|
|
1923
|
-
|
|
1924
|
-
|
|
1925
|
-
|
|
1926
|
-
|
|
1927
|
-
|
|
1928
|
-
|
|
1929
|
-
|
|
1930
|
-
|
|
1931
|
-
|
|
1932
|
-
|
|
1933
|
-
|
|
1934
|
-
|
|
1935
|
-
|
|
1936
|
-
|
|
1937
|
-
|
|
1938
|
-
|
|
1939
|
-
|
|
1940
|
-
|
|
1941
|
-
|
|
1942
|
-
|
|
1943
|
-
|
|
1944
|
-
|
|
1945
|
-
|
|
1946
|
-
|
|
1947
|
-
|
|
1948
|
-
|
|
1949
|
-
|
|
1950
|
-
|
|
1951
|
-
|
|
1952
|
-
|
|
1953
|
-
|
|
1954
|
-
|
|
1955
|
-
|
|
1956
|
-
|
|
1957
|
-
|
|
1958
|
-
|
|
1959
|
-
|
|
1960
|
-
|
|
1961
|
-
|
|
1962
|
-
|
|
1963
|
-
|
|
1964
|
-
|
|
1965
|
-
|
|
1966
|
-
|
|
1967
|
-
|
|
1968
|
-
|
|
1969
|
-
|
|
1970
|
-
|
|
1971
|
-
|
|
1972
|
-
|
|
1973
|
-
|
|
1974
|
-
|
|
1975
|
-
|
|
1976
|
-
|
|
1977
|
-
|
|
1978
|
-
|
|
1979
|
-
|
|
1980
|
-
|
|
1981
|
-
|
|
1982
|
-
|
|
1983
|
-
|
|
1984
|
-
|
|
1985
|
-
|
|
1986
|
-
|
|
1987
|
-
|
|
1988
|
-
|
|
1989
|
-
|
|
1990
|
-
|
|
1991
|
-
|
|
1992
|
-
|
|
1993
|
-
|
|
1994
|
-
|
|
1995
|
-
|
|
1996
|
-
|
|
1997
|
-
|
|
1998
|
-
|
|
1999
|
-
|
|
2000
|
-
|
|
2001
|
-
|
|
2002
|
-
|
|
2003
|
-
|
|
2004
|
-
|
|
2005
|
-
|
|
2006
|
-
|
|
2007
|
-
|
|
2008
|
-
|
|
2009
|
-
|
|
2010
|
-
|
|
2011
|
-
|
|
2012
|
-
|
|
2013
|
-
|
|
2014
|
-
|
|
2015
|
-
|
|
2016
|
-
|
|
2017
|
-
|
|
2018
|
-
|
|
2019
|
-
|
|
2020
|
-
|
|
2021
|
-
|
|
2022
|
-
|
|
2023
|
-
|
|
2024
|
-
|
|
2025
|
-
|
|
2026
|
-
|
|
2027
|
-
|
|
2028
|
-
|
|
2029
|
-
|
|
2030
|
-
|
|
2031
|
-
|
|
2032
|
-
|
|
2033
|
-
|
|
2034
|
-
|
|
2035
|
-
|
|
2036
|
-
|
|
2037
|
-
|
|
2038
|
-
|
|
2039
|
-
|
|
2040
|
-
|
|
2041
|
-
|
|
2042
|
-
|
|
2043
|
-
|
|
2044
|
-
|
|
2045
|
-
|
|
2046
|
-
|
|
2047
|
-
|
|
2048
|
-
|
|
2049
|
-
|
|
2050
|
-
|
|
2051
|
-
|
|
2052
|
-
|
|
2053
|
-
|
|
2054
|
-
|
|
2055
|
-
|
|
2056
|
-
|
|
2057
|
-
|
|
2058
|
-
|
|
2059
|
-
|
|
2060
|
-
|
|
2061
|
-
|
|
2062
|
-
|
|
2063
|
-
|
|
2064
|
-
|
|
2065
|
-
|
|
2066
|
-
|
|
2067
|
-
|
|
2068
|
-
|
|
2069
|
-
|
|
2070
|
-
|
|
2071
|
-
|
|
2072
|
-
|
|
2073
|
-
|
|
2074
|
-
|
|
2075
|
-
|
|
2076
|
-
|
|
2077
|
-
|
|
2078
|
-
|
|
2079
|
-
|
|
2080
|
-
|
|
2081
|
-
|
|
2082
|
-
|
|
2083
|
-
|
|
2084
|
-
|
|
2085
|
-
|
|
2086
|
-
|
|
2087
|
-
|
|
2088
|
-
|
|
2089
|
-
|
|
2090
|
-
|
|
2091
|
-
|
|
2092
|
-
|
|
2093
|
-
|
|
2094
|
-
|
|
2095
|
-
|
|
2096
|
-
|
|
2097
|
-
|
|
2098
|
-
|
|
2099
|
-
|
|
2100
|
-
|
|
2101
|
-
|
|
2102
|
-
|
|
2103
|
-
|
|
2104
|
-
|
|
2105
|
-
|
|
2106
|
-
|
|
2107
|
-
|
|
2108
|
-
|
|
2109
|
-
|
|
2110
|
-
|
|
2111
|
-
|
|
2112
|
-
|
|
2113
|
-
|
|
2114
|
-
|
|
2115
|
-
|
|
2116
|
-
|
|
2117
|
-
|
|
2118
|
-
|
|
2119
|
-
|
|
2120
|
-
|
|
2121
|
-
|
|
2122
|
-
|
|
2123
|
-
|
|
2124
|
-
|
|
2125
|
-
|
|
2126
|
-
|
|
2127
|
-
|
|
2128
|
-
|
|
2129
|
-
|
|
2130
|
-
|
|
2131
|
-
|
|
2132
|
-
|
|
2133
|
-
|
|
2134
|
-
|
|
2135
|
-
|
|
2136
|
-
|
|
2137
|
-
|
|
2138
|
-
|
|
2139
|
-
|
|
2140
|
-
|
|
2141
|
-
|
|
2142
|
-
|
|
2143
|
-
|
|
2144
|
-
|
|
2145
|
-
|
|
2146
|
-
|
|
2147
|
-
|
|
2148
|
-
|
|
2149
|
-
|
|
2150
|
-
|
|
2151
|
-
|
|
2152
|
-
|
|
2153
|
-
|
|
2154
|
-
|
|
2155
|
-
|
|
2156
|
-
|
|
2157
|
-
|
|
2158
|
-
|
|
2159
|
-
|
|
2160
|
-
|
|
2161
|
-
|
|
2162
|
-
|
|
2163
|
-
|
|
2164
|
-
|
|
2165
|
-
|
|
2166
|
-
|
|
2167
|
-
|
|
2168
|
-
|
|
2169
|
-
|
|
2170
|
-
|
|
2171
|
-
|
|
2172
|
-
|
|
2173
|
-
|
|
2174
|
-
|
|
2175
|
-
|
|
2176
|
-
|
|
2177
|
-
|
|
2178
|
-
|
|
2179
|
-
|
|
2180
|
-
|
|
2181
|
-
|
|
2182
|
-
|
|
2183
|
-
|
|
2184
|
-
|
|
2185
|
-
|
|
2186
|
-
|
|
2187
|
-
|
|
2188
|
-
|
|
2189
|
-
|
|
2190
|
-
|
|
2191
|
-
|
|
2192
|
-
|
|
2193
|
-
|
|
2194
|
-
|
|
2195
|
-
|
|
2196
|
-
|
|
2197
|
-
|
|
2198
|
-
|
|
2199
|
-
|
|
2200
|
-
|
|
2201
|
-
|
|
2202
|
-
|
|
2203
|
-
|
|
2204
|
-
|
|
2205
|
-
|
|
2206
|
-
|
|
2207
|
-
|
|
2208
|
-
|
|
2209
|
-
|
|
2210
|
-
|
|
2211
|
-
|
|
2212
|
-
|
|
2213
|
-
|
|
2214
|
-
|
|
2215
|
-
|
|
2216
|
-
|
|
2217
|
-
|
|
2218
|
-
|
|
2219
|
-
|
|
2220
|
-
|
|
2221
|
-
|
|
2222
|
-
|
|
2223
|
-
|
|
2224
|
-
|
|
2225
|
-
|
|
2226
|
-
|
|
2227
|
-
|
|
2228
|
-
|
|
2229
|
-
|
|
2230
|
-
|
|
2231
|
-
|
|
2232
|
-
|
|
2233
|
-
|
|
2234
|
-
|
|
2235
|
-
|
|
2236
|
-
|
|
2237
|
-
|
|
2238
|
-
|
|
2239
|
-
|
|
2240
|
-
|
|
2241
|
-
|
|
2242
|
-
|
|
2243
|
-
|
|
2244
|
-
|
|
2245
|
-
|
|
2246
|
-
|
|
2247
|
-
|
|
2248
|
-
|
|
2249
|
-
|
|
2250
|
-
|
|
2251
|
-
|
|
2252
|
-
|
|
2253
|
-
|
|
2254
|
-
|
|
2255
|
-
|
|
2256
|
-
|
|
2257
|
-
|
|
2258
|
-
|
|
2259
|
-
|
|
2260
|
-
|
|
2261
|
-
|
|
2262
|
-
|
|
2263
|
-
|
|
2264
|
-
|
|
2265
|
-
|
|
2266
|
-
|
|
2267
|
-
|
|
2268
|
-
|
|
2269
|
-
|
|
2270
|
-
|
|
2271
|
-
|
|
2272
|
-
|
|
2273
|
-
|
|
2274
|
-
|
|
2275
|
-
|
|
2276
|
-
|
|
2277
|
-
|
|
2278
|
-
|
|
2279
|
-
|
|
2280
|
-
|
|
2281
|
-
|
|
2282
|
-
|
|
2283
|
-
|
|
2284
|
-
|
|
2285
|
-
|
|
2286
|
-
|
|
2287
|
-
|
|
2288
|
-
|
|
2289
|
-
|
|
2290
|
-
|
|
2291
|
-
|
|
2292
|
-
|
|
2293
|
-
|
|
2294
|
-
|
|
2295
|
-
|
|
2296
|
-
|
|
2297
|
-
|
|
2298
|
-
|
|
2299
|
-
|
|
2300
|
-
|
|
2301
|
-
|
|
2302
|
-
|
|
2303
|
-
|
|
2304
|
-
|
|
2305
|
-
|
|
2306
|
-
|
|
2307
|
-
|
|
2308
|
-
|
|
2309
|
-
|
|
2310
|
-
|
|
2311
|
-
|
|
2312
|
-
|
|
2313
|
-
|
|
2314
|
-
|
|
2315
|
-
|
|
2316
|
-
|
|
2317
|
-
|
|
2318
|
-
|
|
2319
|
-
|
|
2320
|
-
|
|
2321
|
-
|
|
2322
|
-
|
|
2323
|
-
|
|
2324
|
-
|
|
2325
|
-
|
|
2326
|
-
|
|
2327
|
-
|
|
2328
|
-
|
|
2329
|
-
|
|
2330
|
-
|
|
2331
|
-
|
|
2332
|
-
|
|
2333
|
-
|
|
2334
|
-
|
|
2335
|
-
|
|
2336
|
-
|
|
2337
|
-
|
|
2338
|
-
|
|
2339
|
-
|
|
2340
|
-
|
|
2341
|
-
|
|
2342
|
-
|
|
2343
|
-
|
|
2344
|
-
|
|
2345
|
-
|
|
2346
|
-
|
|
2347
|
-
|
|
2348
|
-
|
|
2349
|
-
|
|
2350
|
-
|
|
2351
|
-
|
|
2352
|
-
|
|
2353
|
-
|
|
2354
|
-
|
|
2355
|
-
|
|
2356
|
-
|
|
2357
|
-
|
|
2358
|
-
|
|
2359
|
-
|
|
2360
|
-
|
|
2361
|
-
|
|
2362
|
-
|
|
2363
|
-
|
|
2364
|
-
|
|
2365
|
-
|
|
2366
|
-
|
|
2367
|
-
|
|
2368
|
-
|
|
2369
|
-
|
|
2370
|
-
|
|
2371
|
-
|
|
2372
|
-
|
|
2373
|
-
|
|
2374
|
-
|
|
2375
|
-
|
|
2376
|
-
|
|
2377
|
-
|
|
2378
|
-
|
|
2379
|
-
|
|
2380
|
-
|
|
2381
|
-
|
|
2382
|
-
|
|
2383
|
-
|
|
2384
|
-
|
|
2385
|
-
|
|
2386
|
-
|
|
2387
|
-
|
|
2388
|
-
|
|
2389
|
-
|
|
2390
|
-
|
|
2391
|
-
|
|
2392
|
-
|
|
2393
|
-
|
|
2394
|
-
|
|
2395
|
-
|
|
2396
|
-
|
|
2397
|
-
|
|
2398
|
-
|
|
2399
|
-
|
|
2400
|
-
|
|
2401
|
-
|
|
2402
|
-
|
|
2403
|
-
|
|
2404
|
-
|
|
2405
|
-
|
|
2406
|
-
|
|
2407
|
-
|
|
2408
|
-
|
|
2409
|
-
|
|
2410
|
-
|
|
2411
|
-
|
|
2412
|
-
|
|
2413
|
-
|
|
2414
|
-
|
|
2415
|
-
|
|
2416
|
-
|
|
2417
|
-
|
|
2418
|
-
|
|
2419
|
-
|
|
2420
|
-
|
|
2421
|
-
|
|
2422
|
-
|
|
2423
|
-
|
|
2424
|
-
|
|
2425
|
-
|
|
2426
|
-
|
|
2427
|
-
|
|
2428
|
-
|
|
2429
|
-
|
|
2430
|
-
|
|
2431
|
-
|
|
2432
|
-
|
|
2433
|
-
|
|
2434
|
-
|
|
2435
|
-
|
|
2436
|
-
|
|
2437
|
-
|
|
2438
|
-
|
|
2439
|
-
|
|
2440
|
-
|
|
2441
|
-
|
|
2442
|
-
|
|
2443
|
-
|
|
2444
|
-
|
|
2445
|
-
|
|
2446
|
-
|
|
2447
|
-
|
|
2448
|
-
|
|
2449
|
-
|
|
2450
|
-
|
|
2451
|
-
|
|
2452
|
-
|
|
2453
|
-
|
|
2454
|
-
|
|
2455
|
-
|
|
2456
|
-
|
|
2457
|
-
|
|
2458
|
-
|
|
2459
|
-
|
|
2460
|
-
|
|
2461
|
-
|
|
2462
|
-
|
|
2463
|
-
|
|
2464
|
-
|
|
2465
|
-
|
|
2466
|
-
|
|
2467
|
-
|
|
2468
|
-
|
|
2469
|
-
|
|
17
|
+
constructor(options) {
|
|
18
|
+
super({
|
|
19
|
+
...options,
|
|
20
|
+
name: adapterName,
|
|
21
|
+
useFormatDate: true,
|
|
22
|
+
});
|
|
23
|
+
|
|
24
|
+
// instances and adapters
|
|
25
|
+
// raw arrays
|
|
26
|
+
this.listInstanceRaw = new Map();
|
|
27
|
+
this.adapterUpdatesJsonRaw = [];
|
|
28
|
+
this.listErrorInstanceRaw = [];
|
|
29
|
+
|
|
30
|
+
// user arrays
|
|
31
|
+
this.listAllInstances = [];
|
|
32
|
+
this.listAllActiveInstances = [];
|
|
33
|
+
this.listDeactivatedInstances = [];
|
|
34
|
+
this.listAdapterUpdates = [];
|
|
35
|
+
this.listErrorInstance = [];
|
|
36
|
+
|
|
37
|
+
//counts
|
|
38
|
+
this.countAllInstances = 0;
|
|
39
|
+
this.countAllActiveInstances = 0;
|
|
40
|
+
this.countDeactivatedInstances = 0;
|
|
41
|
+
this.countAdapterUpdates = 0;
|
|
42
|
+
this.countErrorInstance = 0;
|
|
43
|
+
|
|
44
|
+
// devices
|
|
45
|
+
// raw arrays
|
|
46
|
+
this.listAllDevicesRaw = new Map();
|
|
47
|
+
this.batteryLowPoweredRaw = [];
|
|
48
|
+
this.offlineDevicesRaw = [];
|
|
49
|
+
this.upgradableDevicesRaw = [];
|
|
50
|
+
|
|
51
|
+
// arrays
|
|
52
|
+
this.listAllDevicesUserRaw = [];
|
|
53
|
+
this.listAllDevices = [];
|
|
54
|
+
this.offlineDevices = [];
|
|
55
|
+
this.linkQualityDevices = [];
|
|
56
|
+
this.batteryPowered = [];
|
|
57
|
+
this.batteryLowPowered = [];
|
|
58
|
+
this.selAdapter = [];
|
|
59
|
+
this.adapterSelected = [];
|
|
60
|
+
this.upgradableList = [];
|
|
61
|
+
|
|
62
|
+
// counts
|
|
63
|
+
this.offlineDevicesCount = 0;
|
|
64
|
+
this.deviceCounter = 0;
|
|
65
|
+
this.linkQualityCount = 0;
|
|
66
|
+
this.batteryPoweredCount = 0;
|
|
67
|
+
this.lowBatteryPoweredCount = 0;
|
|
68
|
+
this.upgradableDevicesCount = 0;
|
|
69
|
+
|
|
70
|
+
// Blacklist
|
|
71
|
+
// Instances
|
|
72
|
+
this.blacklistInstancesLists = [];
|
|
73
|
+
this.blacklistInstancesNotify = [];
|
|
74
|
+
|
|
75
|
+
// Devices
|
|
76
|
+
this.blacklistLists = [];
|
|
77
|
+
this.blacklistAdapterLists = [];
|
|
78
|
+
this.blacklistNotify = [];
|
|
79
|
+
|
|
80
|
+
// Timelist instances
|
|
81
|
+
this.userTimeInstancesList = new Map();
|
|
82
|
+
|
|
83
|
+
// Interval timer
|
|
84
|
+
this.refreshDataTimeout = null;
|
|
85
|
+
|
|
86
|
+
// Shared processing lock – verhindert Race Condition zwischen main() und refreshData()
|
|
87
|
+
this.processingLock = false;
|
|
88
|
+
|
|
89
|
+
// Check if main function is running (wird noch intern genutzt für pendingRescan-Guard)
|
|
90
|
+
this.mainRunning = false;
|
|
91
|
+
|
|
92
|
+
// Pending rescan flag (set if a new device was detected while main() was running)
|
|
93
|
+
this.pendingRescan = false;
|
|
94
|
+
|
|
95
|
+
// Pending refresh flag (set if refreshData() wurde während main() geblockt)
|
|
96
|
+
this.pendingRefresh = false;
|
|
97
|
+
|
|
98
|
+
this.on('ready', this.onReady.bind(this));
|
|
99
|
+
this.on('stateChange', this.onStateChange.bind(this));
|
|
100
|
+
this.on('objectChange', this.onObjectChange.bind(this));
|
|
101
|
+
this.on('message', this.onMessage.bind(this));
|
|
102
|
+
this.on('unload', this.onUnload.bind(this));
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
/**
|
|
106
|
+
* onReady
|
|
107
|
+
*/
|
|
108
|
+
async onReady() {
|
|
109
|
+
this.log.debug(`Adapter ${adapterName} was started`);
|
|
110
|
+
|
|
111
|
+
// set user language
|
|
112
|
+
if (this.config.userSelectedLanguage === '') {
|
|
113
|
+
if (this.language !== undefined && this.language !== null) {
|
|
114
|
+
this.config.userSelectedLanguage = this.language;
|
|
115
|
+
} else {
|
|
116
|
+
this.config.userSelectedLanguage = 'de';
|
|
117
|
+
}
|
|
118
|
+
}
|
|
119
|
+
this.log.debug(`Set language to ${this.config.userSelectedLanguage}`);
|
|
120
|
+
|
|
121
|
+
this.configCreateInstanceList = this.config.checkAdapterInstances;
|
|
122
|
+
this.configListOnlyBattery = this.config.listOnlyBattery;
|
|
123
|
+
this.configCreateOwnFolder = this.config.createOwnFolder;
|
|
124
|
+
this.configCreateHtmlList = this.config.createHtmlList;
|
|
125
|
+
|
|
126
|
+
try {
|
|
127
|
+
// create list with enabled adapters for monitor devices
|
|
128
|
+
for (const device of Object.values(this.config.tableDevices)) {
|
|
129
|
+
if (device.enabled) {
|
|
130
|
+
for (const [_adapterName, adapter] of Object.entries(adapterArray)) {
|
|
131
|
+
if (String(adapter.adapterKey).toLowerCase() === String(device.adapterKey).toLowerCase()) {
|
|
132
|
+
this.selAdapter.push(adapter);
|
|
133
|
+
this.adapterSelected.push(adapter.adapterKey);
|
|
134
|
+
break;
|
|
135
|
+
}
|
|
136
|
+
}
|
|
137
|
+
}
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
// Check if an adapter to monitor devices is selected.
|
|
141
|
+
if (this.adapterSelected.length >= 1) {
|
|
142
|
+
this.log.debug(JSON.stringify(this.selAdapter));
|
|
143
|
+
this.log.info(`Number of selected adapters to monitor devices: ${this.adapterSelected.length}. Loading data from: ${this.adapterSelected.join(', ')} ...`);
|
|
144
|
+
} else {
|
|
145
|
+
this.log.info(`No adapters selected to monitor devices.`);
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
// create Blacklist
|
|
149
|
+
await crud.createBlacklist(this);
|
|
150
|
+
|
|
151
|
+
// create user defined list with time of error for instances
|
|
152
|
+
await crud.createTimeListInstances(this);
|
|
153
|
+
|
|
154
|
+
//create datapoints for each adapter if selected
|
|
155
|
+
for (const [id] of Object.entries(adapterArray)) {
|
|
156
|
+
try {
|
|
157
|
+
if (!this.configCreateOwnFolder) {
|
|
158
|
+
await crud.deleteDPsForEachAdapter(this, id);
|
|
159
|
+
await crud.deleteHtmlListDatapoints(this, id);
|
|
160
|
+
} else {
|
|
161
|
+
const adapter = adapterArray[id];
|
|
162
|
+
|
|
163
|
+
if (this.adapterSelected.includes(adapter.adapterKey)) {
|
|
164
|
+
await crud.createDPsForEachAdapter(this, id);
|
|
165
|
+
// create HTML list datapoints
|
|
166
|
+
if (!this.configCreateHtmlList) {
|
|
167
|
+
await crud.deleteHtmlListDatapoints(this, id);
|
|
168
|
+
} else {
|
|
169
|
+
await crud.createHtmlListDatapoints(this, id);
|
|
170
|
+
}
|
|
171
|
+
this.log.debug(`Created datapoints for ${tools.capitalize(id)}`);
|
|
172
|
+
}
|
|
173
|
+
}
|
|
174
|
+
} catch (error) {
|
|
175
|
+
this.log.error(`[onReady - create and fill datapoints for each adapter] - ${error}`);
|
|
176
|
+
}
|
|
177
|
+
}
|
|
178
|
+
|
|
179
|
+
// create HTML list datapoints
|
|
180
|
+
if (!this.configCreateHtmlList) {
|
|
181
|
+
await crud.deleteHtmlListDatapoints(this);
|
|
182
|
+
await crud.deleteHtmlListDatapointsInstances(this);
|
|
183
|
+
} else {
|
|
184
|
+
await crud.createHtmlListDatapoints(this);
|
|
185
|
+
if (this.config.checkAdapterInstances) {
|
|
186
|
+
await crud.createHtmlListDatapointsInstances(this);
|
|
187
|
+
}
|
|
188
|
+
}
|
|
189
|
+
if (!this.config.checkAdapterInstances) {
|
|
190
|
+
await crud.deleteHtmlListDatapointsInstances(this);
|
|
191
|
+
}
|
|
192
|
+
|
|
193
|
+
// instances and adapters
|
|
194
|
+
if (this.configCreateInstanceList) {
|
|
195
|
+
// instances
|
|
196
|
+
await crud.createDPsForInstances(this);
|
|
197
|
+
await this.getAllInstanceData();
|
|
198
|
+
// adapter updates
|
|
199
|
+
await crud.createAdapterUpdateData(this, adapterUpdateListDP);
|
|
200
|
+
} else {
|
|
201
|
+
await crud.deleteDPsForInstances(this);
|
|
202
|
+
}
|
|
203
|
+
|
|
204
|
+
await this.main();
|
|
205
|
+
|
|
206
|
+
// update last contact data in interval
|
|
207
|
+
await this.refreshData();
|
|
208
|
+
|
|
209
|
+
// send overview for low battery devices
|
|
210
|
+
if (this.config.checkSendBatteryMsgDaily) {
|
|
211
|
+
await this.sendScheduleNotifications('lowBatteryDevices');
|
|
212
|
+
}
|
|
213
|
+
|
|
214
|
+
// send overview of offline devices
|
|
215
|
+
if (this.config.checkSendOfflineMsgDaily) {
|
|
216
|
+
await this.sendScheduleNotifications('offlineDevices');
|
|
217
|
+
}
|
|
218
|
+
|
|
219
|
+
// send overview of upgradeable devices
|
|
220
|
+
if (this.config.checkSendUpgradeMsgDaily) {
|
|
221
|
+
await this.sendScheduleNotifications('updateDevices');
|
|
222
|
+
}
|
|
223
|
+
|
|
224
|
+
// send overview of updatable adapters
|
|
225
|
+
if (this.config.checkSendAdapterUpdateMsgDaily) {
|
|
226
|
+
await this.sendScheduleNotifications('updateAdapter');
|
|
227
|
+
}
|
|
228
|
+
|
|
229
|
+
// send overview of deactivated instances
|
|
230
|
+
if (this.config.checkSendInstanceDeactivatedDaily) {
|
|
231
|
+
await this.sendScheduleNotifications('deactivatedInstance');
|
|
232
|
+
}
|
|
233
|
+
|
|
234
|
+
// send overview of instances with error
|
|
235
|
+
if (this.config.checkSendInstanceFailedDaily) {
|
|
236
|
+
await this.sendScheduleNotifications('errorInstance');
|
|
237
|
+
}
|
|
238
|
+
} catch (error) {
|
|
239
|
+
this.log.error(`[onReady] - ${error}`);
|
|
240
|
+
this.terminate ? this.terminate(15) : process.exit(15);
|
|
241
|
+
}
|
|
242
|
+
} // <-- onReady end
|
|
243
|
+
|
|
244
|
+
/**
|
|
245
|
+
* main function
|
|
246
|
+
*/
|
|
247
|
+
async main() {
|
|
248
|
+
// Guard: wenn bereits ein Prozess läuft (main oder refreshData), rescan vormerken und zurückkehren
|
|
249
|
+
if (this.processingLock) {
|
|
250
|
+
this.pendingRescan = true;
|
|
251
|
+
return;
|
|
252
|
+
}
|
|
253
|
+
|
|
254
|
+
this.log.debug(`Function started main`);
|
|
255
|
+
this.processingLock = true;
|
|
256
|
+
this.mainRunning = true;
|
|
257
|
+
|
|
258
|
+
do {
|
|
259
|
+
this.pendingRescan = false;
|
|
260
|
+
|
|
261
|
+
// cancel run if no adapter is selected
|
|
262
|
+
if (this.adapterSelected.length === 0) {
|
|
263
|
+
break;
|
|
264
|
+
}
|
|
265
|
+
|
|
266
|
+
// fill counts and lists of all selected adapter
|
|
267
|
+
try {
|
|
268
|
+
// Erst alle Adapter-Daten sammeln, DANN einmal createLists aufrufen.
|
|
269
|
+
// createLists resettet batteryLowPowered/offlineDevices/etc. am Anfang –
|
|
270
|
+
// wird es innerhalb der Schleife aufgerufen, enthält listAllDevicesRaw beim
|
|
271
|
+
// 1. Durchlauf nur Adapter 0, beim 2. nur Adapter 0+1 usw. → instabile Listen.
|
|
272
|
+
for (let i = 0; i < this.selAdapter.length; i++) {
|
|
273
|
+
await crud.createData(this, i);
|
|
274
|
+
}
|
|
275
|
+
await crud.createLists(this); // einmal, nachdem alle Daten gesammelt sind
|
|
276
|
+
await crud.writeDatapoints(this); // fill the datapoints
|
|
277
|
+
this.log.debug(`Created and filled data for all adapters`);
|
|
278
|
+
} catch (error) {
|
|
279
|
+
this.log.error(`[main - create data of all adapter] - ${error}`);
|
|
280
|
+
}
|
|
281
|
+
|
|
282
|
+
// fill datapoints for each adapter if selected
|
|
283
|
+
if (this.configCreateOwnFolder) {
|
|
284
|
+
try {
|
|
285
|
+
for (const [id] of Object.entries(adapterArray)) {
|
|
286
|
+
const adapter = adapterArray[id];
|
|
287
|
+
|
|
288
|
+
if (this.adapterSelected.includes(adapter.adapterKey)) {
|
|
289
|
+
// createLists nur EINMAL pro Adapter aufrufen, nicht pro Gerät (Systemlast + Mixing-Fix)
|
|
290
|
+
await crud.createLists(this, id);
|
|
291
|
+
await crud.writeDatapoints(this, id); // fill the datapoints
|
|
292
|
+
this.log.debug(`Created and filled data for ${tools.capitalize(id)}`);
|
|
293
|
+
}
|
|
294
|
+
}
|
|
295
|
+
} catch (error) {
|
|
296
|
+
this.log.error(`[main - create and fill datapoints for each adapter] - ${error}`);
|
|
297
|
+
}
|
|
298
|
+
}
|
|
299
|
+
|
|
300
|
+
if (this.pendingRescan) {
|
|
301
|
+
this.log.info(`[main] Pending rescan detected – Schleife wird wiederholt`);
|
|
302
|
+
}
|
|
303
|
+
|
|
304
|
+
} while (this.pendingRescan);
|
|
305
|
+
|
|
306
|
+
this.processingLock = false;
|
|
307
|
+
this.mainRunning = false;
|
|
308
|
+
this.log.debug(`Function finished: ${this.main.name}`);
|
|
309
|
+
|
|
310
|
+
// Falls refreshData() während main() gewartet hat, jetzt nachholen
|
|
311
|
+
if (this.pendingRefresh) {
|
|
312
|
+
this.pendingRefresh = false;
|
|
313
|
+
this.log.debug(`[main] pendingRefresh – starte refreshData nach main()`);
|
|
314
|
+
await this.refreshData();
|
|
315
|
+
}
|
|
316
|
+
} //<--End of main function
|
|
317
|
+
|
|
318
|
+
// If you need to react to object changes, uncomment the following block and the corresponding line in the constructor.
|
|
319
|
+
// You also need to subscribe to the objects with `this.subscribeObjects`, similar to `this.subscribeStates`.
|
|
320
|
+
//
|
|
321
|
+
|
|
322
|
+
async onObjectChange(id, obj) {
|
|
323
|
+
if (obj) {
|
|
324
|
+
try {
|
|
325
|
+
// The object was changed
|
|
326
|
+
//this.log.debug(`object ${id} changed: ${JSON.stringify(obj)}`);
|
|
327
|
+
|
|
328
|
+
if (this.config.checkAdapterInstances && id.startsWith('system.adapter.')) {
|
|
329
|
+
//read new instance data and add it to the lists
|
|
330
|
+
if (typeof id === 'string' && /\d$/.test(id)) {
|
|
331
|
+
await this.getInstanceData(id);
|
|
332
|
+
}
|
|
333
|
+
} else {
|
|
334
|
+
if (Array.from(this.listAllDevicesRaw.values()).some((obj) => obj.mainSelector === id)) {
|
|
335
|
+
if (!this.mainRunning) {
|
|
336
|
+
await this.main();
|
|
337
|
+
} else {
|
|
338
|
+
this.pendingRescan = true;
|
|
339
|
+
}
|
|
340
|
+
} else {
|
|
341
|
+
// Check if the changed object belongs to a monitored adapter (new device)
|
|
342
|
+
const belongsToMonitoredAdapter = this.adapterSelected.some((adapterKey) =>
|
|
343
|
+
id.toLowerCase().startsWith(`${adapterKey.toLowerCase()}.`)
|
|
344
|
+
);
|
|
345
|
+
|
|
346
|
+
if (belongsToMonitoredAdapter) {
|
|
347
|
+
if (!this.mainRunning) {
|
|
348
|
+
this.log.info(`[onObjectChange] New device detected: ${id} – triggering rescan`);
|
|
349
|
+
await this.main();
|
|
350
|
+
} else {
|
|
351
|
+
this.log.debug(`[onObjectChange] main() is running – rescan for ${id} queued`);
|
|
352
|
+
this.pendingRescan = true;
|
|
353
|
+
}
|
|
354
|
+
}
|
|
355
|
+
}
|
|
356
|
+
}
|
|
357
|
+
} catch (error) {
|
|
358
|
+
this.log.error(`Issue at object change: ${error}`);
|
|
359
|
+
}
|
|
360
|
+
} else {
|
|
361
|
+
try {
|
|
362
|
+
// The object was deleted
|
|
363
|
+
this.log.debug(`object ${id} deleted`);
|
|
364
|
+
|
|
365
|
+
// delete instance data in map
|
|
366
|
+
if (this.listInstanceRaw.has(id)) {
|
|
367
|
+
this.listInstanceRaw.delete(id);
|
|
368
|
+
}
|
|
369
|
+
|
|
370
|
+
// delete device data in map
|
|
371
|
+
if (this.listAllDevicesRaw.has(id)) {
|
|
372
|
+
this.listAllDevicesRaw.delete(id);
|
|
373
|
+
}
|
|
374
|
+
// also remove all child devices if a parent/adapter object was deleted
|
|
375
|
+
const idPrefix = `${id}.`;
|
|
376
|
+
for (const key of this.listAllDevicesRaw.keys()) {
|
|
377
|
+
if (key.startsWith(idPrefix)) {
|
|
378
|
+
this.log.debug(`[onObjectChange] removing child device from map: ${key}`);
|
|
379
|
+
this.listAllDevicesRaw.delete(key);
|
|
380
|
+
}
|
|
381
|
+
}
|
|
382
|
+
|
|
383
|
+
//unsubscribe of Objects and states
|
|
384
|
+
this.unsubscribeForeignObjects(id);
|
|
385
|
+
this.unsubscribeForeignStates(id);
|
|
386
|
+
} catch (error) {
|
|
387
|
+
this.log.error(`Issue at object deletion: ${error}`);
|
|
388
|
+
}
|
|
389
|
+
}
|
|
390
|
+
}
|
|
391
|
+
|
|
392
|
+
async onStateChange(id, state) {
|
|
393
|
+
if (state) {
|
|
394
|
+
// this.log.debug(`State changed: ${id} changed ${state.val}`);
|
|
395
|
+
try {
|
|
396
|
+
/*=============================================
|
|
397
|
+
= Instances / Adapter =
|
|
398
|
+
=============================================*/
|
|
399
|
+
if (this.config.checkAdapterInstances) {
|
|
400
|
+
// Adapter Update data
|
|
401
|
+
if (id.endsWith('updatesJson')) {
|
|
402
|
+
await this.renewAdapterUpdateData(id);
|
|
403
|
+
}
|
|
404
|
+
// Instanz data
|
|
405
|
+
if (Array.from(this.listInstanceRaw.values()).some((obj) => Object.values(obj).includes(id))) {
|
|
406
|
+
await this.renewInstanceData(id, state);
|
|
407
|
+
}
|
|
408
|
+
}
|
|
409
|
+
|
|
410
|
+
/*=============================================
|
|
411
|
+
= Devices =
|
|
412
|
+
=============================================*/
|
|
413
|
+
if (Array.from(this.listAllDevicesRaw.values()).some((obj) => Object.values(obj).includes(id))) {
|
|
414
|
+
const listDirty = await this.renewDeviceData(id, state);
|
|
415
|
+
|
|
416
|
+
if (listDirty) {
|
|
417
|
+
await crud.createLists(this);
|
|
418
|
+
await crud.writeDatapoints(this);
|
|
419
|
+
|
|
420
|
+
if (this.configCreateOwnFolder) {
|
|
421
|
+
for (const [adId] of Object.entries(adapterArray)) {
|
|
422
|
+
const adapter = adapterArray[adId];
|
|
423
|
+
if (this.adapterSelected.includes(adapter.adapterKey)) {
|
|
424
|
+
await crud.createLists(this, adId);
|
|
425
|
+
await crud.writeDatapoints(this, adId);
|
|
426
|
+
}
|
|
427
|
+
}
|
|
428
|
+
}
|
|
429
|
+
}
|
|
430
|
+
}
|
|
431
|
+
} catch (error) {
|
|
432
|
+
this.log.error(`Issue at state change: ${id}`);
|
|
433
|
+
}
|
|
434
|
+
} else {
|
|
435
|
+
// The state was deleted
|
|
436
|
+
this.log.debug(`state ${id} deleted`);
|
|
437
|
+
}
|
|
438
|
+
}
|
|
439
|
+
|
|
440
|
+
onMessage(obj) {
|
|
441
|
+
const devices = [];
|
|
442
|
+
const instances = [];
|
|
443
|
+
const instancesTime = [];
|
|
444
|
+
let countDevices = 0;
|
|
445
|
+
let countInstances = 0;
|
|
446
|
+
|
|
447
|
+
switch (obj.command) {
|
|
448
|
+
case 'devicesList':
|
|
449
|
+
if (obj.message) {
|
|
450
|
+
try {
|
|
451
|
+
for (const deviceData of this.listAllDevicesRaw.values()) {
|
|
452
|
+
const label = `${deviceData.Adapter}: ${deviceData.Device}`;
|
|
453
|
+
const valueObjectDevices = {
|
|
454
|
+
deviceName: deviceData.Device,
|
|
455
|
+
adapter: deviceData.Adapter,
|
|
456
|
+
path: deviceData.Path,
|
|
457
|
+
};
|
|
458
|
+
devices[countDevices] = {label: label, value: JSON.stringify(valueObjectDevices)};
|
|
459
|
+
countDevices++;
|
|
460
|
+
}
|
|
461
|
+
const sortDevices = devices.slice(0);
|
|
462
|
+
sortDevices.sort(function (a, b) {
|
|
463
|
+
const x = a.label;
|
|
464
|
+
const y = b.label;
|
|
465
|
+
return x < y ? -1 : x > y ? 1 : 0;
|
|
466
|
+
});
|
|
467
|
+
this.sendTo(obj.from, obj.command, sortDevices, obj.callback);
|
|
468
|
+
} catch (error) {
|
|
469
|
+
this.log.error(`[onMessage - deviceList for blacklisttable] - ${error}`);
|
|
470
|
+
}
|
|
471
|
+
}
|
|
472
|
+
break;
|
|
473
|
+
|
|
474
|
+
case 'instancesList':
|
|
475
|
+
if (obj.message) {
|
|
476
|
+
try {
|
|
477
|
+
for (const [instance, instanceData] of this.listInstanceRaw) {
|
|
478
|
+
const label = `${instanceData.Adapter}: ${instance}`;
|
|
479
|
+
const valueObjectInstances = {
|
|
480
|
+
adapter: instanceData.Adapter,
|
|
481
|
+
instanceID: instance,
|
|
482
|
+
};
|
|
483
|
+
instances[countInstances] = {label: label, value: JSON.stringify(valueObjectInstances)};
|
|
484
|
+
countInstances++;
|
|
485
|
+
}
|
|
486
|
+
const sortInstances = instances.slice(0);
|
|
487
|
+
sortInstances.sort(function (a, b) {
|
|
488
|
+
const x = a.label;
|
|
489
|
+
const y = b.label;
|
|
490
|
+
return x < y ? -1 : x > y ? 1 : 0;
|
|
491
|
+
});
|
|
492
|
+
this.sendTo(obj.from, obj.command, sortInstances, obj.callback);
|
|
493
|
+
} catch (error) {
|
|
494
|
+
this.log.error(`[onMessage - instanceList] - ${error}`);
|
|
495
|
+
}
|
|
496
|
+
}
|
|
497
|
+
break;
|
|
498
|
+
case 'instancesListTime':
|
|
499
|
+
if (obj.message) {
|
|
500
|
+
try {
|
|
501
|
+
for (const [instance, instanceData] of this.listInstanceRaw) {
|
|
502
|
+
const label = `${instanceData.Adapter}: ${instance}`;
|
|
503
|
+
const valueObjectInstances = {
|
|
504
|
+
adapter: instanceData.Adapter,
|
|
505
|
+
instanceName: instance,
|
|
506
|
+
};
|
|
507
|
+
instancesTime[countInstances] = {label: label, value: JSON.stringify(valueObjectInstances)};
|
|
508
|
+
countInstances++;
|
|
509
|
+
}
|
|
510
|
+
const sortInstances = instancesTime.slice(0);
|
|
511
|
+
sortInstances.sort(function (a, b) {
|
|
512
|
+
const x = a.label;
|
|
513
|
+
const y = b.label;
|
|
514
|
+
return x < y ? -1 : x > y ? 1 : 0;
|
|
515
|
+
});
|
|
516
|
+
this.sendTo(obj.from, obj.command, sortInstances, obj.callback);
|
|
517
|
+
} catch (error) {
|
|
518
|
+
this.log.error(`[onMessage - instanceList] - ${error}`);
|
|
519
|
+
}
|
|
520
|
+
}
|
|
521
|
+
break;
|
|
522
|
+
default:
|
|
523
|
+
this.log.warn(`[onMessage] Unknown command: ${obj.command}`);
|
|
524
|
+
break;
|
|
525
|
+
}
|
|
526
|
+
}
|
|
527
|
+
|
|
528
|
+
/**
|
|
529
|
+
* refresh data with interval
|
|
530
|
+
* is neccessary to refresh lastContact data, especially of devices without state changes
|
|
531
|
+
*/
|
|
532
|
+
async refreshData() {
|
|
533
|
+
if (isUnloaded) {
|
|
534
|
+
return;
|
|
535
|
+
} // cancel run if unloaded was called.
|
|
536
|
+
|
|
537
|
+
// Timer ZUERST clearen – vor allen awaits, um Timer-Akkumulation zu verhindern
|
|
538
|
+
if (this.refreshDataTimeout) {
|
|
539
|
+
this.clearTimeout(this.refreshDataTimeout);
|
|
540
|
+
this.refreshDataTimeout = null;
|
|
541
|
+
}
|
|
542
|
+
|
|
543
|
+
// Race Condition Guard: wenn main() gerade läuft, refresh verschieben
|
|
544
|
+
if (this.processingLock) {
|
|
545
|
+
this.log.debug(`[refreshData] processingLock aktiv – refreshData wird nach main() nachgeholt`);
|
|
546
|
+
this.pendingRefresh = true;
|
|
547
|
+
// Timer neu ansetzen damit der refresh nicht komplett verloren geht
|
|
548
|
+
this.refreshDataTimeout = this.setTimeout(async () => {
|
|
549
|
+
this.log.debug('Updating Data (delayed after lock)');
|
|
550
|
+
await this.refreshData();
|
|
551
|
+
}, this.config.updateinterval * 1000);
|
|
552
|
+
return;
|
|
553
|
+
}
|
|
554
|
+
|
|
555
|
+
this.processingLock = true;
|
|
556
|
+
const nextTimeout = this.config.updateinterval * 1000;
|
|
557
|
+
|
|
558
|
+
try {
|
|
559
|
+
const statusChanged = await tools.checkLastContact(this);
|
|
560
|
+
|
|
561
|
+
if (statusChanged) {
|
|
562
|
+
this.log.debug(`[refreshData] Statusänderung erkannt – Listen werden neu gebaut`);
|
|
563
|
+
await crud.createLists(this);
|
|
564
|
+
await crud.writeDatapoints(this);
|
|
565
|
+
|
|
566
|
+
if (this.configCreateOwnFolder) {
|
|
567
|
+
for (const [id] of Object.entries(adapterArray)) {
|
|
568
|
+
const adapter = adapterArray[id];
|
|
569
|
+
|
|
570
|
+
if (this.adapterSelected.includes(adapter.adapterKey)) {
|
|
571
|
+
await crud.createLists(this, id);
|
|
572
|
+
await crud.writeDatapoints(this, id);
|
|
573
|
+
this.log.debug(`Created and filled data for ${tools.capitalize(id)}`);
|
|
574
|
+
}
|
|
575
|
+
}
|
|
576
|
+
}
|
|
577
|
+
} else {
|
|
578
|
+
this.log.debug(`[refreshData] Kein Statuswechsel – kein Neuschreiben der Listen`);
|
|
579
|
+
const lastCheck = `${this.formatDate(new Date(), 'DD.MM.YYYY')} - ${this.formatDate(new Date(), 'hh:mm:ss')}`;
|
|
580
|
+
await this.setStateChangedAsync('lastCheck', lastCheck, true);
|
|
581
|
+
}
|
|
582
|
+
|
|
583
|
+
if (this.configCreateInstanceList) {
|
|
584
|
+
await this.createInstanceList();
|
|
585
|
+
await this.writeInstanceDPs();
|
|
586
|
+
}
|
|
587
|
+
} finally {
|
|
588
|
+
this.processingLock = false;
|
|
589
|
+
}
|
|
590
|
+
|
|
591
|
+
if (isUnloaded) {
|
|
592
|
+
return;
|
|
593
|
+
} // nochmal prüfen nach den awaits
|
|
594
|
+
|
|
595
|
+
this.refreshDataTimeout = this.setTimeout(async () => {
|
|
596
|
+
this.log.debug('Updating Data');
|
|
597
|
+
await this.refreshData();
|
|
598
|
+
}, nextTimeout);
|
|
599
|
+
} // <-- refreshData end
|
|
600
|
+
|
|
601
|
+
/*=============================================
|
|
602
|
+
= functions to get data =
|
|
603
|
+
=============================================*/
|
|
604
|
+
|
|
605
|
+
/**
|
|
606
|
+
* @param {object} id - deviceID
|
|
607
|
+
* @param {object} i - each Device
|
|
608
|
+
*/
|
|
609
|
+
async getDeviceName(id, i) {
|
|
610
|
+
try {
|
|
611
|
+
//id = id.replace(/[\]\\[.*,;'"`<>\\\s?]/g, '-');
|
|
612
|
+
|
|
613
|
+
const currDeviceString = id.slice(0, id.lastIndexOf('.') + 1 - 1);
|
|
614
|
+
const shortCurrDeviceString = currDeviceString.slice(0, currDeviceString.lastIndexOf('.') + 1 - 1);
|
|
615
|
+
const shortshortCurrDeviceString = shortCurrDeviceString.slice(0, shortCurrDeviceString.lastIndexOf('.') + 1 - 1);
|
|
616
|
+
|
|
617
|
+
// Get device name
|
|
618
|
+
const deviceObject = await this.getForeignObjectAsync(currDeviceString);
|
|
619
|
+
const shortDeviceObject = await this.getForeignObjectAsync(shortCurrDeviceString);
|
|
620
|
+
const shortshortDeviceObject = await this.getForeignObjectAsync(shortshortCurrDeviceString);
|
|
621
|
+
let deviceName;
|
|
622
|
+
let folderName;
|
|
623
|
+
let deviceID;
|
|
624
|
+
|
|
625
|
+
switch (this.selAdapter[i].adapterID) {
|
|
626
|
+
case 'fullybrowser':
|
|
627
|
+
deviceName = `${await tools.getInitValue(this, currDeviceString + this.selAdapter[i].id)} ${await tools.getInitValue(this, currDeviceString + this.selAdapter[i].id2)}`;
|
|
628
|
+
break;
|
|
629
|
+
|
|
630
|
+
// Get ID with short currDeviceString from objectjson
|
|
631
|
+
case 'hueExt':
|
|
632
|
+
case 'hmrpc':
|
|
633
|
+
case 'matter':
|
|
634
|
+
case 'nukiExt':
|
|
635
|
+
case 'wled':
|
|
636
|
+
case 'mqttNuki':
|
|
637
|
+
case 'loqedSmartLock':
|
|
638
|
+
case 'viessmann':
|
|
639
|
+
case 'homekitController':
|
|
640
|
+
case 'ring':
|
|
641
|
+
if (shortDeviceObject && typeof shortDeviceObject === 'object' && shortDeviceObject.common) {
|
|
642
|
+
deviceName = shortDeviceObject.common.name;
|
|
643
|
+
}
|
|
644
|
+
break;
|
|
645
|
+
|
|
646
|
+
// Get ID with short short currDeviceString from objectjson (HMiP Devices)
|
|
647
|
+
case 'hmiP':
|
|
648
|
+
if (shortshortDeviceObject && typeof shortshortDeviceObject === 'object' && shortshortDeviceObject.common) {
|
|
649
|
+
deviceName = shortshortDeviceObject.common.name;
|
|
650
|
+
}
|
|
651
|
+
break;
|
|
652
|
+
|
|
653
|
+
// Get ID with short currDeviceString from datapoint
|
|
654
|
+
case 'mihomeVacuum':
|
|
655
|
+
case 'roomba':
|
|
656
|
+
folderName = shortCurrDeviceString.slice(shortCurrDeviceString.lastIndexOf('.') + 1);
|
|
657
|
+
deviceID = await tools.getInitValue(this, shortCurrDeviceString + this.selAdapter[i].id);
|
|
658
|
+
deviceName = `I${folderName} ${deviceID}`;
|
|
659
|
+
break;
|
|
660
|
+
|
|
661
|
+
//Get ID of foldername
|
|
662
|
+
case 'tado':
|
|
663
|
+
case 'wifilight':
|
|
664
|
+
case 'fullybrowserV3':
|
|
665
|
+
case 'sonoff':
|
|
666
|
+
deviceName = currDeviceString.slice(currDeviceString.lastIndexOf('.') + 1);
|
|
667
|
+
break;
|
|
668
|
+
|
|
669
|
+
// Format Device name
|
|
670
|
+
case 'sureflap':
|
|
671
|
+
if (deviceObject && typeof deviceObject === 'object' && deviceObject.common) {
|
|
672
|
+
deviceName = deviceObject.common.name
|
|
673
|
+
.replace(/'/g, '')
|
|
674
|
+
.replace(/\(\d+\)/g, '')
|
|
675
|
+
.trim()
|
|
676
|
+
.replace('Hub', 'Hub -')
|
|
677
|
+
.replace('Device', 'Device -');
|
|
678
|
+
}
|
|
679
|
+
break;
|
|
680
|
+
|
|
681
|
+
//Get ID of foldername
|
|
682
|
+
case 'yeelight':
|
|
683
|
+
deviceName = shortCurrDeviceString.slice(shortCurrDeviceString.lastIndexOf('.') + 1);
|
|
684
|
+
break;
|
|
685
|
+
|
|
686
|
+
// Get ID with main selektor from objectjson
|
|
687
|
+
default:
|
|
688
|
+
if (this.selAdapter[i].id !== 'none' || this.selAdapter[i].id !== undefined) {
|
|
689
|
+
deviceName = await tools.getInitValue(this, currDeviceString + this.selAdapter[i].id);
|
|
690
|
+
}
|
|
691
|
+
if (deviceName === null || deviceName === undefined) {
|
|
692
|
+
if (deviceObject && typeof deviceObject === 'object' && deviceObject.common) {
|
|
693
|
+
deviceName = deviceObject.common.name;
|
|
694
|
+
}
|
|
695
|
+
}
|
|
696
|
+
break;
|
|
697
|
+
}
|
|
698
|
+
return deviceName;
|
|
699
|
+
} catch (error) {
|
|
700
|
+
this.log.error(`[getDeviceName] - ${error}`);
|
|
701
|
+
}
|
|
702
|
+
}
|
|
703
|
+
|
|
704
|
+
/**
|
|
705
|
+
* calculate Signalstrength
|
|
706
|
+
*
|
|
707
|
+
* @param {object} deviceQualityState - State value
|
|
708
|
+
* @param {object} adapterID - adapter name
|
|
709
|
+
*/
|
|
710
|
+
async calculateSignalStrength(deviceQualityState, adapterID) {
|
|
711
|
+
let linkQuality;
|
|
712
|
+
let linkQualityRaw;
|
|
713
|
+
let mqttNukiValue;
|
|
714
|
+
|
|
715
|
+
if (deviceQualityState != null) {
|
|
716
|
+
const {val} = deviceQualityState;
|
|
717
|
+
|
|
718
|
+
if (typeof val === 'number') {
|
|
719
|
+
if (this.config.trueState) {
|
|
720
|
+
linkQuality = val;
|
|
721
|
+
} else {
|
|
722
|
+
switch (adapterID) {
|
|
723
|
+
case 'roomba':
|
|
724
|
+
case 'sonoff':
|
|
725
|
+
case 'smartgarden':
|
|
726
|
+
linkQuality = `${val}%`;
|
|
727
|
+
linkQualityRaw = val;
|
|
728
|
+
break;
|
|
729
|
+
case 'lupusec':
|
|
730
|
+
case 'fullybrowserV3':
|
|
731
|
+
linkQuality = val;
|
|
732
|
+
break;
|
|
733
|
+
default:
|
|
734
|
+
if (val <= -255) {
|
|
735
|
+
linkQuality = ' - ';
|
|
736
|
+
} else if (val < 0) {
|
|
737
|
+
linkQualityRaw = Math.min(Math.max(2 * (val + 100), 0), 100);
|
|
738
|
+
linkQuality = `${linkQualityRaw}%`;
|
|
739
|
+
} else if (val >= 0) {
|
|
740
|
+
linkQualityRaw = parseFloat(((100 / 255) * val).toFixed(0));
|
|
741
|
+
linkQuality = `${linkQualityRaw}%`;
|
|
742
|
+
}
|
|
743
|
+
break;
|
|
744
|
+
}
|
|
745
|
+
}
|
|
746
|
+
} else if (typeof val === 'string') {
|
|
747
|
+
switch (adapterID) {
|
|
748
|
+
case 'netatmo':
|
|
749
|
+
linkQuality = val;
|
|
750
|
+
break;
|
|
751
|
+
case 'nukiExt':
|
|
752
|
+
linkQuality = ' - ';
|
|
753
|
+
break;
|
|
754
|
+
case 'mqttNuki':
|
|
755
|
+
linkQuality = val;
|
|
756
|
+
mqttNukiValue = parseInt(linkQuality);
|
|
757
|
+
if (this.config.trueState) {
|
|
758
|
+
linkQuality = val;
|
|
759
|
+
} else if (mqttNukiValue < 0) {
|
|
760
|
+
linkQualityRaw = Math.min(Math.max(2 * (mqttNukiValue + 100), 0), 100);
|
|
761
|
+
linkQuality = `${linkQualityRaw}%`;
|
|
762
|
+
}
|
|
763
|
+
break;
|
|
764
|
+
}
|
|
765
|
+
}
|
|
766
|
+
} else {
|
|
767
|
+
linkQuality = ' - ';
|
|
768
|
+
}
|
|
769
|
+
return [linkQuality, linkQualityRaw];
|
|
770
|
+
}
|
|
771
|
+
|
|
772
|
+
/**
|
|
773
|
+
* get battery data
|
|
774
|
+
*
|
|
775
|
+
* @param {object} deviceBatteryState - State value
|
|
776
|
+
* @param {object} deviceLowBatState - State value
|
|
777
|
+
* @param {object} faultReportingState - State value
|
|
778
|
+
* @param {object} adapterID - adapter name
|
|
779
|
+
*/
|
|
780
|
+
async getBatteryData(deviceBatteryState, deviceLowBatState, faultReportingState, adapterID) {
|
|
781
|
+
let batteryHealth = '-';
|
|
782
|
+
let isBatteryDevice = false;
|
|
783
|
+
let batteryHealthRaw;
|
|
784
|
+
let batteryHealthUnitRaw;
|
|
785
|
+
|
|
786
|
+
switch (adapterID) {
|
|
787
|
+
case 'lupusec':
|
|
788
|
+
if (deviceBatteryState === undefined) {
|
|
789
|
+
if (deviceLowBatState === 1) {
|
|
790
|
+
batteryHealth = 'ok';
|
|
791
|
+
isBatteryDevice = true;
|
|
792
|
+
} else {
|
|
793
|
+
batteryHealth = 'low';
|
|
794
|
+
isBatteryDevice = true;
|
|
795
|
+
}
|
|
796
|
+
}
|
|
797
|
+
break;
|
|
798
|
+
case 'hmrpc':
|
|
799
|
+
if (deviceBatteryState === undefined) {
|
|
800
|
+
if (faultReportingState !== undefined && faultReportingState !== 6) {
|
|
801
|
+
batteryHealth = 'ok';
|
|
802
|
+
isBatteryDevice = true;
|
|
803
|
+
} else if (deviceLowBatState !== undefined && deviceLowBatState !== 1) {
|
|
804
|
+
batteryHealth = 'ok';
|
|
805
|
+
isBatteryDevice = true;
|
|
806
|
+
} else if (deviceLowBatState !== undefined) {
|
|
807
|
+
batteryHealth = 'low';
|
|
808
|
+
isBatteryDevice = true;
|
|
809
|
+
}
|
|
810
|
+
} else if (deviceBatteryState !== 0 && deviceBatteryState < 6) {
|
|
811
|
+
batteryHealth = `${deviceBatteryState}V`;
|
|
812
|
+
batteryHealthRaw = deviceBatteryState;
|
|
813
|
+
batteryHealthUnitRaw = 'V';
|
|
814
|
+
isBatteryDevice = true;
|
|
815
|
+
}
|
|
816
|
+
break;
|
|
817
|
+
case 'xsense':
|
|
818
|
+
if (deviceBatteryState === undefined) {
|
|
819
|
+
// do nothin brdge has no battery
|
|
820
|
+
isBatteryDevice = false;
|
|
821
|
+
} else if (deviceBatteryState >= 0) {
|
|
822
|
+
batteryHealthRaw = deviceBatteryState;
|
|
823
|
+
batteryHealthUnitRaw = '';
|
|
824
|
+
isBatteryDevice = true;
|
|
825
|
+
switch (batteryHealthRaw) {
|
|
826
|
+
case 1:
|
|
827
|
+
batteryHealth = 'low';
|
|
828
|
+
break;
|
|
829
|
+
case 2:
|
|
830
|
+
batteryHealth = 'medium';
|
|
831
|
+
break;
|
|
832
|
+
case 3:
|
|
833
|
+
batteryHealth = 'ok';
|
|
834
|
+
break;
|
|
835
|
+
default:
|
|
836
|
+
batteryHealth = 'error';
|
|
837
|
+
}
|
|
838
|
+
}
|
|
839
|
+
break;
|
|
840
|
+
default:
|
|
841
|
+
if (deviceBatteryState === undefined) {
|
|
842
|
+
if (deviceLowBatState !== undefined) {
|
|
843
|
+
// Explicit OK states: false, 'NORMAL', 0 (some adapters use 0=ok)
|
|
844
|
+
if (
|
|
845
|
+
deviceLowBatState === false ||
|
|
846
|
+
deviceLowBatState === 'NORMAL' ||
|
|
847
|
+
deviceLowBatState === 0
|
|
848
|
+
) {
|
|
849
|
+
batteryHealth = 'ok';
|
|
850
|
+
isBatteryDevice = true;
|
|
851
|
+
} else {
|
|
852
|
+
// true, 1, any other string != 'NORMAL' → low
|
|
853
|
+
batteryHealth = 'low';
|
|
854
|
+
isBatteryDevice = true;
|
|
855
|
+
}
|
|
856
|
+
}
|
|
857
|
+
} else {
|
|
858
|
+
if (typeof deviceBatteryState === 'string') {
|
|
859
|
+
if (deviceBatteryState === 'high' || deviceBatteryState === 'medium') {
|
|
860
|
+
batteryHealth = 'ok';
|
|
861
|
+
isBatteryDevice = true;
|
|
862
|
+
} else if (deviceBatteryState === 'low') {
|
|
863
|
+
batteryHealth = 'low';
|
|
864
|
+
isBatteryDevice = true;
|
|
865
|
+
}
|
|
866
|
+
} else {
|
|
867
|
+
batteryHealth = `${deviceBatteryState}%`;
|
|
868
|
+
batteryHealthRaw = deviceBatteryState;
|
|
869
|
+
batteryHealthUnitRaw = '%';
|
|
870
|
+
isBatteryDevice = true;
|
|
871
|
+
}
|
|
872
|
+
}
|
|
873
|
+
break;
|
|
874
|
+
}
|
|
875
|
+
|
|
876
|
+
return [batteryHealth, isBatteryDevice, batteryHealthRaw, batteryHealthUnitRaw];
|
|
877
|
+
}
|
|
878
|
+
|
|
879
|
+
/**
|
|
880
|
+
* set low bat indicator
|
|
881
|
+
*
|
|
882
|
+
* @param {object} deviceBatteryState
|
|
883
|
+
* @param {object} deviceLowBatState
|
|
884
|
+
* @param {object} faultReportState
|
|
885
|
+
* @param {object} adapterID
|
|
886
|
+
*/
|
|
887
|
+
|
|
888
|
+
async setLowbatIndicator(deviceBatteryState, deviceLowBatState, faultReportState, adapterID) {
|
|
889
|
+
let lowBatIndicator = false;
|
|
890
|
+
|
|
891
|
+
if (deviceLowBatState !== undefined || faultReportState !== undefined) {
|
|
892
|
+
switch (adapterID) {
|
|
893
|
+
case 'hmrpc':
|
|
894
|
+
if (deviceLowBatState === 1 || deviceLowBatState === true || faultReportState === 6) {
|
|
895
|
+
lowBatIndicator = true;
|
|
896
|
+
}
|
|
897
|
+
break;
|
|
898
|
+
default:
|
|
899
|
+
if (typeof deviceLowBatState === 'boolean' && deviceLowBatState === true) {
|
|
900
|
+
// true = low bat
|
|
901
|
+
lowBatIndicator = true;
|
|
902
|
+
} else if (typeof deviceLowBatState === 'string' && deviceLowBatState !== 'NORMAL') {
|
|
903
|
+
// any string other than 'NORMAL' = low bat
|
|
904
|
+
lowBatIndicator = true;
|
|
905
|
+
} else if (typeof deviceLowBatState === 'number' && deviceLowBatState === 1) {
|
|
906
|
+
// 1 = low bat (0 = ok), consistent with getBatteryData
|
|
907
|
+
lowBatIndicator = true;
|
|
908
|
+
}
|
|
909
|
+
}
|
|
910
|
+
} else if (typeof deviceBatteryState === 'number' && deviceBatteryState < this.config.minWarnBatterie) {
|
|
911
|
+
lowBatIndicator = true;
|
|
912
|
+
} else if (typeof deviceBatteryState === 'string' && deviceBatteryState === 'low') {
|
|
913
|
+
lowBatIndicator = true;
|
|
914
|
+
}
|
|
915
|
+
|
|
916
|
+
return lowBatIndicator;
|
|
917
|
+
}
|
|
918
|
+
|
|
919
|
+
/**
|
|
920
|
+
* get Last Contact
|
|
921
|
+
*
|
|
922
|
+
* @param {object} selector - Selector
|
|
923
|
+
*/
|
|
924
|
+
async getLastContact(selector) {
|
|
925
|
+
const lastContact = tools.getTimestamp(selector);
|
|
926
|
+
|
|
927
|
+
let lastContactString;
|
|
928
|
+
|
|
929
|
+
if (lastContact >= 3600) {
|
|
930
|
+
lastContactString = `${(lastContact / 3600).toFixed(1)} ${translations.hours[this.config.userSelectedLanguage]}`;
|
|
931
|
+
} else if (lastContact >= 60) {
|
|
932
|
+
lastContactString = `${Math.round(lastContact / 60)} ${translations.minits[this.config.userSelectedLanguage]}`;
|
|
933
|
+
} else {
|
|
934
|
+
lastContactString = `${Math.round(lastContact)} ${translations.secs[this.config.userSelectedLanguage]}`;
|
|
935
|
+
}
|
|
936
|
+
|
|
937
|
+
return lastContactString;
|
|
938
|
+
}
|
|
939
|
+
|
|
940
|
+
async getOnlineState(timeSelector, adapterID, treeDP, linkQuality, deviceUnreachState, deviceStateSelectorHMRPC, rssiPeerSelectorHMRPC) {
|
|
941
|
+
let lastContactString;
|
|
942
|
+
let lastContact;
|
|
943
|
+
let deviceState = 'Online';
|
|
944
|
+
let linkQualitySet = linkQuality ?? '0%';
|
|
945
|
+
|
|
946
|
+
try {
|
|
947
|
+
const deviceTimeSelector = await this.getForeignStateAsync(timeSelector);
|
|
948
|
+
const deviceUnreachSelector = await this.getForeignStateAsync(treeDP);
|
|
949
|
+
|
|
950
|
+
const lastDeviceUnreachStateChange = deviceUnreachSelector?.lc != null ? tools.getTimestamp(deviceUnreachSelector.lc) : tools.getTimestamp(deviceTimeSelector?.ts ?? Date.now());
|
|
951
|
+
|
|
952
|
+
// ignore disabled device from zigbee2MQTT
|
|
953
|
+
if (adapterID === 'zigbee2MQTT') {
|
|
954
|
+
const is_device_disabled = await tools.isDisabledDevice(this, treeDP.substring(0, treeDP.lastIndexOf('.')));
|
|
955
|
+
|
|
956
|
+
if (is_device_disabled) {
|
|
957
|
+
return [null, 'disabled', ' - '];
|
|
958
|
+
}
|
|
959
|
+
}
|
|
960
|
+
|
|
961
|
+
if (adapterID === 'hmrpc') {
|
|
962
|
+
const deviceState = await this.getForeignStateAsync(deviceStateSelectorHMRPC);
|
|
963
|
+
const rssiPeer = await this.getForeignStateAsync(rssiPeerSelectorHMRPC);
|
|
964
|
+
|
|
965
|
+
if (linkQuality !== ' - ' && deviceTimeSelector) {
|
|
966
|
+
const ts = deviceUnreachState === 1 ? deviceTimeSelector.lc : deviceTimeSelector.ts;
|
|
967
|
+
lastContactString = await this.getLastContact(ts);
|
|
968
|
+
} else if (deviceState) {
|
|
969
|
+
lastContactString = await this.getLastContact(deviceState.ts);
|
|
970
|
+
} else if (rssiPeer) {
|
|
971
|
+
lastContactString = await this.getLastContact(rssiPeer.ts);
|
|
972
|
+
}
|
|
973
|
+
} else if (deviceTimeSelector) {
|
|
974
|
+
const ts = !deviceUnreachState ? deviceTimeSelector.lc : deviceTimeSelector.ts;
|
|
975
|
+
lastContactString = await this.getLastContact(ts);
|
|
976
|
+
}
|
|
977
|
+
|
|
978
|
+
if (deviceTimeSelector) {
|
|
979
|
+
lastContact = tools.getTimestamp(deviceTimeSelector.ts);
|
|
980
|
+
}
|
|
981
|
+
|
|
982
|
+
const gefundenerAdapter = Object.values(adapterArray).find((adapter) => adapter.adapterID === adapterID);
|
|
983
|
+
if (!gefundenerAdapter) {
|
|
984
|
+
this.log.warn(`[getOnlineState] - adapter not found in adapterArray for adapterID: ${adapterID}`);
|
|
985
|
+
return [lastContactString ?? ' - ', deviceState, linkQualitySet];
|
|
986
|
+
}
|
|
987
|
+
const device = Object.values(this.config.tableDevices).find((adapter) => adapter.adapterKey === gefundenerAdapter.adapterKey);
|
|
988
|
+
if (!device) {
|
|
989
|
+
this.log.warn(`[getOnlineState] - device config not found for adapterKey: ${gefundenerAdapter.adapterKey}`);
|
|
990
|
+
return [lastContactString ?? ' - ', deviceState, linkQualitySet];
|
|
991
|
+
}
|
|
992
|
+
const maxSecondDevicesOffline = device.maxSecondDevicesOffline;
|
|
993
|
+
|
|
994
|
+
switch (adapterID) {
|
|
995
|
+
case 'hmrpc': {
|
|
996
|
+
if (maxSecondDevicesOffline <= 0) {
|
|
997
|
+
if (deviceUnreachState === 1) {
|
|
998
|
+
deviceState = 'Offline'; //set online state to offline
|
|
999
|
+
if (linkQuality !== ' - ') {
|
|
1000
|
+
linkQualitySet = '0%';
|
|
1001
|
+
} // set linkQuality to nothing
|
|
1002
|
+
}
|
|
1003
|
+
} else if (lastDeviceUnreachStateChange > maxSecondDevicesOffline && deviceUnreachState === 1) {
|
|
1004
|
+
deviceState = 'Offline'; //set online state to offline
|
|
1005
|
+
if (linkQuality !== ' - ') {
|
|
1006
|
+
linkQualitySet = '0%';
|
|
1007
|
+
} // set linkQuality to nothing
|
|
1008
|
+
}
|
|
1009
|
+
break;
|
|
1010
|
+
}
|
|
1011
|
+
case 'proxmox': {
|
|
1012
|
+
if (maxSecondDevicesOffline <= 0) {
|
|
1013
|
+
if (deviceUnreachState !== 'running' && deviceUnreachState !== 'online') {
|
|
1014
|
+
deviceState = 'Offline'; //set online state to offline
|
|
1015
|
+
if (linkQuality !== ' - ') {
|
|
1016
|
+
linkQualitySet = '0%';
|
|
1017
|
+
} // set linkQuality to nothing
|
|
1018
|
+
}
|
|
1019
|
+
} else if (lastDeviceUnreachStateChange > maxSecondDevicesOffline && deviceUnreachState !== 'running' && deviceUnreachState !== 'online') {
|
|
1020
|
+
deviceState = 'Offline'; //set online state to offline
|
|
1021
|
+
if (linkQuality !== ' - ') {
|
|
1022
|
+
linkQualitySet = '0%';
|
|
1023
|
+
} // set linkQuality to nothing
|
|
1024
|
+
}
|
|
1025
|
+
break;
|
|
1026
|
+
}
|
|
1027
|
+
case 'hmiP':
|
|
1028
|
+
case 'maxcube': {
|
|
1029
|
+
if (maxSecondDevicesOffline <= 0) {
|
|
1030
|
+
if (deviceUnreachState) {
|
|
1031
|
+
deviceState = 'Offline'; //set online state to offline
|
|
1032
|
+
if (linkQuality !== ' - ') {
|
|
1033
|
+
linkQualitySet = '0%';
|
|
1034
|
+
} // set linkQuality to nothing
|
|
1035
|
+
}
|
|
1036
|
+
} else if (lastDeviceUnreachStateChange > maxSecondDevicesOffline && deviceUnreachState) {
|
|
1037
|
+
deviceState = 'Offline'; //set online state to offline
|
|
1038
|
+
if (linkQuality !== ' - ') {
|
|
1039
|
+
linkQualitySet = '0%';
|
|
1040
|
+
} // set linkQuality to nothing
|
|
1041
|
+
}
|
|
1042
|
+
break;
|
|
1043
|
+
}
|
|
1044
|
+
case 'apcups':
|
|
1045
|
+
case 'hue':
|
|
1046
|
+
case 'hueExt':
|
|
1047
|
+
case 'matter':
|
|
1048
|
+
case 'ping':
|
|
1049
|
+
case 'deconz':
|
|
1050
|
+
case 'shelly':
|
|
1051
|
+
case 'sonoff':
|
|
1052
|
+
case 'tradfri':
|
|
1053
|
+
case 'unifi':
|
|
1054
|
+
case 'zigbee':
|
|
1055
|
+
case 'zigbee2MQTT': {
|
|
1056
|
+
if (maxSecondDevicesOffline <= 0) {
|
|
1057
|
+
if (!deviceUnreachState) {
|
|
1058
|
+
deviceState = 'Offline'; //set online state to offline
|
|
1059
|
+
if (linkQuality !== ' - ') {
|
|
1060
|
+
linkQualitySet = '0%';
|
|
1061
|
+
} // set linkQuality to nothing
|
|
1062
|
+
}
|
|
1063
|
+
} else if (!deviceUnreachState && lastDeviceUnreachStateChange > maxSecondDevicesOffline) {
|
|
1064
|
+
deviceState = 'Offline'; //set online state to offline
|
|
1065
|
+
if (linkQuality !== ' - ') {
|
|
1066
|
+
linkQualitySet = '0%';
|
|
1067
|
+
} // set linkQuality to nothing
|
|
1068
|
+
}
|
|
1069
|
+
break;
|
|
1070
|
+
}
|
|
1071
|
+
case 'mqttClientZigbee2Mqtt': {
|
|
1072
|
+
if (maxSecondDevicesOffline <= 0) {
|
|
1073
|
+
if (deviceUnreachState !== 'online') {
|
|
1074
|
+
deviceState = 'Offline'; //set online state to offline
|
|
1075
|
+
if (linkQuality !== ' - ') {
|
|
1076
|
+
linkQualitySet = '0%';
|
|
1077
|
+
} // set linkQuality to nothing
|
|
1078
|
+
}
|
|
1079
|
+
} else if (deviceUnreachState !== 'online' && lastDeviceUnreachStateChange > maxSecondDevicesOffline) {
|
|
1080
|
+
deviceState = 'Offline'; //set online state to offline
|
|
1081
|
+
if (linkQuality !== ' - ') {
|
|
1082
|
+
linkQualitySet = '0%';
|
|
1083
|
+
}
|
|
1084
|
+
}
|
|
1085
|
+
break;
|
|
1086
|
+
}
|
|
1087
|
+
case 'mihome': {
|
|
1088
|
+
const offlineByTime = maxSecondDevicesOffline <= 0 || (lastContact && lastContact > maxSecondDevicesOffline);
|
|
1089
|
+
const offlineByState = deviceUnreachState !== undefined ? !deviceUnreachState && offlineByTime : offlineByTime;
|
|
1090
|
+
|
|
1091
|
+
if (offlineByState) {
|
|
1092
|
+
deviceState = 'Offline';
|
|
1093
|
+
if (linkQuality !== ' - ') {
|
|
1094
|
+
linkQualitySet = '0%';
|
|
1095
|
+
}
|
|
1096
|
+
}
|
|
1097
|
+
break;
|
|
1098
|
+
}
|
|
1099
|
+
case 'smartgarden': {
|
|
1100
|
+
if (maxSecondDevicesOffline <= 0) {
|
|
1101
|
+
if (deviceUnreachState === 'OFFLINE') {
|
|
1102
|
+
deviceState = 'Offline'; //set online state to offline
|
|
1103
|
+
if (linkQuality !== ' - ') {
|
|
1104
|
+
linkQualitySet = '0%';
|
|
1105
|
+
} // set linkQuality to nothing
|
|
1106
|
+
}
|
|
1107
|
+
} else if (deviceUnreachState === 'OFFLINE' && lastDeviceUnreachStateChange > maxSecondDevicesOffline) {
|
|
1108
|
+
deviceState = 'Offline'; //set online state to offline
|
|
1109
|
+
if (linkQuality !== ' - ') {
|
|
1110
|
+
linkQualitySet = '0%';
|
|
1111
|
+
} // set linkQuality to nothing
|
|
1112
|
+
}
|
|
1113
|
+
break;
|
|
1114
|
+
}
|
|
1115
|
+
default: {
|
|
1116
|
+
// Gerät gilt als offline, wenn es unerreichbar ist und keine Wartezeit definiert ist, oder wenn der letzte Kontakt zu lange her ist als Wartezeit
|
|
1117
|
+
let shouldBeOffline = false;
|
|
1118
|
+
|
|
1119
|
+
if (maxSecondDevicesOffline <= 0) {
|
|
1120
|
+
if (!deviceUnreachState) {
|
|
1121
|
+
shouldBeOffline = true;
|
|
1122
|
+
}
|
|
1123
|
+
} else if (lastContact && lastContact > maxSecondDevicesOffline) {
|
|
1124
|
+
shouldBeOffline = true;
|
|
1125
|
+
}
|
|
1126
|
+
|
|
1127
|
+
if (shouldBeOffline) {
|
|
1128
|
+
deviceState = 'Offline'; // Gerät auf offline setzen
|
|
1129
|
+
if (linkQuality !== ' - ') {
|
|
1130
|
+
linkQualitySet = '0%';
|
|
1131
|
+
}
|
|
1132
|
+
}
|
|
1133
|
+
}
|
|
1134
|
+
}
|
|
1135
|
+
|
|
1136
|
+
return [lastContactString, deviceState, linkQualitySet];
|
|
1137
|
+
} catch (error) {
|
|
1138
|
+
this.log.error(`[getLastContact] - ${error}`);
|
|
1139
|
+
}
|
|
1140
|
+
}
|
|
1141
|
+
|
|
1142
|
+
/**
|
|
1143
|
+
* @param {any} adapterID
|
|
1144
|
+
* @param {string | number | boolean | null} deviceUpdateSelector
|
|
1145
|
+
*/
|
|
1146
|
+
async checkDeviceUpdate(adapterID, deviceUpdateSelector) {
|
|
1147
|
+
let isUpgradable = false;
|
|
1148
|
+
|
|
1149
|
+
switch (adapterID) {
|
|
1150
|
+
case 'hmiP':
|
|
1151
|
+
isUpgradable = deviceUpdateSelector === 'UPDATE_AVAILABLE';
|
|
1152
|
+
break;
|
|
1153
|
+
|
|
1154
|
+
case 'ring':
|
|
1155
|
+
isUpgradable = deviceUpdateSelector !== 'Up to Date';
|
|
1156
|
+
break;
|
|
1157
|
+
|
|
1158
|
+
default:
|
|
1159
|
+
if (typeof deviceUpdateSelector === 'boolean') {
|
|
1160
|
+
isUpgradable = deviceUpdateSelector;
|
|
1161
|
+
}
|
|
1162
|
+
break;
|
|
1163
|
+
}
|
|
1164
|
+
|
|
1165
|
+
return isUpgradable;
|
|
1166
|
+
}
|
|
1167
|
+
|
|
1168
|
+
/**
|
|
1169
|
+
* fill the lists for user
|
|
1170
|
+
*
|
|
1171
|
+
* @param {object} device
|
|
1172
|
+
*/
|
|
1173
|
+
async theLists(device) {
|
|
1174
|
+
// Raw List with all devices for user
|
|
1175
|
+
if (device.Status !== 'disabled') {
|
|
1176
|
+
// Deduplication: some adapters (e.g. hmrpc with multiple channels, hue-extended with
|
|
1177
|
+
// devices appearing under both lights and sensors) create multiple Map entries for the
|
|
1178
|
+
// same physical device. Use Path as unique key to prevent duplicate list entries.
|
|
1179
|
+
const lang = this.config.userSelectedLanguage;
|
|
1180
|
+
const alreadyInUserRaw = this.listAllDevicesUserRaw.some((d) => d.Device === device.Device && d.Adapter === device.Adapter);
|
|
1181
|
+
if (!alreadyInUserRaw) {
|
|
1182
|
+
this.listAllDevicesUserRaw.push({
|
|
1183
|
+
Device: device.Device,
|
|
1184
|
+
Adapter: device.Adapter,
|
|
1185
|
+
Instance: device.instance,
|
|
1186
|
+
'Instance connected': device.instanceDeviceConnected,
|
|
1187
|
+
isBatteryDevice: device.isBatteryDevice,
|
|
1188
|
+
Battery: device.Battery,
|
|
1189
|
+
BatteryRaw: device.BatteryRaw,
|
|
1190
|
+
BatteryUnitRaw: device.BatteryUnitRaw,
|
|
1191
|
+
isLowBat: device.LowBat,
|
|
1192
|
+
'Signal strength': device.SignalStrength,
|
|
1193
|
+
'Signal strength Raw': device.SignalStrengthRaw,
|
|
1194
|
+
'Last contact': device.LastContact,
|
|
1195
|
+
'Update Available': device.Upgradable,
|
|
1196
|
+
Status: device.Status,
|
|
1197
|
+
});
|
|
1198
|
+
}
|
|
1199
|
+
|
|
1200
|
+
// List with all devices
|
|
1201
|
+
const alreadyInAll = this.listAllDevices.some(
|
|
1202
|
+
(d) => d[translations.Device[lang]] === device.Device && d[translations.Adapter[lang]] === device.Adapter,
|
|
1203
|
+
);
|
|
1204
|
+
if (!alreadyInAll) {
|
|
1205
|
+
this.listAllDevices.push({
|
|
1206
|
+
[translations.Device[lang]]: device.Device,
|
|
1207
|
+
[translations.Adapter[lang]]: device.Adapter,
|
|
1208
|
+
[translations.Battery[lang]]: device.Battery,
|
|
1209
|
+
[translations.Signal_strength[lang]]: device.SignalStrength,
|
|
1210
|
+
[translations.Last_Contact[lang]]: device.LastContact,
|
|
1211
|
+
[translations.Status[lang]]: device.Status,
|
|
1212
|
+
});
|
|
1213
|
+
}
|
|
1214
|
+
|
|
1215
|
+
// LinkQuality lists
|
|
1216
|
+
if (device.SignalStrength !== ' - ') {
|
|
1217
|
+
const alreadyInLQ = this.linkQualityDevices.some(
|
|
1218
|
+
(d) => d[translations.Device[lang]] === device.Device && d[translations.Adapter[lang]] === device.Adapter,
|
|
1219
|
+
);
|
|
1220
|
+
if (!alreadyInLQ) {
|
|
1221
|
+
this.linkQualityDevices.push({
|
|
1222
|
+
[translations.Device[lang]]: device.Device,
|
|
1223
|
+
[translations.Adapter[lang]]: device.Adapter,
|
|
1224
|
+
[translations.Signal_strength[lang]]: device.SignalStrength,
|
|
1225
|
+
});
|
|
1226
|
+
}
|
|
1227
|
+
}
|
|
1228
|
+
|
|
1229
|
+
// Battery lists
|
|
1230
|
+
if (device.isBatteryDevice) {
|
|
1231
|
+
const alreadyInBat = this.batteryPowered.some(
|
|
1232
|
+
(d) => d[translations.Device[lang]] === device.Device && d[translations.Adapter[lang]] === device.Adapter,
|
|
1233
|
+
);
|
|
1234
|
+
if (!alreadyInBat) {
|
|
1235
|
+
this.batteryPowered.push({
|
|
1236
|
+
[translations.Device[lang]]: device.Device,
|
|
1237
|
+
[translations.Adapter[lang]]: device.Adapter,
|
|
1238
|
+
[translations.Battery[lang]]: device.Battery,
|
|
1239
|
+
[translations.Status[lang]]: device.Status,
|
|
1240
|
+
});
|
|
1241
|
+
}
|
|
1242
|
+
}
|
|
1243
|
+
|
|
1244
|
+
// Low Bat lists
|
|
1245
|
+
if (device.LowBat && device.Status !== 'Offline') {
|
|
1246
|
+
const alreadyInLowBat = this.batteryLowPowered.some(
|
|
1247
|
+
(d) => d[translations.Device[lang]] === device.Device && d[translations.Adapter[lang]] === device.Adapter,
|
|
1248
|
+
);
|
|
1249
|
+
if (!alreadyInLowBat) {
|
|
1250
|
+
this.batteryLowPowered.push({
|
|
1251
|
+
[translations.Device[lang]]: device.Device,
|
|
1252
|
+
[translations.Adapter[lang]]: device.Adapter,
|
|
1253
|
+
[translations.Battery[lang]]: device.Battery,
|
|
1254
|
+
});
|
|
1255
|
+
}
|
|
1256
|
+
}
|
|
1257
|
+
|
|
1258
|
+
// Offline List
|
|
1259
|
+
if (device.Status === 'Offline') {
|
|
1260
|
+
const alreadyOffline = this.offlineDevices.some(
|
|
1261
|
+
(d) => d[translations.Device[lang]] === device.Device && d[translations.Adapter[lang]] === device.Adapter,
|
|
1262
|
+
);
|
|
1263
|
+
if (!alreadyOffline) {
|
|
1264
|
+
this.offlineDevices.push({
|
|
1265
|
+
[translations.Device[lang]]: device.Device,
|
|
1266
|
+
[translations.Adapter[lang]]: device.Adapter,
|
|
1267
|
+
[translations.Last_Contact[lang]]: device.LastContact,
|
|
1268
|
+
});
|
|
1269
|
+
}
|
|
1270
|
+
}
|
|
1271
|
+
|
|
1272
|
+
// Device update List
|
|
1273
|
+
if (device.Upgradable === true || device.Upgradable === 1) {
|
|
1274
|
+
const alreadyUpgradable = this.upgradableList.some(
|
|
1275
|
+
(d) => d[translations.Device[lang]] === device.Device && d[translations.Adapter[lang]] === device.Adapter,
|
|
1276
|
+
);
|
|
1277
|
+
if (!alreadyUpgradable) {
|
|
1278
|
+
this.upgradableList.push({
|
|
1279
|
+
[translations.Device[lang]]: device.Device,
|
|
1280
|
+
[translations.Adapter[lang]]: device.Adapter,
|
|
1281
|
+
});
|
|
1282
|
+
}
|
|
1283
|
+
}
|
|
1284
|
+
}
|
|
1285
|
+
}
|
|
1286
|
+
|
|
1287
|
+
/**
|
|
1288
|
+
* @param {string | string[]} id
|
|
1289
|
+
* @param {ioBroker.State} state
|
|
1290
|
+
*/
|
|
1291
|
+
async renewDeviceData(id, state) {
|
|
1292
|
+
let batteryData;
|
|
1293
|
+
let signalData;
|
|
1294
|
+
let oldLowBatState;
|
|
1295
|
+
let contactData;
|
|
1296
|
+
let oldStatus;
|
|
1297
|
+
let isLowBatValue;
|
|
1298
|
+
let listDirty = false;
|
|
1299
|
+
|
|
1300
|
+
const deviceID = id.slice(0, id.lastIndexOf('.') + 1 - 1);
|
|
1301
|
+
const deviceData = this.listAllDevicesRaw.get(deviceID);
|
|
1302
|
+
|
|
1303
|
+
this.log.debug(`[renewDeviceData] - ${id}`);
|
|
1304
|
+
|
|
1305
|
+
if (deviceData) {
|
|
1306
|
+
const gefundenerAdapter = Object.values(adapterArray).find((adapter) => adapter.adapterID === deviceData.adapterID);
|
|
1307
|
+
if (!gefundenerAdapter) {
|
|
1308
|
+
this.log.warn(`[renewDeviceData] - adapter not found for adapterID: ${deviceData.adapterID}`);
|
|
1309
|
+
return;
|
|
1310
|
+
}
|
|
1311
|
+
const silentEnabled = Object.values(this.config.tableDevices).find((adapter) => adapter.adapterKey === gefundenerAdapter.adapterKey);
|
|
1312
|
+
if (!silentEnabled) {
|
|
1313
|
+
this.log.warn(`[renewDeviceData] - device config not found for adapterKey: ${gefundenerAdapter.adapterKey}`);
|
|
1314
|
+
return;
|
|
1315
|
+
}
|
|
1316
|
+
|
|
1317
|
+
// On statechange update available datapoint
|
|
1318
|
+
switch (id) {
|
|
1319
|
+
// device connection
|
|
1320
|
+
case deviceData.instanceDeviceConnectionDP:
|
|
1321
|
+
if (state.val !== deviceData.instanceDeviceConnected) {
|
|
1322
|
+
deviceData.instanceDeviceConnected = state.val;
|
|
1323
|
+
}
|
|
1324
|
+
break;
|
|
1325
|
+
|
|
1326
|
+
// device updates
|
|
1327
|
+
case deviceData.UpdateDP:
|
|
1328
|
+
if (state.val !== deviceData.Upgradable) {
|
|
1329
|
+
deviceData.Upgradable = await this.checkDeviceUpdate(deviceData.adapterID, state.val);
|
|
1330
|
+
listDirty = true;
|
|
1331
|
+
if (deviceData.Upgradable === true) {
|
|
1332
|
+
if (this.config.checkSendDeviceUpgrade && !this.blacklistNotify.includes(deviceData.Path)) {
|
|
1333
|
+
await this.sendStateNotifications('Devices', 'updateDevice', deviceID, silentEnabled.telegramSilent);
|
|
1334
|
+
}
|
|
1335
|
+
}
|
|
1336
|
+
}
|
|
1337
|
+
break;
|
|
1338
|
+
|
|
1339
|
+
// device signal – kein listDirty, SignalStrength ändert sich ständig
|
|
1340
|
+
case deviceData.SignalStrengthDP:
|
|
1341
|
+
signalData = await this.calculateSignalStrength(state, deviceData.adapterID);
|
|
1342
|
+
deviceData.SignalStrength = signalData[0];
|
|
1343
|
+
break;
|
|
1344
|
+
|
|
1345
|
+
// device battery
|
|
1346
|
+
case deviceData.batteryDP:
|
|
1347
|
+
if (deviceData.isBatteryDevice) {
|
|
1348
|
+
oldLowBatState = deviceData.LowBat;
|
|
1349
|
+
if (state.val === 0 && deviceData.BatteryRaw >= 5) {
|
|
1350
|
+
break;
|
|
1351
|
+
}
|
|
1352
|
+
batteryData = await this.getBatteryData(state.val, oldLowBatState, deviceData.faultReport, deviceData.adapterID);
|
|
1353
|
+
deviceData.Battery = batteryData[0];
|
|
1354
|
+
deviceData.BatteryRaw = batteryData[2];
|
|
1355
|
+
deviceData.BatteryUnitRaw = batteryData[3];
|
|
1356
|
+
if (deviceData.LowBatDP !== 'none') {
|
|
1357
|
+
isLowBatValue = await tools.getInitValue(this, deviceData.LowBatDP);
|
|
1358
|
+
} else {
|
|
1359
|
+
isLowBatValue = undefined;
|
|
1360
|
+
}
|
|
1361
|
+
deviceData.LowBat = await this.setLowbatIndicator(state.val, isLowBatValue, deviceData.faultReport, deviceData.adapterID);
|
|
1362
|
+
listDirty = true;
|
|
1363
|
+
if (deviceData.LowBat && oldLowBatState !== deviceData.LowBat) {
|
|
1364
|
+
if (this.config.checkSendBatteryMsg && !this.blacklistNotify.includes(deviceData.Path)) {
|
|
1365
|
+
await this.sendStateNotifications('Devices', 'lowBatDevice', deviceID, silentEnabled.telegramSilent);
|
|
1366
|
+
}
|
|
1367
|
+
}
|
|
1368
|
+
}
|
|
1369
|
+
break;
|
|
1370
|
+
|
|
1371
|
+
// device low bat
|
|
1372
|
+
case deviceData.LowBatDP:
|
|
1373
|
+
if (deviceData.isBatteryDevice) {
|
|
1374
|
+
oldLowBatState = deviceData.LowBat;
|
|
1375
|
+
batteryData = await this.getBatteryData(deviceData.BatteryRaw, state.val, deviceData.faultReport, deviceData.adapterID);
|
|
1376
|
+
deviceData.Battery = batteryData[0];
|
|
1377
|
+
deviceData.BatteryRaw = batteryData[2];
|
|
1378
|
+
deviceData.BatteryUnitRaw = batteryData[3];
|
|
1379
|
+
deviceData.LowBat = await this.setLowbatIndicator(deviceData.BatteryRaw, state.val, deviceData.faultReport, deviceData.adapterID);
|
|
1380
|
+
listDirty = true;
|
|
1381
|
+
if (deviceData.LowBat && oldLowBatState !== deviceData.LowBat) {
|
|
1382
|
+
if (this.config.checkSendBatteryMsg && !this.blacklistNotify.includes(deviceData.Path)) {
|
|
1383
|
+
await this.sendStateNotifications('Devices', 'lowBatDevice', deviceID, silentEnabled.telegramSilent);
|
|
1384
|
+
}
|
|
1385
|
+
}
|
|
1386
|
+
}
|
|
1387
|
+
break;
|
|
1388
|
+
|
|
1389
|
+
//device error / fault reports
|
|
1390
|
+
case deviceData.faultReportDP:
|
|
1391
|
+
if (deviceData.isBatteryDevice) {
|
|
1392
|
+
oldLowBatState = deviceData.LowBat;
|
|
1393
|
+
batteryData = await this.getBatteryData(deviceData.BatteryRaw, oldLowBatState, state.val, deviceData.adapterID);
|
|
1394
|
+
deviceData.Battery = batteryData[0];
|
|
1395
|
+
deviceData.BatteryRaw = batteryData[2];
|
|
1396
|
+
deviceData.BatteryUnitRaw = batteryData[3];
|
|
1397
|
+
deviceData.LowBat = await this.setLowbatIndicator(deviceData.BatteryRaw, undefined, state.val, deviceData.adapterID);
|
|
1398
|
+
listDirty = true;
|
|
1399
|
+
if (deviceData.LowBat && oldLowBatState !== deviceData.LowBat) {
|
|
1400
|
+
if (this.config.checkSendBatteryMsg && !this.blacklistNotify.includes(deviceData.Path)) {
|
|
1401
|
+
await this.sendStateNotifications('Devices', 'lowBatDevice', deviceID, silentEnabled.telegramSilent);
|
|
1402
|
+
}
|
|
1403
|
+
}
|
|
1404
|
+
}
|
|
1405
|
+
break;
|
|
1406
|
+
|
|
1407
|
+
// device unreach
|
|
1408
|
+
case deviceData.UnreachDP:
|
|
1409
|
+
if (deviceData.instanceDeviceConnected !== undefined) {
|
|
1410
|
+
if (deviceData.UnreachState !== state.val) {
|
|
1411
|
+
oldStatus = deviceData.Status;
|
|
1412
|
+
deviceData.UnreachState = state.val;
|
|
1413
|
+
|
|
1414
|
+
contactData = await this.getOnlineState(
|
|
1415
|
+
deviceData.timeSelector,
|
|
1416
|
+
deviceData.adapterID,
|
|
1417
|
+
deviceData.UnreachDP,
|
|
1418
|
+
deviceData.SignalStrength,
|
|
1419
|
+
deviceData.UnreachState,
|
|
1420
|
+
deviceData.deviceStateSelectorHMRPC,
|
|
1421
|
+
deviceData.rssiPeerSelectorHMRPC,
|
|
1422
|
+
);
|
|
1423
|
+
|
|
1424
|
+
if (contactData !== undefined && contactData !== null) {
|
|
1425
|
+
deviceData.LastContact = contactData[0];
|
|
1426
|
+
deviceData.Status = contactData[1];
|
|
1427
|
+
deviceData.SignalStrength = contactData[2];
|
|
1428
|
+
}
|
|
1429
|
+
|
|
1430
|
+
if (oldStatus !== deviceData.Status) {
|
|
1431
|
+
listDirty = true;
|
|
1432
|
+
}
|
|
1433
|
+
|
|
1434
|
+
if (this.config.checkSendOfflineMsg && oldStatus !== deviceData.Status && !this.blacklistNotify.includes(deviceData.Path)) {
|
|
1435
|
+
if (await tools.getTimestampConnectionDP(this, deviceData.instanceDeviceConnectionDP, 50000)) {
|
|
1436
|
+
await this.sendStateNotifications('Devices', 'onlineStateDevice', deviceID, silentEnabled.telegramSilent);
|
|
1437
|
+
}
|
|
1438
|
+
}
|
|
1439
|
+
}
|
|
1440
|
+
}
|
|
1441
|
+
}
|
|
1442
|
+
}
|
|
1443
|
+
return listDirty;
|
|
1444
|
+
}
|
|
1445
|
+
|
|
1446
|
+
/**
|
|
1447
|
+
* get all Instances at start
|
|
1448
|
+
*/
|
|
1449
|
+
async getAllInstanceData() {
|
|
1450
|
+
try {
|
|
1451
|
+
const allInstances = `system.adapter.*`;
|
|
1452
|
+
await this.getInstanceData(allInstances);
|
|
1453
|
+
} catch (error) {
|
|
1454
|
+
this.log.error(`[getInstance] - ${error}`);
|
|
1455
|
+
}
|
|
1456
|
+
}
|
|
1457
|
+
|
|
1458
|
+
/**
|
|
1459
|
+
* get instance data
|
|
1460
|
+
*
|
|
1461
|
+
*@param {string} instanceObject
|
|
1462
|
+
*/
|
|
1463
|
+
async getInstanceData(instanceObject) {
|
|
1464
|
+
try {
|
|
1465
|
+
const idDP = `${instanceObject}.alive`;
|
|
1466
|
+
const instanceAliveDP = await this.getForeignStatesAsync(idDP);
|
|
1467
|
+
|
|
1468
|
+
this.adapterUpdatesJsonRaw = await this.getAdapterUpdateData(adapterUpdateListDP);
|
|
1469
|
+
|
|
1470
|
+
for (const [id] of Object.entries(instanceAliveDP)) {
|
|
1471
|
+
if (!(typeof id === 'string' && id.startsWith(`system.adapter.`))) {
|
|
1472
|
+
continue;
|
|
1473
|
+
}
|
|
1474
|
+
|
|
1475
|
+
// get instance name
|
|
1476
|
+
const instanceID = await this.getInstanceName(id);
|
|
1477
|
+
|
|
1478
|
+
// get instance connected to host data
|
|
1479
|
+
const instanceConnectedHostDP = `system.adapter.${instanceID}.connected`;
|
|
1480
|
+
const instanceConnectedHostVal = await tools.getInitValue(this, instanceConnectedHostDP);
|
|
1481
|
+
|
|
1482
|
+
// get instance connected to device data
|
|
1483
|
+
const instanceConnectedDeviceDP = `${instanceID}.info.connection`;
|
|
1484
|
+
const devicesState = await this.getForeignStateAsync(instanceConnectedDeviceDP);
|
|
1485
|
+
|
|
1486
|
+
let instanceConnectedDeviceVal;
|
|
1487
|
+
if (devicesState !== null && typeof devicesState.val === 'boolean') {
|
|
1488
|
+
instanceConnectedDeviceVal = await tools.getInitValue(this, instanceConnectedDeviceDP);
|
|
1489
|
+
} else {
|
|
1490
|
+
instanceConnectedDeviceVal = 'N/A';
|
|
1491
|
+
}
|
|
1492
|
+
|
|
1493
|
+
// get adapter version
|
|
1494
|
+
const instanceObjectPath = `system.adapter.${instanceID}`;
|
|
1495
|
+
let adapterName;
|
|
1496
|
+
let adapterVersion;
|
|
1497
|
+
let adapterAvailableUpdate = '';
|
|
1498
|
+
let instanceMode;
|
|
1499
|
+
let scheduleTime = 'N/A';
|
|
1500
|
+
|
|
1501
|
+
const instanceObjectData = await this.getForeignObjectAsync(instanceObjectPath);
|
|
1502
|
+
|
|
1503
|
+
if (instanceObjectData) {
|
|
1504
|
+
adapterName = tools.capitalize(instanceObjectData.common.name);
|
|
1505
|
+
adapterVersion = instanceObjectData.common.version;
|
|
1506
|
+
instanceMode = instanceObjectData.common.mode;
|
|
1507
|
+
|
|
1508
|
+
if (instanceMode === 'schedule') {
|
|
1509
|
+
scheduleTime = instanceObjectData.common.schedule;
|
|
1510
|
+
}
|
|
1511
|
+
}
|
|
1512
|
+
|
|
1513
|
+
const updateEntry = this.adapterUpdatesJsonRaw.find((entry) => entry.adapter.toLowerCase() === adapterName.toLowerCase());
|
|
1514
|
+
|
|
1515
|
+
if (updateEntry) {
|
|
1516
|
+
adapterAvailableUpdate = updateEntry.newVersion;
|
|
1517
|
+
} else {
|
|
1518
|
+
adapterAvailableUpdate = ' - ';
|
|
1519
|
+
}
|
|
1520
|
+
|
|
1521
|
+
let isAlive;
|
|
1522
|
+
let isHealthy;
|
|
1523
|
+
let instanceStatus;
|
|
1524
|
+
if (instanceMode === 'schedule') {
|
|
1525
|
+
const instanceStatusRaw = await this.checkScheduleisHealty(instanceID, scheduleTime);
|
|
1526
|
+
isAlive = instanceStatusRaw[0];
|
|
1527
|
+
isHealthy = instanceStatusRaw[1];
|
|
1528
|
+
instanceStatus = instanceStatusRaw[2];
|
|
1529
|
+
} else if (instanceMode === 'daemon') {
|
|
1530
|
+
const instanceStatusRaw = await this.checkDaemonIsHealthy(instanceID);
|
|
1531
|
+
isAlive = instanceStatusRaw[0];
|
|
1532
|
+
isHealthy = instanceStatusRaw[1];
|
|
1533
|
+
instanceStatus = instanceStatusRaw[2];
|
|
1534
|
+
}
|
|
1535
|
+
|
|
1536
|
+
//subscribe to statechanges
|
|
1537
|
+
this.subscribeForeignStates(id);
|
|
1538
|
+
this.subscribeForeignStates(instanceConnectedHostDP);
|
|
1539
|
+
this.subscribeForeignStates(instanceConnectedDeviceDP);
|
|
1540
|
+
this.subscribeForeignObjects(`system.adapter.*`);
|
|
1541
|
+
// this.subscribeForeignStates('*');
|
|
1542
|
+
// this.subscribeForeignObjects('*');
|
|
1543
|
+
|
|
1544
|
+
// create raw list
|
|
1545
|
+
this.listInstanceRaw.set(instanceID, {
|
|
1546
|
+
Adapter: adapterName,
|
|
1547
|
+
instanceObjectPath: instanceObjectPath,
|
|
1548
|
+
instanceMode: instanceMode,
|
|
1549
|
+
schedule: scheduleTime,
|
|
1550
|
+
adapterVersion: adapterVersion,
|
|
1551
|
+
updateAvailable: adapterAvailableUpdate,
|
|
1552
|
+
isAlive: isAlive,
|
|
1553
|
+
isHealthy: isHealthy,
|
|
1554
|
+
isConnectedHost: instanceConnectedHostVal,
|
|
1555
|
+
isConnectedDevice: instanceConnectedDeviceVal,
|
|
1556
|
+
status: instanceStatus,
|
|
1557
|
+
aliveDP: `system.adapter.${instanceID}.alive`,
|
|
1558
|
+
hostConnectionDP: instanceConnectedHostDP,
|
|
1559
|
+
deviceConnectionDP: instanceConnectedDeviceDP,
|
|
1560
|
+
});
|
|
1561
|
+
}
|
|
1562
|
+
await this.createInstanceList();
|
|
1563
|
+
await this.writeInstanceDPs();
|
|
1564
|
+
} catch (error) {
|
|
1565
|
+
this.log.error(`[getInstanceData] - ${error}`);
|
|
1566
|
+
}
|
|
1567
|
+
}
|
|
1568
|
+
|
|
1569
|
+
/**
|
|
1570
|
+
* get Instances
|
|
1571
|
+
*
|
|
1572
|
+
* @param {string} id - Path of alive datapoint
|
|
1573
|
+
*/
|
|
1574
|
+
async getInstanceName(id) {
|
|
1575
|
+
let instance = id;
|
|
1576
|
+
instance = instance.slice(15); // remove "system.adapter."
|
|
1577
|
+
instance = instance.slice(0, instance.lastIndexOf('.') + 1 - 1); // remove ".alive"
|
|
1578
|
+
return instance;
|
|
1579
|
+
}
|
|
1580
|
+
|
|
1581
|
+
/**
|
|
1582
|
+
* Check if instance is alive and ok
|
|
1583
|
+
*
|
|
1584
|
+
* @param {string} instanceID
|
|
1585
|
+
*/
|
|
1586
|
+
async checkDaemonIsHealthy(instanceID) {
|
|
1587
|
+
const connectedHostState = await tools.getInitValue(this, `system.adapter.${instanceID}.connected`);
|
|
1588
|
+
const isAlive = await tools.getInitValue(this, `system.adapter.${instanceID}.alive`);
|
|
1589
|
+
let connectedDeviceState = await tools.getInitValue(this, `${instanceID}.info.connection`);
|
|
1590
|
+
if (connectedDeviceState === undefined) {
|
|
1591
|
+
connectedDeviceState = true;
|
|
1592
|
+
}
|
|
1593
|
+
|
|
1594
|
+
let isHealthy = false;
|
|
1595
|
+
let instanceStatusString = translations.instance_deactivated[this.config.userSelectedLanguage];
|
|
1596
|
+
|
|
1597
|
+
if (isAlive) {
|
|
1598
|
+
if (connectedHostState && connectedDeviceState) {
|
|
1599
|
+
isHealthy = true;
|
|
1600
|
+
instanceStatusString = translations.instance_okay[this.config.userSelectedLanguage];
|
|
1601
|
+
} else if (!connectedHostState) {
|
|
1602
|
+
instanceStatusString = translations.not_connected_host[this.config.userSelectedLanguage];
|
|
1603
|
+
} else if (!connectedDeviceState) {
|
|
1604
|
+
instanceStatusString = translations.not_connected_device[this.config.userSelectedLanguage];
|
|
1605
|
+
}
|
|
1606
|
+
}
|
|
1607
|
+
|
|
1608
|
+
return [Boolean(isAlive), Boolean(isHealthy), String(instanceStatusString), Boolean(connectedHostState), Boolean(connectedDeviceState)];
|
|
1609
|
+
}
|
|
1610
|
+
|
|
1611
|
+
/**
|
|
1612
|
+
* Check if instance is alive and ok
|
|
1613
|
+
*
|
|
1614
|
+
* @param {string} instanceID
|
|
1615
|
+
* @param {number} instanceDeactivationTime
|
|
1616
|
+
*/
|
|
1617
|
+
async checkDaemonIsAlive(instanceID, instanceDeactivationTime) {
|
|
1618
|
+
let isAlive = await tools.getInitValue(this, `system.adapter.${instanceID}.alive`);
|
|
1619
|
+
let daemonIsAlive;
|
|
1620
|
+
let isHealthy = false;
|
|
1621
|
+
let instanceStatusString = isAlive ? translations.instance_activated[this.config.userSelectedLanguage] : translations.instance_deactivated[this.config.userSelectedLanguage];
|
|
1622
|
+
|
|
1623
|
+
if (isAlive) {
|
|
1624
|
+
daemonIsAlive = await this.checkDaemonIsHealthy(instanceID);
|
|
1625
|
+
} else {
|
|
1626
|
+
await this.delay(instanceDeactivationTime);
|
|
1627
|
+
daemonIsAlive = await this.checkDaemonIsHealthy(instanceID);
|
|
1628
|
+
if (!daemonIsAlive[0]) {
|
|
1629
|
+
await this.delay(instanceDeactivationTime);
|
|
1630
|
+
daemonIsAlive = await this.checkDaemonIsHealthy(instanceID);
|
|
1631
|
+
}
|
|
1632
|
+
}
|
|
1633
|
+
|
|
1634
|
+
isAlive = Boolean(daemonIsAlive[0]);
|
|
1635
|
+
isHealthy = Boolean(daemonIsAlive[1]);
|
|
1636
|
+
instanceStatusString = String(daemonIsAlive[2]);
|
|
1637
|
+
const connectedToHost = Boolean(daemonIsAlive[3]);
|
|
1638
|
+
const connectedToDevice = Boolean(daemonIsAlive[4]);
|
|
1639
|
+
|
|
1640
|
+
return [isAlive, isHealthy, instanceStatusString, connectedToHost, connectedToDevice];
|
|
1641
|
+
}
|
|
1642
|
+
|
|
1643
|
+
async checkScheduleisHealty(instanceID, scheduleTime) {
|
|
1644
|
+
let lastUpdate;
|
|
1645
|
+
let previousCronRun = null;
|
|
1646
|
+
let lastCronRun;
|
|
1647
|
+
let diff;
|
|
1648
|
+
let isAlive = false;
|
|
1649
|
+
let isHealthy = false;
|
|
1650
|
+
let instanceStatusString = translations.instance_deactivated[this.config.userSelectedLanguage];
|
|
1651
|
+
const isAliveSchedule = await this.getForeignStateAsync(`system.adapter.${instanceID}.alive`);
|
|
1652
|
+
|
|
1653
|
+
if (isAliveSchedule) {
|
|
1654
|
+
lastUpdate = Math.round((Date.now() - isAliveSchedule.lc) / 1000); // Last state change in seconds
|
|
1655
|
+
previousCronRun = await this.getPreviousCronRun(scheduleTime); // When was the last cron run
|
|
1656
|
+
if (previousCronRun) {
|
|
1657
|
+
lastCronRun = Math.round(previousCronRun / 1000); // change distance to last run in seconds
|
|
1658
|
+
diff = lastCronRun - lastUpdate;
|
|
1659
|
+
if (diff > -300) {
|
|
1660
|
+
// if 5 minutes difference exceeded, instance is not alive
|
|
1661
|
+
isAlive = true;
|
|
1662
|
+
isHealthy = true;
|
|
1663
|
+
instanceStatusString = translations.instance_okay[this.config.userSelectedLanguage];
|
|
1664
|
+
}
|
|
1665
|
+
}
|
|
1666
|
+
}
|
|
1667
|
+
|
|
1668
|
+
return [isAlive, isHealthy, instanceStatusString];
|
|
1669
|
+
}
|
|
1670
|
+
|
|
1671
|
+
/**
|
|
1672
|
+
* set status for instance
|
|
1673
|
+
*
|
|
1674
|
+
* @param {string} instanceMode
|
|
1675
|
+
* @param {string} scheduleTime
|
|
1676
|
+
* @param {any} instanceID
|
|
1677
|
+
*/
|
|
1678
|
+
async setInstanceStatus(instanceMode, scheduleTime, instanceID) {
|
|
1679
|
+
let instanceDeactivationTime = (this.config.offlineTimeInstances * 1000) / 2;
|
|
1680
|
+
let instanceErrorTime = (this.config.errorTimeInstances * 1000) / 2;
|
|
1681
|
+
let isAlive;
|
|
1682
|
+
let isHealthy;
|
|
1683
|
+
let instanceStatusString;
|
|
1684
|
+
let daemonIsAlive;
|
|
1685
|
+
let daemonIsNotAlive;
|
|
1686
|
+
let scheduleIsAlive;
|
|
1687
|
+
let connectedToHost;
|
|
1688
|
+
let connectedToDevice;
|
|
1689
|
+
|
|
1690
|
+
switch (instanceMode) {
|
|
1691
|
+
case 'schedule':
|
|
1692
|
+
scheduleIsAlive = await this.checkScheduleisHealty(instanceID, scheduleTime);
|
|
1693
|
+
isAlive = Boolean(scheduleIsAlive[0]);
|
|
1694
|
+
isHealthy = Boolean(scheduleIsAlive[1]);
|
|
1695
|
+
instanceStatusString = String(scheduleIsAlive[2]);
|
|
1696
|
+
break;
|
|
1697
|
+
case 'daemon':
|
|
1698
|
+
// check with time the user did define for error and deactivation
|
|
1699
|
+
if (this.userTimeInstancesList.has(instanceID)) {
|
|
1700
|
+
const userTimeInstances = this.userTimeInstancesList.get(instanceID);
|
|
1701
|
+
instanceDeactivationTime = (userTimeInstances.deactivationTime * 1000) / 2;
|
|
1702
|
+
instanceErrorTime = (userTimeInstances.errorTime * 1000) / 2;
|
|
1703
|
+
}
|
|
1704
|
+
daemonIsAlive = await this.checkDaemonIsHealthy(instanceID);
|
|
1705
|
+
if (daemonIsAlive[0] && !daemonIsAlive[1]) {
|
|
1706
|
+
await this.delay(instanceErrorTime);
|
|
1707
|
+
const daemonIsAliveAfterDelay = await this.checkDaemonIsHealthy(instanceID);
|
|
1708
|
+
|
|
1709
|
+
if (daemonIsAliveAfterDelay[0] && !daemonIsAliveAfterDelay[1]) {
|
|
1710
|
+
await this.delay(instanceErrorTime);
|
|
1711
|
+
const daemonIsAliveAfterSecondDelay = await this.checkDaemonIsHealthy(instanceID);
|
|
1712
|
+
|
|
1713
|
+
// nach allen Retries: Status übernehmen (egal ob erholt oder weiterhin fehlerhaft)
|
|
1714
|
+
isAlive = Boolean(daemonIsAliveAfterSecondDelay[0]);
|
|
1715
|
+
isHealthy = Boolean(daemonIsAliveAfterSecondDelay[1]);
|
|
1716
|
+
instanceStatusString = String(daemonIsAliveAfterSecondDelay[2]);
|
|
1717
|
+
connectedToHost = Boolean(daemonIsAliveAfterSecondDelay[3]);
|
|
1718
|
+
connectedToDevice = Boolean(daemonIsAliveAfterSecondDelay[4]);
|
|
1719
|
+
} else {
|
|
1720
|
+
// nach erstem Retry wieder gesund
|
|
1721
|
+
isAlive = Boolean(daemonIsAliveAfterDelay[0]);
|
|
1722
|
+
isHealthy = Boolean(daemonIsAliveAfterDelay[1]);
|
|
1723
|
+
instanceStatusString = String(daemonIsAliveAfterDelay[2]);
|
|
1724
|
+
connectedToHost = Boolean(daemonIsAliveAfterDelay[3]);
|
|
1725
|
+
connectedToDevice = Boolean(daemonIsAliveAfterDelay[4]);
|
|
1726
|
+
}
|
|
1727
|
+
} else {
|
|
1728
|
+
daemonIsNotAlive = await this.checkDaemonIsAlive(instanceID, instanceDeactivationTime);
|
|
1729
|
+
isAlive = Boolean(daemonIsNotAlive[0]);
|
|
1730
|
+
isHealthy = Boolean(daemonIsNotAlive[1]);
|
|
1731
|
+
instanceStatusString = String(daemonIsNotAlive[2]);
|
|
1732
|
+
connectedToHost = Boolean(daemonIsNotAlive[3]);
|
|
1733
|
+
connectedToDevice = Boolean(daemonIsNotAlive[4]);
|
|
1734
|
+
}
|
|
1735
|
+
|
|
1736
|
+
break;
|
|
1737
|
+
}
|
|
1738
|
+
|
|
1739
|
+
return [isAlive, isHealthy, instanceStatusString, connectedToHost, connectedToDevice];
|
|
1740
|
+
}
|
|
1741
|
+
|
|
1742
|
+
/**
|
|
1743
|
+
* create adapter update raw lists
|
|
1744
|
+
*
|
|
1745
|
+
* @param {string} adapterUpdateListDP
|
|
1746
|
+
*/
|
|
1747
|
+
async getAdapterUpdateData(adapterUpdateListDP) {
|
|
1748
|
+
// Clear the existing adapter updates data
|
|
1749
|
+
let adapterUpdatesJsonRaw = [];
|
|
1750
|
+
let adapterJsonList = {};
|
|
1751
|
+
|
|
1752
|
+
// Fetch the adapter updates list
|
|
1753
|
+
const adapterUpdatesListVal = await this.getForeignStatesAsync(adapterUpdateListDP);
|
|
1754
|
+
|
|
1755
|
+
// Extract adapter data from the list - merge all admin instances
|
|
1756
|
+
for (const [_id, value] of Object.entries(adapterUpdatesListVal)) {
|
|
1757
|
+
const parsed = tools.parseData(value.val);
|
|
1758
|
+
if (parsed && typeof parsed === 'object') {
|
|
1759
|
+
Object.assign(adapterJsonList, parsed);
|
|
1760
|
+
}
|
|
1761
|
+
}
|
|
1762
|
+
|
|
1763
|
+
// Populate the adapter updates data
|
|
1764
|
+
for (const [id, adapterData] of Object.entries(adapterJsonList)) {
|
|
1765
|
+
adapterUpdatesJsonRaw.push({
|
|
1766
|
+
adapter: tools.capitalize(id),
|
|
1767
|
+
newVersion: adapterData.availableVersion,
|
|
1768
|
+
oldVersion: adapterData.installedVersion,
|
|
1769
|
+
});
|
|
1770
|
+
}
|
|
1771
|
+
|
|
1772
|
+
return adapterUpdatesJsonRaw;
|
|
1773
|
+
}
|
|
1774
|
+
|
|
1775
|
+
/**
|
|
1776
|
+
* create instanceList
|
|
1777
|
+
*/
|
|
1778
|
+
async createAdapterUpdateList() {
|
|
1779
|
+
this.listAdapterUpdates = [];
|
|
1780
|
+
this.countAdapterUpdates = 0;
|
|
1781
|
+
|
|
1782
|
+
for (const updateData of this.adapterUpdatesJsonRaw) {
|
|
1783
|
+
this.listAdapterUpdates.push({
|
|
1784
|
+
[translations.Adapter[this.config.userSelectedLanguage]]: updateData.adapter,
|
|
1785
|
+
[translations.Available_Version[this.config.userSelectedLanguage]]: updateData.newVersion,
|
|
1786
|
+
[translations.Installed_Version[this.config.userSelectedLanguage]]: updateData.oldVersion,
|
|
1787
|
+
});
|
|
1788
|
+
}
|
|
1789
|
+
|
|
1790
|
+
this.countAdapterUpdates = this.listAdapterUpdates.length;
|
|
1791
|
+
await this.writeAdapterUpdatesDPs();
|
|
1792
|
+
}
|
|
1793
|
+
|
|
1794
|
+
/**
|
|
1795
|
+
* write datapoints for adapter with updates
|
|
1796
|
+
*/
|
|
1797
|
+
async writeAdapterUpdatesDPs() {
|
|
1798
|
+
// Write Datapoints for counts
|
|
1799
|
+
await this.setStateChangedAsync(`adapterAndInstances.countAdapterUpdates`, {
|
|
1800
|
+
val: this.countAdapterUpdates,
|
|
1801
|
+
ack: true
|
|
1802
|
+
});
|
|
1803
|
+
|
|
1804
|
+
if (this.countAdapterUpdates === 0) {
|
|
1805
|
+
this.listAdapterUpdates = [
|
|
1806
|
+
{
|
|
1807
|
+
[translations.Adapter[this.config.userSelectedLanguage]]: '--no updates--',
|
|
1808
|
+
[translations.Available_Version[this.config.userSelectedLanguage]]: '',
|
|
1809
|
+
[translations.Installed_Version[this.config.userSelectedLanguage]]: '',
|
|
1810
|
+
},
|
|
1811
|
+
];
|
|
1812
|
+
}
|
|
1813
|
+
await this.setStateChangedAsync(`adapterAndInstances.listAdapterUpdates`, {
|
|
1814
|
+
val: JSON.stringify(this.listAdapterUpdates),
|
|
1815
|
+
ack: true
|
|
1816
|
+
});
|
|
1817
|
+
}
|
|
1818
|
+
|
|
1819
|
+
/**
|
|
1820
|
+
* create instanceList
|
|
1821
|
+
*/
|
|
1822
|
+
async createInstanceList() {
|
|
1823
|
+
this.listAllInstances = [];
|
|
1824
|
+
this.listAllActiveInstances = [];
|
|
1825
|
+
this.listDeactivatedInstances = [];
|
|
1826
|
+
this.listErrorInstanceRaw = [];
|
|
1827
|
+
this.listErrorInstance = [];
|
|
1828
|
+
|
|
1829
|
+
for (const [instance, instanceData] of this.listInstanceRaw) {
|
|
1830
|
+
// fill raw list
|
|
1831
|
+
if (instanceData.isAlive && !instanceData.isHealthy) {
|
|
1832
|
+
this.listErrorInstanceRaw.push({
|
|
1833
|
+
Adapter: instanceData.Adapter,
|
|
1834
|
+
Instance: instance,
|
|
1835
|
+
Mode: instanceData.instanceMode,
|
|
1836
|
+
Status: instanceData.status,
|
|
1837
|
+
});
|
|
1838
|
+
}
|
|
1839
|
+
|
|
1840
|
+
if (this.blacklistInstancesLists.includes(instance)) {
|
|
1841
|
+
continue;
|
|
1842
|
+
}
|
|
1843
|
+
// all instances
|
|
1844
|
+
this.listAllInstances.push({
|
|
1845
|
+
[translations.Adapter[this.config.userSelectedLanguage]]: instanceData.Adapter,
|
|
1846
|
+
[translations.Instance[this.config.userSelectedLanguage]]: instance,
|
|
1847
|
+
[translations.Mode[this.config.userSelectedLanguage]]: instanceData.instanceMode,
|
|
1848
|
+
[translations.Schedule[this.config.userSelectedLanguage]]: instanceData.schedule,
|
|
1849
|
+
[translations.Version[this.config.userSelectedLanguage]]: instanceData.adapterVersion,
|
|
1850
|
+
[translations.Updateable[this.config.userSelectedLanguage]]: instanceData.updateAvailable,
|
|
1851
|
+
[translations.Status[this.config.userSelectedLanguage]]: instanceData.status,
|
|
1852
|
+
});
|
|
1853
|
+
|
|
1854
|
+
if (!instanceData.isAlive) {
|
|
1855
|
+
// list with deactivated instances
|
|
1856
|
+
this.listDeactivatedInstances.push({
|
|
1857
|
+
[translations.Adapter[this.config.userSelectedLanguage]]: instanceData.Adapter,
|
|
1858
|
+
[translations.Instance[this.config.userSelectedLanguage]]: instance,
|
|
1859
|
+
[translations.Status[this.config.userSelectedLanguage]]: instanceData.status,
|
|
1860
|
+
});
|
|
1861
|
+
} else {
|
|
1862
|
+
// list with active instances
|
|
1863
|
+
this.listAllActiveInstances.push({
|
|
1864
|
+
[translations.Adapter[this.config.userSelectedLanguage]]: instanceData.Adapter,
|
|
1865
|
+
[translations.Instance[this.config.userSelectedLanguage]]: instance,
|
|
1866
|
+
[translations.Mode[this.config.userSelectedLanguage]]: instanceData.instanceMode,
|
|
1867
|
+
[translations.Schedule[this.config.userSelectedLanguage]]: instanceData.schedule,
|
|
1868
|
+
[translations.Status[this.config.userSelectedLanguage]]: instanceData.status,
|
|
1869
|
+
});
|
|
1870
|
+
}
|
|
1871
|
+
|
|
1872
|
+
// list with error instances
|
|
1873
|
+
if (instanceData.isAlive && !instanceData.isHealthy) {
|
|
1874
|
+
this.listErrorInstance.push({
|
|
1875
|
+
[translations.Adapter[this.config.userSelectedLanguage]]: instanceData.Adapter,
|
|
1876
|
+
[translations.Instance[this.config.userSelectedLanguage]]: instance,
|
|
1877
|
+
[translations.Mode[this.config.userSelectedLanguage]]: instanceData.instanceMode,
|
|
1878
|
+
[translations.Status[this.config.userSelectedLanguage]]: instanceData.status,
|
|
1879
|
+
});
|
|
1880
|
+
}
|
|
1881
|
+
}
|
|
1882
|
+
await this.countInstances();
|
|
1883
|
+
}
|
|
1884
|
+
|
|
1885
|
+
/**
|
|
1886
|
+
* count instanceList
|
|
1887
|
+
*/
|
|
1888
|
+
async countInstances() {
|
|
1889
|
+
this.countAllInstances = 0;
|
|
1890
|
+
this.countAllActiveInstances = 0;
|
|
1891
|
+
this.countDeactivatedInstances = 0;
|
|
1892
|
+
this.countErrorInstance = 0;
|
|
1893
|
+
|
|
1894
|
+
this.countAllInstances = this.listAllInstances.length;
|
|
1895
|
+
this.countAllActiveInstances = this.listAllActiveInstances.length;
|
|
1896
|
+
this.countDeactivatedInstances = this.listDeactivatedInstances.length;
|
|
1897
|
+
this.countErrorInstance = this.listErrorInstance.length;
|
|
1898
|
+
}
|
|
1899
|
+
|
|
1900
|
+
/**
|
|
1901
|
+
* write datapoints for instances list and counts
|
|
1902
|
+
*/
|
|
1903
|
+
async writeInstanceDPs() {
|
|
1904
|
+
// List all instances
|
|
1905
|
+
await this.setStateChangedAsync(`adapterAndInstances.listAllInstances`, {
|
|
1906
|
+
val: JSON.stringify(this.listAllInstances),
|
|
1907
|
+
ack: true
|
|
1908
|
+
});
|
|
1909
|
+
await this.setStateChangedAsync(`adapterAndInstances.countAllInstances`, {
|
|
1910
|
+
val: this.countAllInstances,
|
|
1911
|
+
ack: true
|
|
1912
|
+
});
|
|
1913
|
+
|
|
1914
|
+
// List all active instances
|
|
1915
|
+
await this.setStateChangedAsync(`adapterAndInstances.listAllActiveInstances`, {
|
|
1916
|
+
val: JSON.stringify(this.listAllActiveInstances),
|
|
1917
|
+
ack: true
|
|
1918
|
+
});
|
|
1919
|
+
await this.setStateChangedAsync(`adapterAndInstances.countAllActiveInstances`, {
|
|
1920
|
+
val: this.countAllActiveInstances,
|
|
1921
|
+
ack: true
|
|
1922
|
+
});
|
|
1923
|
+
|
|
1924
|
+
// list deactivated instances
|
|
1925
|
+
if (this.countDeactivatedInstances === 0) {
|
|
1926
|
+
this.listDeactivatedInstances = [
|
|
1927
|
+
{
|
|
1928
|
+
[translations.Adapter[this.config.userSelectedLanguage]]: '--none--',
|
|
1929
|
+
[translations.Instance[this.config.userSelectedLanguage]]: '',
|
|
1930
|
+
[translations.Version[this.config.userSelectedLanguage]]: '',
|
|
1931
|
+
[translations.Status[this.config.userSelectedLanguage]]: '',
|
|
1932
|
+
},
|
|
1933
|
+
];
|
|
1934
|
+
}
|
|
1935
|
+
await this.setStateChangedAsync(`adapterAndInstances.listDeactivatedInstances`, {
|
|
1936
|
+
val: JSON.stringify(this.listDeactivatedInstances),
|
|
1937
|
+
ack: true
|
|
1938
|
+
});
|
|
1939
|
+
await this.setStateChangedAsync(`adapterAndInstances.countDeactivatedInstances`, {
|
|
1940
|
+
val: this.countDeactivatedInstances,
|
|
1941
|
+
ack: true
|
|
1942
|
+
});
|
|
1943
|
+
|
|
1944
|
+
// list error instances
|
|
1945
|
+
if (this.countErrorInstance === 0) {
|
|
1946
|
+
this.listErrorInstance = [
|
|
1947
|
+
{
|
|
1948
|
+
[translations.Adapter[this.config.userSelectedLanguage]]: '--none--',
|
|
1949
|
+
[translations.Instance[this.config.userSelectedLanguage]]: '',
|
|
1950
|
+
[translations.Mode[this.config.userSelectedLanguage]]: '',
|
|
1951
|
+
[translations.Status[this.config.userSelectedLanguage]]: '',
|
|
1952
|
+
},
|
|
1953
|
+
];
|
|
1954
|
+
}
|
|
1955
|
+
await this.setStateChangedAsync(`adapterAndInstances.listInstancesError`, {
|
|
1956
|
+
val: JSON.stringify(this.listErrorInstance),
|
|
1957
|
+
ack: true
|
|
1958
|
+
});
|
|
1959
|
+
await this.setStateChangedAsync(`adapterAndInstances.countInstancesError`, {
|
|
1960
|
+
val: this.countErrorInstance,
|
|
1961
|
+
ack: true
|
|
1962
|
+
});
|
|
1963
|
+
}
|
|
1964
|
+
|
|
1965
|
+
/**
|
|
1966
|
+
* @param {string} id
|
|
1967
|
+
*/
|
|
1968
|
+
async renewAdapterUpdateData(id) {
|
|
1969
|
+
const previousAdapterUpdatesCount = this.countAdapterUpdates;
|
|
1970
|
+
|
|
1971
|
+
// Fetch and process adapter update data
|
|
1972
|
+
await this.getAdapterUpdateData(id);
|
|
1973
|
+
await this.createAdapterUpdateList();
|
|
1974
|
+
|
|
1975
|
+
// Check and send update notification if required
|
|
1976
|
+
if (this.config.checkSendAdapterUpdateMsg && this.countAdapterUpdates > previousAdapterUpdatesCount) {
|
|
1977
|
+
await this.sendStateNotifications('AdapterUpdates', 'updateAdapter', null);
|
|
1978
|
+
}
|
|
1979
|
+
|
|
1980
|
+
// Update instances with available adapter updates
|
|
1981
|
+
for (const instance of this.listInstanceRaw.values()) {
|
|
1982
|
+
const adapterUpdate = this.adapterUpdatesJsonRaw.find((entry) => entry.adapter.toLowerCase() === instance.Adapter.toLowerCase());
|
|
1983
|
+
|
|
1984
|
+
if (adapterUpdate) {
|
|
1985
|
+
instance.updateAvailable = adapterUpdate.newVersion;
|
|
1986
|
+
} else {
|
|
1987
|
+
instance.updateAvailable = ' - ';
|
|
1988
|
+
}
|
|
1989
|
+
}
|
|
1990
|
+
}
|
|
1991
|
+
|
|
1992
|
+
/**
|
|
1993
|
+
* call function on state change, renew data and send messages
|
|
1994
|
+
*
|
|
1995
|
+
* @param {string} id
|
|
1996
|
+
* @param {ioBroker.State} state
|
|
1997
|
+
*/
|
|
1998
|
+
async renewInstanceData(id, state) {
|
|
1999
|
+
const instanceID = await this.getInstanceName(id);
|
|
2000
|
+
const instanceData = this.listInstanceRaw.get(instanceID);
|
|
2001
|
+
if (instanceData) {
|
|
2002
|
+
let instanceStatusRaw;
|
|
2003
|
+
|
|
2004
|
+
const checkInstance = async (instanceID, instanceData) => {
|
|
2005
|
+
instanceStatusRaw = await this.setInstanceStatus(instanceData.instanceMode, instanceData.schedule, instanceID);
|
|
2006
|
+
instanceData.isAlive = instanceStatusRaw[0];
|
|
2007
|
+
instanceData.isHealthy = instanceStatusRaw[1];
|
|
2008
|
+
instanceData.status = instanceStatusRaw[2];
|
|
2009
|
+
instanceData.isConnectedHost = instanceStatusRaw[3];
|
|
2010
|
+
instanceData.isConnectedDevice = instanceStatusRaw[4];
|
|
2011
|
+
return;
|
|
2012
|
+
};
|
|
2013
|
+
|
|
2014
|
+
switch (id) {
|
|
2015
|
+
case `system.adapter.${instanceID}.alive`:
|
|
2016
|
+
if (state.val !== instanceData.isAlive) {
|
|
2017
|
+
await checkInstance(instanceID, instanceData);
|
|
2018
|
+
// send message when instance was deactivated
|
|
2019
|
+
if (this.config.checkSendInstanceDeactivatedMsg && !instanceData.isAlive) {
|
|
2020
|
+
if (this.blacklistInstancesNotify.includes(instanceID)) {
|
|
2021
|
+
break;
|
|
2022
|
+
}
|
|
2023
|
+
// Restart-Erkennung: Toleranzzeit abwarten und prüfen ob Instanz schon wieder läuft
|
|
2024
|
+
const restartTolerance = this.userTimeInstancesList.has(instanceID)
|
|
2025
|
+
? this.userTimeInstancesList.get(instanceID).deactivationTime * 1000
|
|
2026
|
+
: this.config.offlineTimeInstances * 1000;
|
|
2027
|
+
|
|
2028
|
+
this.log.debug(`[renewInstanceData] Instance ${instanceID} went offline - waiting ${restartTolerance}ms to check for restart...`);
|
|
2029
|
+
await this.delay(restartTolerance);
|
|
2030
|
+
|
|
2031
|
+
const aliveAfterWait = await tools.getInitValue(this, `system.adapter.${instanceID}.alive`);
|
|
2032
|
+
if (aliveAfterWait) {
|
|
2033
|
+
// Instanz ist bereits wieder online → war nur ein Neustart
|
|
2034
|
+
this.log.debug(`[renewInstanceData] Instance ${instanceID} is back online after restart. No deactivation notification sent.`);
|
|
2035
|
+
await checkInstance(instanceID, instanceData);
|
|
2036
|
+
break;
|
|
2037
|
+
}
|
|
2038
|
+
|
|
2039
|
+
await this.sendStateNotifications('Instances', 'deactivatedInstance', instanceID);
|
|
2040
|
+
}
|
|
2041
|
+
}
|
|
2042
|
+
break;
|
|
2043
|
+
|
|
2044
|
+
case `system.adapter.${instanceID}.connected`:
|
|
2045
|
+
if (state.val !== instanceData.isConnectedHost && instanceData.isAlive) {
|
|
2046
|
+
await checkInstance(instanceID, instanceData);
|
|
2047
|
+
// send message when instance has an error
|
|
2048
|
+
if (this.config.checkSendInstanceFailedMsg && !instanceData.isHealthy && instanceData.isAlive) {
|
|
2049
|
+
if (this.blacklistInstancesNotify.includes(instanceID)) {
|
|
2050
|
+
return;
|
|
2051
|
+
}
|
|
2052
|
+
await this.sendStateNotifications('Instances', 'errorInstance', instanceID);
|
|
2053
|
+
}
|
|
2054
|
+
}
|
|
2055
|
+
break;
|
|
2056
|
+
|
|
2057
|
+
case `${instanceID}.info.connection`:
|
|
2058
|
+
if (state.val !== instanceData.isConnectedDevice && instanceData.isAlive) {
|
|
2059
|
+
await checkInstance(instanceID, instanceData);
|
|
2060
|
+
// send message when instance has an error
|
|
2061
|
+
if (this.config.checkSendInstanceFailedMsg && !instanceData.isHealthy && instanceData.isAlive) {
|
|
2062
|
+
if (this.blacklistInstancesNotify.includes(instanceID)) {
|
|
2063
|
+
return;
|
|
2064
|
+
}
|
|
2065
|
+
await this.sendStateNotifications('Instances', 'errorInstance', instanceID);
|
|
2066
|
+
}
|
|
2067
|
+
}
|
|
2068
|
+
break;
|
|
2069
|
+
}
|
|
2070
|
+
}
|
|
2071
|
+
}
|
|
2072
|
+
|
|
2073
|
+
/*=============================================
|
|
2074
|
+
= functions to send notifications =
|
|
2075
|
+
=============================================*/
|
|
2076
|
+
|
|
2077
|
+
/**
|
|
2078
|
+
* Notification service
|
|
2079
|
+
*
|
|
2080
|
+
* @param {string} text - Text which should be send
|
|
2081
|
+
* @param silent
|
|
2082
|
+
*/
|
|
2083
|
+
async sendNotification(text, silent = false) {
|
|
2084
|
+
// Pushover
|
|
2085
|
+
if (this.config.instancePushover) {
|
|
2086
|
+
try {
|
|
2087
|
+
//first check if instance is living
|
|
2088
|
+
const pushoverAliveState = await tools.getInitValue(this, `system.adapter.${this.config.instancePushover}.alive`);
|
|
2089
|
+
|
|
2090
|
+
if (!pushoverAliveState) {
|
|
2091
|
+
this.log.warn('Pushover instance is not running. Message could not be sent. Please check your instance configuration.');
|
|
2092
|
+
} else {
|
|
2093
|
+
await this.sendToAsync(this.config.instancePushover, 'send', {
|
|
2094
|
+
message: text,
|
|
2095
|
+
title: this.config.titlePushover,
|
|
2096
|
+
device: this.config.devicePushover,
|
|
2097
|
+
user: this.config.userPushover,
|
|
2098
|
+
priority: this.config.prioPushover,
|
|
2099
|
+
});
|
|
2100
|
+
}
|
|
2101
|
+
} catch (error) {
|
|
2102
|
+
this.log.error(`[sendNotification Pushover] - ${error}`);
|
|
2103
|
+
}
|
|
2104
|
+
}
|
|
2105
|
+
|
|
2106
|
+
// Telegram
|
|
2107
|
+
if (this.config.instanceTelegram) {
|
|
2108
|
+
try {
|
|
2109
|
+
//first check if instance is living
|
|
2110
|
+
const telegramAliveState = await tools.getInitValue(this, `system.adapter.${this.config.instanceTelegram}.alive`);
|
|
2111
|
+
|
|
2112
|
+
if (!telegramAliveState) {
|
|
2113
|
+
this.log.warn('Telegram instance is not running. Message could not be sent. Please check your instance configuration.');
|
|
2114
|
+
} else {
|
|
2115
|
+
await this.sendToAsync(this.config.instanceTelegram, 'send', {
|
|
2116
|
+
text: text,
|
|
2117
|
+
user: this.config.deviceTelegram,
|
|
2118
|
+
chatId: this.config.chatIdTelegram,
|
|
2119
|
+
disable_notification: silent,
|
|
2120
|
+
});
|
|
2121
|
+
}
|
|
2122
|
+
} catch (error) {
|
|
2123
|
+
this.log.error(`[sendNotification Telegram] - ${error}`);
|
|
2124
|
+
}
|
|
2125
|
+
}
|
|
2126
|
+
|
|
2127
|
+
// Whatsapp
|
|
2128
|
+
if (this.config.instanceWhatsapp) {
|
|
2129
|
+
try {
|
|
2130
|
+
//first check if instance is living
|
|
2131
|
+
const whatsappAliveState = await tools.getInitValue(this, `system.adapter.${this.config.instanceWhatsapp}.alive`);
|
|
2132
|
+
|
|
2133
|
+
if (!whatsappAliveState) {
|
|
2134
|
+
this.log.warn('Whatsapp instance is not running. Message could not be sent. Please check your instance configuration.');
|
|
2135
|
+
} else {
|
|
2136
|
+
await this.sendToAsync(this.config.instanceWhatsapp, 'send', {
|
|
2137
|
+
text: text,
|
|
2138
|
+
phone: this.config.phoneWhatsapp,
|
|
2139
|
+
});
|
|
2140
|
+
}
|
|
2141
|
+
} catch (error) {
|
|
2142
|
+
this.log.error(`[sendNotification Whatsapp] - ${error}`);
|
|
2143
|
+
}
|
|
2144
|
+
}
|
|
2145
|
+
|
|
2146
|
+
// Matrix
|
|
2147
|
+
if (this.config.instanceMatrix) {
|
|
2148
|
+
try {
|
|
2149
|
+
//first check if instance is living
|
|
2150
|
+
const matrixAliveState = await tools.getInitValue(this, `system.adapter.${this.config.instanceMatrix}.alive`);
|
|
2151
|
+
|
|
2152
|
+
if (!matrixAliveState) {
|
|
2153
|
+
this.log.warn('Matrix instance is not running. Message could not be sent. Please check your instance configuration.');
|
|
2154
|
+
} else {
|
|
2155
|
+
await this.sendToAsync(this.config.instanceMatrix, 'send', {
|
|
2156
|
+
html: `<h1>${this.config.titleMatrix}</h1>`,
|
|
2157
|
+
text: text,
|
|
2158
|
+
});
|
|
2159
|
+
}
|
|
2160
|
+
} catch (error) {
|
|
2161
|
+
this.log.error(`[sendNotification Matrix] - ${error}`);
|
|
2162
|
+
}
|
|
2163
|
+
}
|
|
2164
|
+
|
|
2165
|
+
// Signal
|
|
2166
|
+
if (this.config.instanceSignal) {
|
|
2167
|
+
try {
|
|
2168
|
+
//first check if instance is living
|
|
2169
|
+
const signalAliveState = await tools.getInitValue(this, `system.adapter.${this.config.instanceSignal}.alive`);
|
|
2170
|
+
|
|
2171
|
+
if (!signalAliveState) {
|
|
2172
|
+
this.log.warn('Signal instance is not running. Message could not be sent. Please check your instance configuration.');
|
|
2173
|
+
} else {
|
|
2174
|
+
await this.sendToAsync(this.config.instanceSignal, 'send', {
|
|
2175
|
+
text: text,
|
|
2176
|
+
phone: this.config.phoneSignal,
|
|
2177
|
+
});
|
|
2178
|
+
}
|
|
2179
|
+
} catch (error) {
|
|
2180
|
+
this.log.error(`[sendNotification Signal] - ${error}`);
|
|
2181
|
+
}
|
|
2182
|
+
}
|
|
2183
|
+
|
|
2184
|
+
// Email
|
|
2185
|
+
if (this.config.instanceEmail) {
|
|
2186
|
+
try {
|
|
2187
|
+
//first check if instance is living
|
|
2188
|
+
const eMailAliveState = await tools.getInitValue(this, `system.adapter.${this.config.instanceEmail}.alive`);
|
|
2189
|
+
|
|
2190
|
+
if (!eMailAliveState) {
|
|
2191
|
+
this.log.warn('eMail instance is not running. Message could not be sent. Please check your instance configuration.');
|
|
2192
|
+
} else {
|
|
2193
|
+
await this.sendToAsync(this.config.instanceEmail, 'send', {
|
|
2194
|
+
sendTo: this.config.sendToEmail,
|
|
2195
|
+
text: text,
|
|
2196
|
+
subject: this.config.subjectEmail,
|
|
2197
|
+
});
|
|
2198
|
+
}
|
|
2199
|
+
} catch (error) {
|
|
2200
|
+
this.log.error(`[sendNotification eMail] - ${error}`);
|
|
2201
|
+
}
|
|
2202
|
+
}
|
|
2203
|
+
|
|
2204
|
+
// Jarvis Notification
|
|
2205
|
+
if (this.config.instanceJarvis) {
|
|
2206
|
+
try {
|
|
2207
|
+
//first check if instance is living
|
|
2208
|
+
const jarvisAliveState = await tools.getInitValue(this, `system.adapter.${this.config.instanceJarvis}.alive`);
|
|
2209
|
+
|
|
2210
|
+
if (!jarvisAliveState) {
|
|
2211
|
+
this.log.warn('Jarvis instance is not running. Message could not be sent. Please check your instance configuration.');
|
|
2212
|
+
} else {
|
|
2213
|
+
const jsonText = JSON.stringify(text);
|
|
2214
|
+
await this.setForeignStateAsync(
|
|
2215
|
+
`${this.config.instanceJarvis}.addNotification`,
|
|
2216
|
+
`{"title":"${this.config.titleJarvis} (${this.formatDate(new Date(), 'DD.MM.YYYY - hh:mm:ss')})","message": ${jsonText},"display": "drawer"}`,
|
|
2217
|
+
);
|
|
2218
|
+
}
|
|
2219
|
+
} catch (error) {
|
|
2220
|
+
this.log.error(`[sendNotification Jarvis] - ${error}`);
|
|
2221
|
+
}
|
|
2222
|
+
}
|
|
2223
|
+
|
|
2224
|
+
// Lovelace Notification
|
|
2225
|
+
if (this.config.instanceLovelace) {
|
|
2226
|
+
try {
|
|
2227
|
+
//first check if instance is living
|
|
2228
|
+
const lovelaceAliveState = await tools.getInitValue(this, `system.adapter.${this.config.instanceLovelace}.alive`);
|
|
2229
|
+
|
|
2230
|
+
if (!lovelaceAliveState) {
|
|
2231
|
+
this.log.warn('Lovelace instance is not running. Message could not be sent. Please check your instance configuration.');
|
|
2232
|
+
} else {
|
|
2233
|
+
const jsonText = JSON.stringify(text);
|
|
2234
|
+
await this.setForeignStateAsync(
|
|
2235
|
+
`${this.config.instanceLovelace}.notifications.add`,
|
|
2236
|
+
`{"message":${jsonText}, "title":"${this.config.titleLovelace} (${this.formatDate(new Date(), 'DD.MM.YYYY - hh:mm:ss')})"}`,
|
|
2237
|
+
);
|
|
2238
|
+
}
|
|
2239
|
+
} catch (error) {
|
|
2240
|
+
this.log.error(`[sendNotification Lovelace] - ${error}`);
|
|
2241
|
+
}
|
|
2242
|
+
}
|
|
2243
|
+
|
|
2244
|
+
// Synochat Notification
|
|
2245
|
+
if (this.config.instanceSynochat) {
|
|
2246
|
+
try {
|
|
2247
|
+
//first check if instance is living
|
|
2248
|
+
const synochatAliveState = await tools.getInitValue(this, `system.adapter.${this.config.instanceSynochat}.alive`);
|
|
2249
|
+
|
|
2250
|
+
if (!synochatAliveState) {
|
|
2251
|
+
this.log.warn('Synochat instance is not running. Message could not be sent. Please check your instance configuration.');
|
|
2252
|
+
} else {
|
|
2253
|
+
if (this.config.channelSynochat !== undefined) {
|
|
2254
|
+
await this.setForeignStateAsync(`${this.config.instanceSynochat}.${this.config.channelSynochat}.message`, text);
|
|
2255
|
+
} else {
|
|
2256
|
+
this.log.warn('Synochat channel is not set. Message could not be sent. Please check your instance configuration.');
|
|
2257
|
+
}
|
|
2258
|
+
}
|
|
2259
|
+
} catch (error) {
|
|
2260
|
+
this.log.error(`[sendNotification Synochat] - ${error}`);
|
|
2261
|
+
}
|
|
2262
|
+
}
|
|
2263
|
+
} // <-- End of sendNotification function
|
|
2264
|
+
|
|
2265
|
+
/*---------- Notifications ----------*/
|
|
2266
|
+
/**
|
|
2267
|
+
* Notifications on state changes
|
|
2268
|
+
*
|
|
2269
|
+
* @param {string} mainType
|
|
2270
|
+
* @param {string} type
|
|
2271
|
+
* @param {object} id
|
|
2272
|
+
* @param silent
|
|
2273
|
+
*/
|
|
2274
|
+
async sendStateNotifications(mainType, type, id, silent = false) {
|
|
2275
|
+
if (isUnloaded) {
|
|
2276
|
+
return;
|
|
2277
|
+
}
|
|
2278
|
+
let objectData;
|
|
2279
|
+
let adapterName;
|
|
2280
|
+
let list = '';
|
|
2281
|
+
let message = '';
|
|
2282
|
+
|
|
2283
|
+
if (id !== null) {
|
|
2284
|
+
if (mainType === 'Devices') {
|
|
2285
|
+
objectData = this.listAllDevicesRaw.get(id);
|
|
2286
|
+
adapterName = this.config.showAdapterNameinMsg ? `${objectData.Adapter}: ` : '';
|
|
2287
|
+
} else if (mainType === 'Instances') {
|
|
2288
|
+
objectData = this.listInstanceRaw.get(id);
|
|
2289
|
+
}
|
|
2290
|
+
}
|
|
2291
|
+
|
|
2292
|
+
const setMessage = async (message) => {
|
|
2293
|
+
this.log.info(message);
|
|
2294
|
+
await this.setStateAsync('lastNotification', message, true);
|
|
2295
|
+
await this.sendNotification(message, silent);
|
|
2296
|
+
};
|
|
2297
|
+
|
|
2298
|
+
switch (type) {
|
|
2299
|
+
case 'lowBatDevice':
|
|
2300
|
+
message = `${translations.Device_low_bat_detected[this.config.userSelectedLanguage]}: \n${adapterName} ${objectData.Device} (${objectData.Battery})`;
|
|
2301
|
+
await setMessage(message);
|
|
2302
|
+
break;
|
|
2303
|
+
|
|
2304
|
+
case 'onlineStateDevice':
|
|
2305
|
+
switch (objectData.Status) {
|
|
2306
|
+
case 'Online':
|
|
2307
|
+
message = `${translations.Device_available_again[this.config.userSelectedLanguage]}: \n${adapterName} ${objectData.Device} (${objectData.LastContact})`;
|
|
2308
|
+
break;
|
|
2309
|
+
|
|
2310
|
+
case 'Offline':
|
|
2311
|
+
message = `${translations.Device_not_reachable[this.config.userSelectedLanguage]}: \n${adapterName} ${objectData.Device} (${objectData.LastContact})`;
|
|
2312
|
+
break;
|
|
2313
|
+
}
|
|
2314
|
+
await setMessage(message);
|
|
2315
|
+
break;
|
|
2316
|
+
|
|
2317
|
+
case 'updateDevice':
|
|
2318
|
+
message = `${translations.Device_new_updates[this.config.userSelectedLanguage]}: \n${adapterName} ${objectData.Device}`;
|
|
2319
|
+
await setMessage(message);
|
|
2320
|
+
break;
|
|
2321
|
+
|
|
2322
|
+
case 'updateAdapter':
|
|
2323
|
+
if (this.countAdapterUpdates === 0) {
|
|
2324
|
+
return;
|
|
2325
|
+
}
|
|
2326
|
+
|
|
2327
|
+
objectData = this.listAdapterUpdates;
|
|
2328
|
+
list = '';
|
|
2329
|
+
|
|
2330
|
+
for (const id of objectData) {
|
|
2331
|
+
list = `${list}\n${id[translations.Adapter[this.config.userSelectedLanguage]]}: v${id[translations.Available_Version[this.config.userSelectedLanguage]]}`;
|
|
2332
|
+
}
|
|
2333
|
+
|
|
2334
|
+
message = `${translations.Adapter_new_updates[this.config.userSelectedLanguage]}: ${list}`;
|
|
2335
|
+
await setMessage(message);
|
|
2336
|
+
break;
|
|
2337
|
+
|
|
2338
|
+
case 'errorInstance':
|
|
2339
|
+
case 'deactivatedInstance':
|
|
2340
|
+
message = `${translations.Instance_Watchdog[this.config.userSelectedLanguage]}:\n${id}: ${objectData.status}`;
|
|
2341
|
+
await setMessage(message);
|
|
2342
|
+
break;
|
|
2343
|
+
}
|
|
2344
|
+
}
|
|
2345
|
+
|
|
2346
|
+
/**
|
|
2347
|
+
* Notifications per user defined schedule
|
|
2348
|
+
*
|
|
2349
|
+
* @param {string} type
|
|
2350
|
+
* @param silent
|
|
2351
|
+
*/
|
|
2352
|
+
async sendScheduleNotifications(type, silent = false) {
|
|
2353
|
+
if (isUnloaded) {
|
|
2354
|
+
return;
|
|
2355
|
+
}
|
|
2356
|
+
|
|
2357
|
+
const checkDays = [];
|
|
2358
|
+
const dayConfigKeys = ['Sunday', 'Monday', 'Tuesday', 'Wednesday', 'Thursday', 'Friday', 'Saturday'];
|
|
2359
|
+
let list = '';
|
|
2360
|
+
let message = '';
|
|
2361
|
+
|
|
2362
|
+
const setMessage = async (message) => {
|
|
2363
|
+
this.log.info(message);
|
|
2364
|
+
await this.setStateAsync('lastNotification', message, true);
|
|
2365
|
+
if (!message.includes('no updates')) {
|
|
2366
|
+
await this.sendNotification(message, silent);
|
|
2367
|
+
}
|
|
2368
|
+
};
|
|
2369
|
+
|
|
2370
|
+
const processDeviceList = (deviceList, property1, property2) => {
|
|
2371
|
+
list = '';
|
|
2372
|
+
for (const id of deviceList) {
|
|
2373
|
+
if (this.blacklistNotify.includes(id.Path)) {
|
|
2374
|
+
continue;
|
|
2375
|
+
}
|
|
2376
|
+
list += `\n${!this.config.showAdapterNameinMsg ? '' : `${id.Adapter}: `}${id[property1]}${property2 ? ` (${id[property2]})` : ''}`;
|
|
2377
|
+
}
|
|
2378
|
+
};
|
|
2379
|
+
|
|
2380
|
+
const processInstanceList = (instanceList, property) => {
|
|
2381
|
+
list = '';
|
|
2382
|
+
for (const id of instanceList) {
|
|
2383
|
+
if (this.blacklistInstancesNotify.includes(id[translations['Instance'][this.config.userSelectedLanguage]])) {
|
|
2384
|
+
continue;
|
|
2385
|
+
}
|
|
2386
|
+
list += `\n${id[translations['Instance'][this.config.userSelectedLanguage]]}${property ? `: ${id[property]}` : ''}`;
|
|
2387
|
+
}
|
|
2388
|
+
};
|
|
2389
|
+
|
|
2390
|
+
const processNotification = async (list, messageType) => {
|
|
2391
|
+
if (list.length === 0) {
|
|
2392
|
+
return;
|
|
2393
|
+
}
|
|
2394
|
+
|
|
2395
|
+
switch (checkDays.length) {
|
|
2396
|
+
case 1:
|
|
2397
|
+
message = `${translations.Weekly_overview[this.config.userSelectedLanguage]} ${translations[messageType][this.config.userSelectedLanguage]}: ${list}`;
|
|
2398
|
+
break;
|
|
2399
|
+
case 7:
|
|
2400
|
+
message = `${translations.Daily_overview[this.config.userSelectedLanguage]} ${translations[messageType][this.config.userSelectedLanguage]}: ${list}`;
|
|
2401
|
+
break;
|
|
2402
|
+
default:
|
|
2403
|
+
message = `${translations.Overview_of[this.config.userSelectedLanguage]} ${translations[messageType][this.config.userSelectedLanguage]}: ${list}`;
|
|
2404
|
+
break;
|
|
2405
|
+
}
|
|
2406
|
+
|
|
2407
|
+
await setMessage(message);
|
|
2408
|
+
};
|
|
2409
|
+
|
|
2410
|
+
switch (type) {
|
|
2411
|
+
case 'lowBatteryDevices':
|
|
2412
|
+
checkDays.push(...dayConfigKeys.map((day, index) => (this.config[`check${day}`] ? index : null)).filter((day) => day !== null));
|
|
2413
|
+
|
|
2414
|
+
if (checkDays.length === 0) {
|
|
2415
|
+
this.log.warn(`No days selected for daily low battery devices message. Please check the instance configuration!`);
|
|
2416
|
+
return;
|
|
2417
|
+
}
|
|
2418
|
+
this.log.debug(`Number of selected days for daily low battery devices message: ${checkDays.length}. Send Message on: ${checkDays.join(', ')} ...`);
|
|
2419
|
+
|
|
2420
|
+
schedule.scheduleJob(`1 ${this.config.checkSendBatteryTime.split(':').reverse().join(' ')} * * ${checkDays.join(',')}`, async () => {
|
|
2421
|
+
processDeviceList(this.batteryLowPoweredRaw, 'Device', 'Battery');
|
|
2422
|
+
|
|
2423
|
+
await processNotification(list, 'devices_low_bat');
|
|
2424
|
+
});
|
|
2425
|
+
break;
|
|
2426
|
+
|
|
2427
|
+
case 'offlineDevices':
|
|
2428
|
+
checkDays.push(...dayConfigKeys.map((day, index) => (this.config[`checkOffline${day}`] ? index : null)).filter((day) => day !== null));
|
|
2429
|
+
|
|
2430
|
+
if (checkDays.length === 0) {
|
|
2431
|
+
this.log.warn(`No days selected for daily offline devices message. Please check the instance configuration!`);
|
|
2432
|
+
return;
|
|
2433
|
+
}
|
|
2434
|
+
this.log.debug(`Number of selected days for daily offline devices message: ${checkDays.length}. Send Message on: ${checkDays.join(', ')} ...`);
|
|
2435
|
+
|
|
2436
|
+
schedule.scheduleJob(`2 ${this.config.checkSendOfflineTime.split(':').reverse().join(' ')} * * ${checkDays.join(',')}`, async () => {
|
|
2437
|
+
processDeviceList(this.offlineDevicesRaw, `Device`, 'LastContact');
|
|
2438
|
+
|
|
2439
|
+
await processNotification(list, 'offline_devices');
|
|
2440
|
+
});
|
|
2441
|
+
break;
|
|
2442
|
+
|
|
2443
|
+
case 'updateDevices':
|
|
2444
|
+
checkDays.push(...dayConfigKeys.map((day, index) => (this.config[`checkUpgrade${day}`] ? index : null)).filter((day) => day !== null));
|
|
2445
|
+
|
|
2446
|
+
if (checkDays.length === 0) {
|
|
2447
|
+
this.log.warn(`No days selected for daily updatable devices message. Please check the instance configuration!`);
|
|
2448
|
+
return;
|
|
2449
|
+
}
|
|
2450
|
+
this.log.debug(`Number of selected days for daily updatable devices message: ${checkDays.length}. Send Message on: ${checkDays.join(', ')} ...`);
|
|
2451
|
+
|
|
2452
|
+
schedule.scheduleJob(`3 ${this.config.checkSendUpgradeTime.split(':').reverse().join(' ')} * * ${checkDays.join(',')}`, async () => {
|
|
2453
|
+
processDeviceList(this.upgradableDevicesRaw, 'Device');
|
|
2454
|
+
|
|
2455
|
+
await processNotification(list, 'available_updatable_devices');
|
|
2456
|
+
});
|
|
2457
|
+
break;
|
|
2458
|
+
|
|
2459
|
+
case 'updateAdapter':
|
|
2460
|
+
checkDays.push(...dayConfigKeys.map((day, index) => (this.config[`checkAdapterUpdate${day}`] ? index : null)).filter((day) => day !== null));
|
|
2461
|
+
|
|
2462
|
+
if (checkDays.length === 0) {
|
|
2463
|
+
this.log.warn(`No days selected for daily adapter update message. Please check the instance configuration!`);
|
|
2464
|
+
return;
|
|
2465
|
+
}
|
|
2466
|
+
this.log.debug(`Number of selected days for daily adapter update message: ${checkDays.length}. Send Message on: ${checkDays.join(', ')} ...`);
|
|
2467
|
+
|
|
2468
|
+
schedule.scheduleJob(`4 ${this.config.checkSendAdapterUpdateTime.split(':').reverse().join(' ')} * * ${checkDays.join(',')}`, async () => {
|
|
2469
|
+
list = '';
|
|
2470
|
+
for (const id of this.listAdapterUpdates) {
|
|
2471
|
+
list = `${list}\n${id[translations.Adapter[this.config.userSelectedLanguage]]}: v${id[translations.Available_Version[this.config.userSelectedLanguage]]}`;
|
|
2472
|
+
}
|
|
2473
|
+
await processNotification(list, 'available_adapter_updates');
|
|
2474
|
+
});
|
|
2475
|
+
break;
|
|
2476
|
+
|
|
2477
|
+
case 'errorInstance':
|
|
2478
|
+
checkDays.push(...dayConfigKeys.map((day, index) => (this.config[`checkFailedInstances${day}`] ? index : null)).filter((day) => day !== null));
|
|
2479
|
+
|
|
2480
|
+
if (checkDays.length === 0) {
|
|
2481
|
+
this.log.warn(`No days selected for daily instance error message. Please check the instance configuration!`);
|
|
2482
|
+
return;
|
|
2483
|
+
}
|
|
2484
|
+
this.log.debug(`Number of selected days for daily instance error message: ${checkDays.length}. Send Message on: ${checkDays.join(', ')} ...`);
|
|
2485
|
+
|
|
2486
|
+
schedule.scheduleJob(`5 ${this.config.checkSendInstanceFailedTime.split(':').reverse().join(' ')} * * ${checkDays.join(',')}`, async () => {
|
|
2487
|
+
processInstanceList(this.listErrorInstanceRaw, 'Status');
|
|
2488
|
+
|
|
2489
|
+
await processNotification(list, 'error_instances_msg');
|
|
2490
|
+
});
|
|
2491
|
+
break;
|
|
2492
|
+
|
|
2493
|
+
case 'deactivatedInstance':
|
|
2494
|
+
checkDays.push(...dayConfigKeys.map((day, index) => (this.config[`checkInstanceDeactivated${day}`] ? index : null)).filter((day) => day !== null));
|
|
2495
|
+
|
|
2496
|
+
if (checkDays.length === 0) {
|
|
2497
|
+
this.log.warn(`No days selected for daily instance deactivated message. Please check the instance configuration!`);
|
|
2498
|
+
return;
|
|
2499
|
+
}
|
|
2500
|
+
this.log.debug(`Number of selected days for daily instance deactivated message: ${checkDays.length}. Send Message on: ${checkDays.join(', ')} ...`);
|
|
2501
|
+
|
|
2502
|
+
schedule.scheduleJob(`5 ${this.config.checkSendInstanceDeactivatedTime.split(':').reverse().join(' ')} * * ${checkDays.join(',')}`, async () => {
|
|
2503
|
+
processInstanceList(this.listDeactivatedInstances);
|
|
2504
|
+
|
|
2505
|
+
await processNotification(list, 'deactivated_instances_msg');
|
|
2506
|
+
});
|
|
2507
|
+
break;
|
|
2508
|
+
}
|
|
2509
|
+
}
|
|
2510
|
+
|
|
2511
|
+
async getPreviousCronRun(lastCronRun) {
|
|
2512
|
+
try {
|
|
2513
|
+
let interval;
|
|
2514
|
+
// cron-parser v4: parseExpression() – v5: CronExpressionParser.parse()
|
|
2515
|
+
if (typeof cronParserLib.parseExpression === 'function') {
|
|
2516
|
+
interval = cronParserLib.parseExpression(lastCronRun);
|
|
2517
|
+
} else if (cronParserLib.CronExpressionParser && typeof cronParserLib.CronExpressionParser.parse === 'function') {
|
|
2518
|
+
interval = cronParserLib.CronExpressionParser.parse(lastCronRun);
|
|
2519
|
+
} else {
|
|
2520
|
+
throw new Error('cron-parser: no compatible API found (parseExpression / CronExpressionParser.parse)');
|
|
2521
|
+
}
|
|
2522
|
+
const previous = interval.prev();
|
|
2523
|
+
|
|
2524
|
+
// Differenz in ms seit dem vorherigen Cron-Zeitpunkt
|
|
2525
|
+
return Date.now() - previous.getTime();
|
|
2526
|
+
} catch (error) {
|
|
2527
|
+
this.log.error(`[getPreviousCronRun] - ${error}`);
|
|
2528
|
+
return null;
|
|
2529
|
+
}
|
|
2530
|
+
}
|
|
2531
|
+
|
|
2532
|
+
|
|
2533
|
+
/**
|
|
2534
|
+
* @param {() => void} callback
|
|
2535
|
+
*/
|
|
2536
|
+
onUnload(callback) {
|
|
2537
|
+
try {
|
|
2538
|
+
this.log.debug('clearing timeouts');
|
|
2539
|
+
|
|
2540
|
+
isUnloaded = true;
|
|
2541
|
+
|
|
2542
|
+
if (this.refreshDataTimeout) {
|
|
2543
|
+
this.clearTimeout(this.refreshDataTimeout);
|
|
2544
|
+
this.refreshDataTimeout = null;
|
|
2545
|
+
}
|
|
2546
|
+
|
|
2547
|
+
this.log.info('cleaned everything up...');
|
|
2548
|
+
|
|
2549
|
+
callback();
|
|
2550
|
+
} catch (e) {
|
|
2551
|
+
callback();
|
|
2552
|
+
}
|
|
2553
|
+
}
|
|
2470
2554
|
}
|
|
2471
2555
|
|
|
2472
2556
|
if (require.main !== module) {
|
|
2473
|
-
|
|
2474
|
-
|
|
2475
|
-
|
|
2476
|
-
|
|
2477
|
-
|
|
2557
|
+
// Export the constructor in compact mode
|
|
2558
|
+
/**
|
|
2559
|
+
* @param {Partial<utils.AdapterOptions>} [options]
|
|
2560
|
+
*/
|
|
2561
|
+
module.exports = (options) => new DeviceWatcher(options);
|
|
2478
2562
|
} else {
|
|
2479
|
-
|
|
2480
|
-
|
|
2563
|
+
// otherwise start the instance directly
|
|
2564
|
+
new DeviceWatcher();
|
|
2481
2565
|
}
|