iobroker.zigbee 2.0.5 → 3.0.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +22 -3
- package/admin/admin.js +420 -115
- package/admin/index_m.html +285 -229
- package/admin/tab_m.html +108 -91
- package/docs/de/img/Bild30.png +0 -0
- package/docs/de/img/Bild38.png +0 -0
- package/docs/de/img/Info.png +0 -0
- package/docs/de/img/Zigbee_config_de.jpg +0 -0
- package/docs/de/img/battery.png +0 -0
- package/docs/de/img/debug.png +0 -0
- package/docs/de/img/delete.png +0 -0
- package/docs/de/img/disconnected.png +0 -0
- package/docs/de/img/edit_grp.png +0 -0
- package/docs/de/img/edit_image.png +0 -0
- package/docs/de/img/grp_nok.png +0 -0
- package/docs/de/img/grp_ok.png +0 -0
- package/docs/de/img/on_off.png +0 -0
- package/docs/de/img/reconfigure.png +0 -0
- package/docs/de/readme.md +52 -43
- package/docs/en/img/Zigbee_config_en.png +0 -0
- package/docs/en/img/Zigbee_pairing_en.png +0 -0
- package/docs/en/readme.md +66 -66
- package/io-package.json +32 -31
- package/lib/DeviceDebug.js +2 -1
- package/lib/commands.js +203 -40
- package/lib/devices.js +2 -2
- package/lib/exposes.js +8 -30
- package/lib/groups.js +1 -1
- package/lib/localConfig.js +33 -10
- package/lib/networkmap.js +2 -1
- package/lib/seriallist.js +9 -2
- package/lib/statescontroller.js +185 -91
- package/lib/utils.js +41 -11
- package/lib/zbDeviceConfigure.js +10 -3
- package/lib/zigbeecontroller.js +121 -94
- package/main.js +135 -73
- package/package.json +8 -8
- package/docs/en/img/Bild23.png +0 -0
- package/docs/en/img/Bild25.png +0 -0
- package/docs/en/img/Bild26.png +0 -0
- package/docs/en/img/Bild4.png +0 -0
- package/docs/en/img/Bild9.png +0 -0
package/admin/admin.js
CHANGED
|
@@ -32,7 +32,9 @@ let devices = [],
|
|
|
32
32
|
shuffleInstance,
|
|
33
33
|
errorData = [],
|
|
34
34
|
debugMessages = {},
|
|
35
|
-
debugInLog = true
|
|
35
|
+
debugInLog = true,
|
|
36
|
+
nvRamBackup = {},
|
|
37
|
+
isHerdsmanRunning = false;
|
|
36
38
|
const dbgMsgfilter = new Set();
|
|
37
39
|
const dbgMsghide = new Set();
|
|
38
40
|
const updateCardInterval = setInterval(updateCardTimer, 6000);
|
|
@@ -55,11 +57,12 @@ const networkOptions = {
|
|
|
55
57
|
|
|
56
58
|
const savedSettings = [
|
|
57
59
|
'port', 'panID', 'channel', 'disableLed', 'countDown', 'groups', 'extPanID', 'precfgkey', 'transmitPower',
|
|
58
|
-
'adapterType', 'debugHerdsman', 'disableBackup', 'disablePing', 'external', 'startWithInconsistent',
|
|
60
|
+
'adapterType', 'debugHerdsman', 'disableBackup', 'disablePing', 'external', 'startWithInconsistent',
|
|
61
|
+
'warnOnDeviceAnnouncement', 'baudRate', 'flowCTRL', 'autostart'
|
|
59
62
|
];
|
|
60
63
|
|
|
61
64
|
function getDeviceByID(ID) {
|
|
62
|
-
return devices.find((devInfo) => {
|
|
65
|
+
if (devices) return devices.find((devInfo) => {
|
|
63
66
|
try {
|
|
64
67
|
return devInfo._id == ID;
|
|
65
68
|
} catch (e) {
|
|
@@ -380,6 +383,31 @@ function deleteConfirmation(id, name) {
|
|
|
380
383
|
Materialize.updateTextFields();
|
|
381
384
|
}
|
|
382
385
|
|
|
386
|
+
function deleteNvBackupConfirmation() {
|
|
387
|
+
const text = translateWord('Do you really want to the NV Backup data ?');
|
|
388
|
+
$('#modaldelete').find('p').text(text);
|
|
389
|
+
$('#force').prop('checked', false);
|
|
390
|
+
$('#forcediv').addClass('hide');
|
|
391
|
+
$('#modaldelete a.btn[name=\'yes\']').unbind('click');
|
|
392
|
+
$('#modaldelete a.btn[name=\'yes\']').click(() => {
|
|
393
|
+
//const force = $('#force').prop('checked');
|
|
394
|
+
showWaitingDialog('Attempting to delete nvBackup.json', 60000);
|
|
395
|
+
sendTo(namespace, 'deleteNVBackup', {}, function (msg) {
|
|
396
|
+
closeWaitingDialog();
|
|
397
|
+
if (msg) {
|
|
398
|
+
if (msg.error) {
|
|
399
|
+
showMessage(msg.error, _('Error'));
|
|
400
|
+
} else {
|
|
401
|
+
getDevices();
|
|
402
|
+
}
|
|
403
|
+
}
|
|
404
|
+
});
|
|
405
|
+
});
|
|
406
|
+
$('#modaldelete').modal('open');
|
|
407
|
+
Materialize.updateTextFields();
|
|
408
|
+
}
|
|
409
|
+
|
|
410
|
+
|
|
383
411
|
function cleanConfirmation() {
|
|
384
412
|
const text = translateWord('Do you really want to remove orphaned states?');
|
|
385
413
|
$('#modalclean').find('p').text(text);
|
|
@@ -401,7 +429,81 @@ function EndPointIDfromEndPoint(ep) {
|
|
|
401
429
|
return 'unidentified';
|
|
402
430
|
}
|
|
403
431
|
|
|
432
|
+
|
|
433
|
+
|
|
404
434
|
function editName(id, name) {
|
|
435
|
+
|
|
436
|
+
const device_options = {};
|
|
437
|
+
const received_options = {};
|
|
438
|
+
|
|
439
|
+
function removeOption(k) {
|
|
440
|
+
if (k && device_options.hasOwnProperty(k)) delete device_options[k];
|
|
441
|
+
}
|
|
442
|
+
|
|
443
|
+
function addOption() {
|
|
444
|
+
let idx=1;
|
|
445
|
+
let key = '';
|
|
446
|
+
do {
|
|
447
|
+
key = `o${idx++}`;
|
|
448
|
+
}
|
|
449
|
+
while (device_options.hasOwnProperty(key));
|
|
450
|
+
device_options[key] = {key:`option_${idx++}`, value:''};
|
|
451
|
+
}
|
|
452
|
+
|
|
453
|
+
function updateOptions() {
|
|
454
|
+
const html_options=[];
|
|
455
|
+
|
|
456
|
+
console.warn(`device_options is ${JSON.stringify(device_options)}`)
|
|
457
|
+
|
|
458
|
+
for (const k in device_options) {
|
|
459
|
+
html_options.push(`<div class="row">`);
|
|
460
|
+
html_options.push(`<div class="input-field suffix col s5 m5 l5"><input id="option_key_${k}" type="text" class="value" /><label for="option_key_${k}">Option</label></div>`)
|
|
461
|
+
html_options.push(`<div class="input-field suffix col s5 m5 l5"><input id="option_value_${k}" type="text" class="value" /><label for="option_value_${k}">Value</label></div>`)
|
|
462
|
+
html_options.push(`<div class="col"><a id="option_rem_${k}" class='btn' ><i class="material-icons">remove_circle</i></a></div>`);
|
|
463
|
+
html_options.push(`</div>`)
|
|
464
|
+
}
|
|
465
|
+
console.warn(`html is ${$('#modaledit').find('.options_grid').html()}`)
|
|
466
|
+
$('#modaledit').find('.options_grid').html(html_options.join(''));
|
|
467
|
+
console.warn(`html is now ${$('#modaledit').find('.options_grid').html()}`)
|
|
468
|
+
if (html_options.length > 0) {
|
|
469
|
+
$('#modaledit').find('.options_available').removeClass('hide');
|
|
470
|
+
for (const k of Object.keys(device_options)) {
|
|
471
|
+
$(`#option_key_${k}`).val(device_options[k].key);
|
|
472
|
+
$(`#option_value_${k}`).val(device_options[k].value);
|
|
473
|
+
$(`#option_rem_${k}`).unbind('click');
|
|
474
|
+
$(`#option_rem_${k}`).click(() => { removeOption(k); updateOptions() });
|
|
475
|
+
}
|
|
476
|
+
}
|
|
477
|
+
else {
|
|
478
|
+
$('#modaledit').find('.options_available').addClass('hide');
|
|
479
|
+
}
|
|
480
|
+
}
|
|
481
|
+
|
|
482
|
+
function getOptionsFromUI(_do, _so) {
|
|
483
|
+
const _no = {};
|
|
484
|
+
let changed = false;
|
|
485
|
+
for (const k in _do) {
|
|
486
|
+
const key = $(`#option_key_${k}`).val();
|
|
487
|
+
_do[k].key = key;
|
|
488
|
+
const val = $(`#option_value_${k}`).val();
|
|
489
|
+
try {
|
|
490
|
+
_do[k].value = JSON.parse(val);
|
|
491
|
+
}
|
|
492
|
+
catch {
|
|
493
|
+
_do[k].value = val;
|
|
494
|
+
}
|
|
495
|
+
if (device_options[k].key.length > 0) {
|
|
496
|
+
_no[key] = device_options[k].value;
|
|
497
|
+
changed |= _no[key] != _so[key];
|
|
498
|
+
}
|
|
499
|
+
}
|
|
500
|
+
changed |= (Object.keys(_no).length != Object.keys(_so).length);
|
|
501
|
+
if (changed) return _no;
|
|
502
|
+
return undefined;
|
|
503
|
+
}
|
|
504
|
+
|
|
505
|
+
|
|
506
|
+
|
|
405
507
|
console.warn('editName called with ' + id + ' and ' + name);
|
|
406
508
|
const dev = devices.find((d) => d._id == id);
|
|
407
509
|
$('#modaledit').find('input[id=\'d_name\']').val(name);
|
|
@@ -414,14 +516,16 @@ function editName(id, name) {
|
|
|
414
516
|
}
|
|
415
517
|
}
|
|
416
518
|
const numEP = groupables.length;
|
|
417
|
-
|
|
418
|
-
$('#modaledit').find('.row.epid1').addClass('hide');
|
|
419
|
-
$('#modaledit').find('.row.epid2').addClass('hide');
|
|
420
|
-
$('#modaledit').find('.row.epid3').addClass('hide');
|
|
421
|
-
$('#modaledit').find('.row.epid4').addClass('hide');
|
|
422
|
-
$('#modaledit').find('.row.epid5').addClass('hide');
|
|
423
|
-
$('#modaledit').find('.row.epid6').addClass('hide');
|
|
519
|
+
|
|
424
520
|
if (numEP > 0) {
|
|
521
|
+
$('#modaledit').find('.groups_available').removeClass('hide');
|
|
522
|
+
$('#modaledit').find('.row.epid0').addClass('hide');
|
|
523
|
+
$('#modaledit').find('.row.epid1').addClass('hide');
|
|
524
|
+
$('#modaledit').find('.row.epid2').addClass('hide');
|
|
525
|
+
$('#modaledit').find('.row.epid3').addClass('hide');
|
|
526
|
+
$('#modaledit').find('.row.epid4').addClass('hide');
|
|
527
|
+
$('#modaledit').find('.row.epid5').addClass('hide');
|
|
528
|
+
$('#modaledit').find('.row.epid6').addClass('hide');
|
|
425
529
|
// go through all the groups. Find the ones to list for each groupable
|
|
426
530
|
if (numEP == 1) {
|
|
427
531
|
$('#modaledit').find('.endpointid').addClass('hide');
|
|
@@ -451,7 +555,37 @@ function editName(id, name) {
|
|
|
451
555
|
list2select('#d_groups_ep' + i, groups, groupables[i].memberOf || []);
|
|
452
556
|
}
|
|
453
557
|
}
|
|
558
|
+
else
|
|
559
|
+
{
|
|
560
|
+
$('#modaledit').find('.groups_available').addClass('hide');
|
|
561
|
+
}
|
|
562
|
+
sendTo(namespace, 'getLocalConfigItems', { target:id, global:false, key:'options' }, function (msg) {
|
|
563
|
+
if (msg) {
|
|
564
|
+
if (msg.error) showMessage(msg.error, '_Error');
|
|
565
|
+
console.warn(`return is ${msg}`)
|
|
566
|
+
Object.keys(device_options).forEach(key => delete device_options[key]);
|
|
567
|
+
Object.keys(received_options).forEach(key => delete received_options[key]);
|
|
568
|
+
if (typeof msg.options === 'object') {
|
|
569
|
+
|
|
570
|
+
let cnt = 1;
|
|
571
|
+
for (const key in msg.options)
|
|
572
|
+
{
|
|
573
|
+
received_options[key]=msg.options[key];
|
|
574
|
+
device_options[`o${cnt}`] = { key:key, value:msg.options[key]}
|
|
575
|
+
cnt++;
|
|
576
|
+
}
|
|
577
|
+
}
|
|
578
|
+
updateOptions();
|
|
579
|
+
|
|
580
|
+
} else showMessage('callback without message');
|
|
581
|
+
});
|
|
454
582
|
$('#modaledit a.btn[name=\'save\']').unbind('click');
|
|
583
|
+
$('#modaledit a.btn[name=\'add_options\']').unbind('click');
|
|
584
|
+
$('#modaledit a.btn[name=\'add_options\']').click(() => {
|
|
585
|
+
getOptionsFromUI(device_options, received_options);
|
|
586
|
+
addOption();
|
|
587
|
+
updateOptions()
|
|
588
|
+
});
|
|
455
589
|
$('#modaledit a.btn[name=\'save\']').click(() => {
|
|
456
590
|
const newName = $('#modaledit').find('input[id=\'d_name\']').val();
|
|
457
591
|
const groupsbyid = {};
|
|
@@ -462,8 +596,21 @@ function editName(id, name) {
|
|
|
462
596
|
groupsbyid[groupables[i].ep.ID] = GenerateGroupChange(groupables[i].memberOf, ng);
|
|
463
597
|
}
|
|
464
598
|
}
|
|
465
|
-
|
|
599
|
+
// read device_options from UI
|
|
600
|
+
const co = getOptionsFromUI(device_options, received_options)
|
|
601
|
+
console.warn(`options have ${co ? 'changed' : 'not changed'} : ${JSON.stringify(co)} vs ${JSON.stringify(received_options)} , saving them`);
|
|
602
|
+
if (co) {
|
|
603
|
+
sendTo(namespace, 'updateLocalConfigItems', {
|
|
604
|
+
target: id,
|
|
605
|
+
global:false,
|
|
606
|
+
data: { options:co }
|
|
607
|
+
},
|
|
608
|
+
function (msg) {
|
|
609
|
+
if (msg && msg.error) showMessage(msg.error, '_Error');
|
|
610
|
+
});
|
|
611
|
+
}
|
|
466
612
|
updateDev(id, newName, groupsbyid);
|
|
613
|
+
|
|
467
614
|
});
|
|
468
615
|
$('#modaledit').modal('open');
|
|
469
616
|
Materialize.updateTextFields();
|
|
@@ -782,18 +929,27 @@ function getCoordinatorInfo() {
|
|
|
782
929
|
sendTo(namespace, 'getCoordinatorInfo', {}, function (msg) {
|
|
783
930
|
if (msg) {
|
|
784
931
|
if (msg.error) {
|
|
785
|
-
|
|
932
|
+
errorData.push(msg.error);
|
|
933
|
+
isHerdsmanRunning = false;
|
|
934
|
+
updateStartButton();
|
|
786
935
|
} else {
|
|
787
936
|
coordinatorinfo = msg;
|
|
937
|
+
isHerdsmanRunning = true;
|
|
938
|
+
updateStartButton()
|
|
788
939
|
}
|
|
789
940
|
}
|
|
790
941
|
});
|
|
791
942
|
}
|
|
943
|
+
|
|
792
944
|
function checkDebugDevice(id) {
|
|
793
|
-
|
|
945
|
+
// returns: -1: debug not set
|
|
946
|
+
// 0: debug set explicitly
|
|
947
|
+
// > 0: debug set by pattern.
|
|
948
|
+
if (!debugDevices) return -1;
|
|
949
|
+
if (debugDevices.indexOf(id) > -1) return 0 // debug set
|
|
794
950
|
for (const addressPart of debugDevices) {
|
|
795
951
|
if (typeof id === 'string' && id.includes(addressPart)) {
|
|
796
|
-
return debugDevices.indexOf(addressPart)+1;
|
|
952
|
+
return debugDevices.indexOf(addressPart)+1; // debug set by pattern (>0)
|
|
797
953
|
}
|
|
798
954
|
}
|
|
799
955
|
return -1;
|
|
@@ -811,8 +967,8 @@ async function toggleDebugDevice(id) {
|
|
|
811
967
|
});
|
|
812
968
|
}
|
|
813
969
|
|
|
814
|
-
function
|
|
815
|
-
sendTo(namespace, '
|
|
970
|
+
function updateLocalConfigItems(device, data, global) {
|
|
971
|
+
sendTo(namespace, 'updateLocalConfigItems', {target: device, data:data, global:global}, function(msg) {
|
|
816
972
|
if (msg && msg.hasOwnProperty.error) {
|
|
817
973
|
showMessage(msg.error, _('Error'));
|
|
818
974
|
}
|
|
@@ -861,7 +1017,7 @@ async function selectImageOverride(id) {
|
|
|
861
1017
|
const data = {};
|
|
862
1018
|
if (image != 'current') data.icon= image;
|
|
863
1019
|
if (name != dev.common.name) data.name = name;
|
|
864
|
-
|
|
1020
|
+
updateLocalConfigItems(id, data, global);
|
|
865
1021
|
});
|
|
866
1022
|
$('#chooseimage').modal('open');
|
|
867
1023
|
Materialize.updateTextFields();
|
|
@@ -985,6 +1141,7 @@ function displayDebugMessages(msg) {
|
|
|
985
1141
|
Html.push('</tbody></table></li>')
|
|
986
1142
|
for (const devID of Object.keys(dbgData)) {
|
|
987
1143
|
const dev = devices.find((d) => d._id.endsWith(devID.slice(-16)));
|
|
1144
|
+
if (!dev) continue;
|
|
988
1145
|
const type_url = (dev && dev.common && dev.common.type ? sanitizeModelParameter(dev.common.type) : 'unknown');
|
|
989
1146
|
const image = `<img src="${dev.common.icon || dev.icon}" width="40px" onerror="this.onerror=null;this.src='img/unavailable.png';">`
|
|
990
1147
|
const modelUrl = (type_url === 'unknown') ? 'unknown' : `<a href="https://www.zigbee2mqtt.io/devices/${type_url}.html" target="_blank" rel="noopener noreferrer">${image}</a>`;
|
|
@@ -1067,8 +1224,10 @@ function getDebugMessages() {
|
|
|
1067
1224
|
|
|
1068
1225
|
function getDevices() {
|
|
1069
1226
|
getCoordinatorInfo();
|
|
1070
|
-
sendTo(namespace, '
|
|
1227
|
+
sendTo(namespace, 'getDevices', {}, function (msg) {
|
|
1071
1228
|
if (msg) {
|
|
1229
|
+
devices = msg.devices ? msg.devices : [];
|
|
1230
|
+
// check if stashed error messages are sent alongside
|
|
1072
1231
|
if (msg.clean)
|
|
1073
1232
|
$('#state_cleanup_btn').removeClass('hide');
|
|
1074
1233
|
else
|
|
@@ -1077,23 +1236,28 @@ function getDevices() {
|
|
|
1077
1236
|
$('#show_errors_btn').removeClass('hide');
|
|
1078
1237
|
errorData = msg.errors;
|
|
1079
1238
|
}
|
|
1080
|
-
else
|
|
1239
|
+
else {
|
|
1081
1240
|
$('#show_errors_btn').addClass('hide');
|
|
1082
|
-
|
|
1083
|
-
|
|
1084
|
-
|
|
1085
|
-
|
|
1086
|
-
|
|
1087
|
-
|
|
1088
|
-
|
|
1089
|
-
|
|
1090
|
-
|
|
1091
|
-
|
|
1092
|
-
|
|
1241
|
+
}
|
|
1242
|
+
|
|
1243
|
+
//check if debug messages are sent alongside
|
|
1244
|
+
if (msg && typeof (msg.debugDevices == 'array')) {
|
|
1245
|
+
debugDevices = msg.debugDevices;
|
|
1246
|
+
console.warn('debug devices is sent')
|
|
1247
|
+
}
|
|
1248
|
+
else
|
|
1249
|
+
debugDevices = [];
|
|
1250
|
+
if (debugMessages.byId) {
|
|
1251
|
+
debugMessages.byId = msg;
|
|
1252
|
+
if (msg) displayDebugMessages(debugMessages)
|
|
1253
|
+
}
|
|
1093
1254
|
if (msg.error) {
|
|
1094
|
-
|
|
1255
|
+
errorData.push(msg.error);
|
|
1256
|
+
isHerdsmanRunning = false;
|
|
1257
|
+
updateStartButton();
|
|
1095
1258
|
} else {
|
|
1096
|
-
|
|
1259
|
+
isHerdsmanRunning = true;
|
|
1260
|
+
updateStartButton();
|
|
1097
1261
|
showDevices();
|
|
1098
1262
|
getDebugMessages();
|
|
1099
1263
|
getExclude();
|
|
@@ -1128,8 +1292,12 @@ function getMap() {
|
|
|
1128
1292
|
$('#refresh').removeClass('disabled');
|
|
1129
1293
|
if (msg) {
|
|
1130
1294
|
if (msg.error) {
|
|
1131
|
-
|
|
1295
|
+
errorData.push(msg.error);
|
|
1296
|
+
isHerdsmanRunning = false;
|
|
1297
|
+
updateStartButton();
|
|
1132
1298
|
} else {
|
|
1299
|
+
isHerdsmanRunning = true;
|
|
1300
|
+
updateStartButton();
|
|
1133
1301
|
if (msg.errors.length > 0 && $('#errorCollectionOn').is(':checked')) {
|
|
1134
1302
|
showMessage(msg.errors.join('<p>'), 'Map generation messages');
|
|
1135
1303
|
}
|
|
@@ -1149,14 +1317,24 @@ function getRandomExtPanID()
|
|
|
1149
1317
|
return bytes.join('');
|
|
1150
1318
|
}
|
|
1151
1319
|
|
|
1320
|
+
function getRandomChannel()
|
|
1321
|
+
{
|
|
1322
|
+
const channels = [11,15,20,25]
|
|
1323
|
+
return channels[Math.floor(Math.random() * 4)];
|
|
1324
|
+
}
|
|
1325
|
+
|
|
1326
|
+
|
|
1152
1327
|
|
|
1153
1328
|
// the function loadSettings has to exist ...
|
|
1154
1329
|
|
|
1155
1330
|
function load(settings, onChange) {
|
|
1156
|
-
if (settings.
|
|
1331
|
+
if (settings.extPanID === undefined || settings.extPanID == '') {
|
|
1332
|
+
settings.channel = getRandomChannel();
|
|
1333
|
+
}
|
|
1334
|
+
if (settings.panID === undefined || settings.panID == 0) {
|
|
1157
1335
|
settings.panID = Math.floor(Math.random() * 10000);
|
|
1158
1336
|
}
|
|
1159
|
-
if (settings.extPanID === undefined) {
|
|
1337
|
+
if (settings.extPanID === undefined || settings.extPanID == '') {
|
|
1160
1338
|
settings.extPanID = getRandomExtPanID();
|
|
1161
1339
|
}
|
|
1162
1340
|
// fix for previous wrong value
|
|
@@ -1167,9 +1345,6 @@ function load(settings, onChange) {
|
|
|
1167
1345
|
if (settings.precfgkey === undefined) {
|
|
1168
1346
|
settings.precfgkey = '01030507090B0D0F00020406080A0C0D';
|
|
1169
1347
|
}
|
|
1170
|
-
if (settings.channel === undefined) {
|
|
1171
|
-
settings.channel = 11;
|
|
1172
|
-
}
|
|
1173
1348
|
if (settings.disablePing === undefined) {
|
|
1174
1349
|
settings.disablePing = false;
|
|
1175
1350
|
}
|
|
@@ -1179,6 +1354,7 @@ function load(settings, onChange) {
|
|
|
1179
1354
|
if (settings.baudRate === undefined) {
|
|
1180
1355
|
settings.baudRate = 115200;
|
|
1181
1356
|
}
|
|
1357
|
+
if (settings.autostart === undefined) settings.autostart = false;
|
|
1182
1358
|
|
|
1183
1359
|
// example: select elements with id=key and class=value and insert value
|
|
1184
1360
|
for (const key in settings) {
|
|
@@ -1190,10 +1366,12 @@ function load(settings, onChange) {
|
|
|
1190
1366
|
if (value.attr('type') === 'checkbox') {
|
|
1191
1367
|
value.prop('checked', settings[key]).change(function () {
|
|
1192
1368
|
onChange();
|
|
1369
|
+
validateNVRamBackup(false, key)
|
|
1193
1370
|
});
|
|
1194
1371
|
} else {
|
|
1195
1372
|
value.val(settings[key]).change(function () {
|
|
1196
1373
|
onChange();
|
|
1374
|
+
validateNVRamBackup(false, key)
|
|
1197
1375
|
}).keyup(function () {
|
|
1198
1376
|
$(this).trigger('change');
|
|
1199
1377
|
});
|
|
@@ -1206,6 +1384,7 @@ function load(settings, onChange) {
|
|
|
1206
1384
|
//dialog = new MatDialog({EndingTop: '50%'});
|
|
1207
1385
|
getDevices();
|
|
1208
1386
|
getNamedColors();
|
|
1387
|
+
readNVRamBackup(false);
|
|
1209
1388
|
//getDebugMessages();
|
|
1210
1389
|
//getMap();
|
|
1211
1390
|
//addCard();
|
|
@@ -1213,6 +1392,36 @@ function load(settings, onChange) {
|
|
|
1213
1392
|
// Signal to admin, that no changes yet
|
|
1214
1393
|
onChange(false);
|
|
1215
1394
|
|
|
1395
|
+
$('#test-btn').click(function () {
|
|
1396
|
+
console.warn(`isHerdsmanRunning: ${isHerdsmanRunning}`)
|
|
1397
|
+
if (!isHerdsmanRunning) {
|
|
1398
|
+
const port = $('#port.value').val();
|
|
1399
|
+
console.warn(`port is ${port}`)
|
|
1400
|
+
showWaitingDialog(`Trying to connect to ${port}`, 300);
|
|
1401
|
+
sendTo(namespace, 'testConnection', { address:port }, function(msg) {
|
|
1402
|
+
console.warn(`send to returned with ${JSON.stringify(msg)}`);
|
|
1403
|
+
closeWaitingDialog();
|
|
1404
|
+
if (msg) {
|
|
1405
|
+
if (msg.error) {
|
|
1406
|
+
showMessage(msg.error, _('Error'));
|
|
1407
|
+
}
|
|
1408
|
+
}
|
|
1409
|
+
})
|
|
1410
|
+
}
|
|
1411
|
+
else {
|
|
1412
|
+
showMessage('function unavailable while herdsman is running', _('Error'))
|
|
1413
|
+
}
|
|
1414
|
+
});
|
|
1415
|
+
|
|
1416
|
+
$('#readNVRam-btn').click(function() {
|
|
1417
|
+
readNVRamBackup(true);
|
|
1418
|
+
})
|
|
1419
|
+
// test start commands
|
|
1420
|
+
$('#show_test_run').click(function () {
|
|
1421
|
+
console.warn(`isHerdsmanRunning: ${isHerdsmanRunning}`)
|
|
1422
|
+
doTestStart(!isHerdsmanRunning);
|
|
1423
|
+
});
|
|
1424
|
+
|
|
1216
1425
|
$('#state_cleanup_btn').click(function () {
|
|
1217
1426
|
cleanConfirmation();
|
|
1218
1427
|
});
|
|
@@ -1241,6 +1450,10 @@ function load(settings, onChange) {
|
|
|
1241
1450
|
resetConfirmation();
|
|
1242
1451
|
});
|
|
1243
1452
|
|
|
1453
|
+
$('#deleteNVRam-btn').click(function () {
|
|
1454
|
+
deleteNvBackupConfirmation();
|
|
1455
|
+
});
|
|
1456
|
+
|
|
1244
1457
|
$('#viewconfig').click(function () {
|
|
1245
1458
|
showViewConfig();
|
|
1246
1459
|
});
|
|
@@ -1248,6 +1461,9 @@ function load(settings, onChange) {
|
|
|
1248
1461
|
$('#scan').click(function () {
|
|
1249
1462
|
showChannels();
|
|
1250
1463
|
});
|
|
1464
|
+
$('#scan_t').click(function () {
|
|
1465
|
+
showChannels();
|
|
1466
|
+
});
|
|
1251
1467
|
|
|
1252
1468
|
sendTo(namespace, 'getGroups', {}, function (data) {
|
|
1253
1469
|
groups = data.groups;
|
|
@@ -1274,6 +1490,10 @@ function load(settings, onChange) {
|
|
|
1274
1490
|
}
|
|
1275
1491
|
});
|
|
1276
1492
|
|
|
1493
|
+
$('#hardware').click(function() {
|
|
1494
|
+
validateNVRamBackup(false);
|
|
1495
|
+
});
|
|
1496
|
+
|
|
1277
1497
|
$(document).ready(function () {
|
|
1278
1498
|
$('.modal').modal({
|
|
1279
1499
|
startingTop: '30%',
|
|
@@ -1331,6 +1551,7 @@ function showMessages() {
|
|
|
1331
1551
|
data = mess + '\n' + data;
|
|
1332
1552
|
}
|
|
1333
1553
|
$('#stdout').text(data);
|
|
1554
|
+
$('#stdout_t').text(messages.join('\n'));
|
|
1334
1555
|
}
|
|
1335
1556
|
|
|
1336
1557
|
function showPairingProcess() {
|
|
@@ -1344,6 +1565,41 @@ function showPairingProcess() {
|
|
|
1344
1565
|
Materialize.updateTextFields();
|
|
1345
1566
|
}
|
|
1346
1567
|
|
|
1568
|
+
function doTestStart(start) {
|
|
1569
|
+
updateStartButton(true);
|
|
1570
|
+
if (start) {
|
|
1571
|
+
const ovr = { extPanID:$('#extPanID.value').val(),
|
|
1572
|
+
panID: $('#PanID.value').val(),
|
|
1573
|
+
channel: $('#channel.value').val(),
|
|
1574
|
+
port: $('#port.value').val(),
|
|
1575
|
+
adapterType: $('#adapterType.value').val(),
|
|
1576
|
+
baudRate: $('#baudRate.value').val(),
|
|
1577
|
+
precfgkey: $('#precfgkey.value').val(),
|
|
1578
|
+
flowCTRL: $('#flowCTRL.value').prop('checked')
|
|
1579
|
+
};
|
|
1580
|
+
// $('#testStartStart').addClass('disabled');
|
|
1581
|
+
messages = [];
|
|
1582
|
+
sendTo(namespace, 'testConnect', { start:true, zigbeeOptions:ovr }, function(msg) {
|
|
1583
|
+
if (msg) {
|
|
1584
|
+
if (msg.status)
|
|
1585
|
+
$('#testStartStop').removeClass('disabled');
|
|
1586
|
+
else
|
|
1587
|
+
$('#testStartStart').removeClass('disabled');
|
|
1588
|
+
}
|
|
1589
|
+
})
|
|
1590
|
+
}
|
|
1591
|
+
else {
|
|
1592
|
+
//$('#testStartStop').addClass('disabled');
|
|
1593
|
+
sendTo(namespace, 'testConnect', { start:false }, function(msg) {
|
|
1594
|
+
if (msg) {
|
|
1595
|
+
if (msg.status) $('#testStartStart').removeClass('disabled');
|
|
1596
|
+
else $('#testStartStop').removeClass('disabled');
|
|
1597
|
+
}
|
|
1598
|
+
})
|
|
1599
|
+
|
|
1600
|
+
}
|
|
1601
|
+
}
|
|
1602
|
+
|
|
1347
1603
|
// ... and the function save has to exist.
|
|
1348
1604
|
// you have to make sure the callback is called with the settings object as first param!
|
|
1349
1605
|
|
|
@@ -1370,6 +1626,36 @@ function getDevId(adapterDevId) {
|
|
|
1370
1626
|
return adapterDevId.split('.').slice(0, 3).join('.');
|
|
1371
1627
|
}
|
|
1372
1628
|
|
|
1629
|
+
|
|
1630
|
+
function updateStartButton(block) {
|
|
1631
|
+
if (block) {
|
|
1632
|
+
$('#show_test_run').addClass('disabled');
|
|
1633
|
+
$('#reset-btn').addClass('disabled');
|
|
1634
|
+
$('#deleteNVRam-btn').addClass('disabled');
|
|
1635
|
+
$('#ErrorNotificationBtn').removeClass('hide')
|
|
1636
|
+
$('#ErrorNotificationBtn').removeClass('blinking')
|
|
1637
|
+
$('#ErrorNotificationIcon').removeClass('icon-red')
|
|
1638
|
+
$('#ErrorNotificationIcon').addClass('icon-orange')
|
|
1639
|
+
return;
|
|
1640
|
+
}
|
|
1641
|
+
if (isHerdsmanRunning)
|
|
1642
|
+
{
|
|
1643
|
+
$('#ErrorNotificationBtn').addClass('hide')
|
|
1644
|
+
$('#ErrorNotificationBtn').removeClass('blinking');
|
|
1645
|
+
$('#show_test_run').removeClass('disabled');
|
|
1646
|
+
$('#deleteNVRam-btn').removeClass('disabled');
|
|
1647
|
+
$('#reset-btn').removeClass('disabled');
|
|
1648
|
+
}
|
|
1649
|
+
else {
|
|
1650
|
+
$('#ErrorNotificationIcon').addClass('icon-red')
|
|
1651
|
+
$('#ErrorNotificationIcon').removeClass('icon-orange')
|
|
1652
|
+
$('#ErrorNotificationBtn').removeClass('hide')
|
|
1653
|
+
$('#ErrorNotificationBtn').addClass('blinking');
|
|
1654
|
+
$('#show_test_run').removeClass('disabled');
|
|
1655
|
+
$('#deleteNVRam-btn').removeClass('disabled');
|
|
1656
|
+
$('#reset-btn').addClass('disabled');
|
|
1657
|
+
}
|
|
1658
|
+
}
|
|
1373
1659
|
// subscribe to changes
|
|
1374
1660
|
socket.emit('subscribe', namespace + '.*');
|
|
1375
1661
|
socket.emit('subscribeObjects', namespace + '.*');
|
|
@@ -1404,6 +1690,14 @@ socket.on('stateChange', function (id, state) {
|
|
|
1404
1690
|
else {
|
|
1405
1691
|
messages.push(state.val);
|
|
1406
1692
|
showMessages();
|
|
1693
|
+
if (state.val.startsWith('Zigbee-Herdsman started successfully')) {
|
|
1694
|
+
isHerdsmanRunning = true;
|
|
1695
|
+
updateStartButton();
|
|
1696
|
+
}
|
|
1697
|
+
if (state.val.startsWith('herdsman stopped') || state.val.startsWith('Error herdsman')) {
|
|
1698
|
+
isHerdsmanRunning = false;
|
|
1699
|
+
updateStartButton();
|
|
1700
|
+
}
|
|
1407
1701
|
}
|
|
1408
1702
|
} else {
|
|
1409
1703
|
const devId = getDevId(id);
|
|
@@ -2215,31 +2509,6 @@ function list2select(selector, list, selected, getText, getKey, getData) {
|
|
|
2215
2509
|
element.select();
|
|
2216
2510
|
}
|
|
2217
2511
|
|
|
2218
|
-
/*
|
|
2219
|
-
function showGroups() {
|
|
2220
|
-
$('#groups_table').find('.group').remove();
|
|
2221
|
-
if (!groups) return;
|
|
2222
|
-
const element = $('#groups_table');
|
|
2223
|
-
for (const j in groups) {
|
|
2224
|
-
if (groups.hasOwnProperty(j)) {
|
|
2225
|
-
element.append(`<tr id="group_${j}" class="group"><td>${j}</td><td><div>${groups[j]}<span class="right">` +
|
|
2226
|
-
`<a id="${j}" name="groupedit" class="waves-effect green btn-floating"><i class="material-icons">edit</i></a>` +
|
|
2227
|
-
`<a id="${j}" name="groupdelete" class="waves-effect red btn-floating"><i class="material-icons">delete</i></a></span></div></td></tr>`);
|
|
2228
|
-
}
|
|
2229
|
-
}
|
|
2230
|
-
$('a.btn-floating[name=\'groupedit\']').click(function () {
|
|
2231
|
-
const index = $(this).attr('id'),
|
|
2232
|
-
name = groups[index];
|
|
2233
|
-
editGroupName(index, name, false);
|
|
2234
|
-
});
|
|
2235
|
-
$('a.btn-floating[name=\'groupdelete\']').click(function () {
|
|
2236
|
-
const index = $(this).attr('id'),
|
|
2237
|
-
name = groups[index];
|
|
2238
|
-
deleteGroupConfirmation(index, name);
|
|
2239
|
-
});
|
|
2240
|
-
}
|
|
2241
|
-
*/
|
|
2242
|
-
|
|
2243
2512
|
function editGroup(id, name) {
|
|
2244
2513
|
const grp = devGroups[id];
|
|
2245
2514
|
let info = '';
|
|
@@ -2843,48 +3112,6 @@ function showDevInfo(id) {
|
|
|
2843
3112
|
$('#modaldevinfo').modal('open');
|
|
2844
3113
|
}
|
|
2845
3114
|
|
|
2846
|
-
/*
|
|
2847
|
-
function showGroupList(show) {
|
|
2848
|
-
const htmlsections = [];
|
|
2849
|
-
for (const groupid in devGroups) {
|
|
2850
|
-
const dev = devGroups[groupid];
|
|
2851
|
-
const grpname = (dev.common && dev.common.name ? dev.common.name : 'Group ' + groupid);
|
|
2852
|
-
const selectables = [];
|
|
2853
|
-
const members = [];
|
|
2854
|
-
if (dev && dev.memberinfo) {
|
|
2855
|
-
selectables.push(`<select id="members_${groupid}" multiple>`);
|
|
2856
|
-
for (let m = 0; m < dev.memberinfo.length; m++) {
|
|
2857
|
-
members.push(`${dev.memberinfo[m].device}.${dev.memberinfo[m].epid} (${dev.memberinfo[m].ieee})`);
|
|
2858
|
-
selectables.push(`<option value="${m}">${dev.memberinfo[m].device}.${dev.memberinfo[m].epid} (...${dev.memberinfo[m].ieee.slice(-4)})</option>`);
|
|
2859
|
-
}
|
|
2860
|
-
selectables.push('</select>');
|
|
2861
|
-
}
|
|
2862
|
-
htmlsections.push(`
|
|
2863
|
-
<div class="row">
|
|
2864
|
-
<div class="col s4 m4 l4">
|
|
2865
|
-
<h5>${grpname}<h5>
|
|
2866
|
-
</div>
|
|
2867
|
-
<div class=col s7 m7 l7">
|
|
2868
|
-
${members.join('<br>')}
|
|
2869
|
-
</div>
|
|
2870
|
-
</div>
|
|
2871
|
-
`);
|
|
2872
|
-
}
|
|
2873
|
-
|
|
2874
|
-
$('#grouplist').html(htmlsections.join(''));
|
|
2875
|
-
$('#add').click(function () {
|
|
2876
|
-
const maxind = parseInt(Object.getOwnPropertyNames(groups).reduce((a, b) => a > b ? a : b, 0));
|
|
2877
|
-
addGroup(maxind + 1, 'Group ' + maxind + 1, true);
|
|
2878
|
-
showGroupList(false);
|
|
2879
|
-
});
|
|
2880
|
-
|
|
2881
|
-
$('#modalgrouplist a.btn[name=\'save\']').unbind('click');
|
|
2882
|
-
$('#modalgrouplist a.btn[name=\'save\']').click(() => {
|
|
2883
|
-
});
|
|
2884
|
-
if (show) $('#modalgrouplist').modal('open');
|
|
2885
|
-
}
|
|
2886
|
-
*/
|
|
2887
|
-
|
|
2888
3115
|
let waitingTimeout, waitingInt;
|
|
2889
3116
|
|
|
2890
3117
|
function showWaitingDialog(text, timeout) {
|
|
@@ -3251,6 +3478,7 @@ function getDashCard(dev, groupImage, groupstatus) {
|
|
|
3251
3478
|
let options;
|
|
3252
3479
|
if (typeof stateDef.states == 'string') {
|
|
3253
3480
|
const sts = stateDef.states.split(';');
|
|
3481
|
+
if (sts.length < 2) return '';
|
|
3254
3482
|
options = sts.map((item) => {
|
|
3255
3483
|
const v = item.split(':');
|
|
3256
3484
|
return `<option value="${v[0]}" ${(val == v[0]) ? 'selected' : ''}>${v[1]}</option>`;
|
|
@@ -3261,9 +3489,14 @@ function getDashCard(dev, groupImage, groupstatus) {
|
|
|
3261
3489
|
options.push(`<option value="${key}" ${(val == key) ? 'selected' : ''}>${key}</option>`);
|
|
3262
3490
|
}
|
|
3263
3491
|
}
|
|
3492
|
+
if (options.length < 2) return '';
|
|
3264
3493
|
val = `<select class="browser-default enum" style="color : white; background-color: grey; height: 16px; padding: 0; width: auto; display: inline-block">${options.join('')}</select>`;
|
|
3265
|
-
} else {
|
|
3266
|
-
|
|
3494
|
+
} else if (stateDef.write) {
|
|
3495
|
+
return;
|
|
3496
|
+
// val = `<span class="input-field dash value"><input class="dash value" id="${stateDef.name}" value="${val}"></input></span>`;
|
|
3497
|
+
}
|
|
3498
|
+
else {
|
|
3499
|
+
val = `<span class="dash value">${val ? val : '(null)'} ${(stateDef.unit) ? stateDef.unit : ''}</span>`;
|
|
3267
3500
|
}
|
|
3268
3501
|
return `<li><span class="label dash truncate">${stateDef.name}</span><span id=${sid} oid=${id} class="state">${val}</span></li>`;
|
|
3269
3502
|
}).join('') : '';
|
|
@@ -3354,14 +3587,17 @@ function updateCardTimer() {
|
|
|
3354
3587
|
}
|
|
3355
3588
|
|
|
3356
3589
|
function updateDevice(id) {
|
|
3357
|
-
sendTo(namespace, 'getDevice', {id: id}, function (
|
|
3358
|
-
if (
|
|
3359
|
-
|
|
3360
|
-
|
|
3361
|
-
|
|
3362
|
-
|
|
3363
|
-
|
|
3364
|
-
|
|
3590
|
+
sendTo(namespace, 'getDevice', {id: id}, function (msg) {
|
|
3591
|
+
if (msg) {
|
|
3592
|
+
const devs = msg.devices;
|
|
3593
|
+
if (devs) {
|
|
3594
|
+
if (devs.error) {
|
|
3595
|
+
showMessage(devs.error, _('Error'));
|
|
3596
|
+
} else {
|
|
3597
|
+
removeDevice(id);
|
|
3598
|
+
devs.forEach(dev => devices.push(dev));
|
|
3599
|
+
showDevices();
|
|
3600
|
+
}
|
|
3365
3601
|
}
|
|
3366
3602
|
}
|
|
3367
3603
|
});
|
|
@@ -3409,3 +3645,72 @@ function reconfigureDevice(id) {
|
|
|
3409
3645
|
});
|
|
3410
3646
|
showWaitingDialog('Device is being reconfigure', 30);
|
|
3411
3647
|
}
|
|
3648
|
+
|
|
3649
|
+
const warnLevel = {
|
|
3650
|
+
extPanID : function(v) { return !(v && v.toLowerCase().trim()!='dddddddddddddddd')},
|
|
3651
|
+
channel: function(v) { const num = parseInt(v); return !(num==11 || num==15 || num==20 || num==25)},
|
|
3652
|
+
}
|
|
3653
|
+
const validatableKeys = ['channel', 'precfgkey', 'extPanID', 'panID'];
|
|
3654
|
+
|
|
3655
|
+
function validateConfigData(key, val) {
|
|
3656
|
+
if (validatableKeys.indexOf(key) < 0 || !val) return;
|
|
3657
|
+
if (warnLevel[key]) {
|
|
3658
|
+
if (warnLevel[key](val)) {
|
|
3659
|
+
console.warn(`warning set for ${key} (${val})`)
|
|
3660
|
+
$(`#${key}_ALERT`).removeClass('hide')
|
|
3661
|
+
} else $(`#${key}_ALERT`).addClass('hide')
|
|
3662
|
+
}
|
|
3663
|
+
if (nvRamBackup[key]) {
|
|
3664
|
+
console.warn(`value of ${key} is ${val} (${nvRamBackup[key]})`);
|
|
3665
|
+
if ((typeof val == 'string' && typeof nvRamBackup[key] == 'string' && val.toLowerCase == nvRamBackup[key].toLowerCase) || val == nvRamBackup[key])
|
|
3666
|
+
{
|
|
3667
|
+
console.warn(`ok set for ${key} (${val})`)
|
|
3668
|
+
$(`#${key}_OK`).removeClass('hide')
|
|
3669
|
+
$(`#${key}_NOK`).addClass('hide')
|
|
3670
|
+
}
|
|
3671
|
+
else
|
|
3672
|
+
{
|
|
3673
|
+
console.warn(`nok set for ${key} (${val})`)
|
|
3674
|
+
$(`#${key}_OK`).addClass('hide')
|
|
3675
|
+
$(`#${key}_NOK`).removeClass('hide')
|
|
3676
|
+
}
|
|
3677
|
+
}
|
|
3678
|
+
else {
|
|
3679
|
+
console.warn(`noval set for ${key} (${val})`)
|
|
3680
|
+
$(`#${key}_OK`).addClass('hide')
|
|
3681
|
+
$(`#${key}_NOK`).addClass('hide')
|
|
3682
|
+
}
|
|
3683
|
+
}
|
|
3684
|
+
|
|
3685
|
+
function validateNVRamBackup(update, src) {
|
|
3686
|
+
console.warn('validateNVRam');
|
|
3687
|
+
const validatedKeys = src ? [src] : validatableKeys;
|
|
3688
|
+
const validator = {};
|
|
3689
|
+
for (const key of validatedKeys) {
|
|
3690
|
+
const value = $('#' + key + '.value');
|
|
3691
|
+
if (nvRamBackup[key] && update) {
|
|
3692
|
+
if (value.attr('type') === 'checkbox') {
|
|
3693
|
+
value.prop('checked', nvRamBackup[key]);
|
|
3694
|
+
} else {
|
|
3695
|
+
value.val(nvRamBackup[key])
|
|
3696
|
+
}
|
|
3697
|
+
}
|
|
3698
|
+
validateConfigData(key, value.val());
|
|
3699
|
+
}
|
|
3700
|
+
}
|
|
3701
|
+
|
|
3702
|
+
|
|
3703
|
+
function readNVRamBackup(update) {
|
|
3704
|
+
console.warn('read nvRam')
|
|
3705
|
+
sendTo(namespace, 'readNVRam', {}, function(msg) {
|
|
3706
|
+
if (msg) {
|
|
3707
|
+
if (msg.error && update) {
|
|
3708
|
+
showMessages(msg.error, _('Error'));
|
|
3709
|
+
delete msg.error;
|
|
3710
|
+
}
|
|
3711
|
+
nvRamBackup = msg;
|
|
3712
|
+
validateNVRamBackup(update)
|
|
3713
|
+
}
|
|
3714
|
+
});
|
|
3715
|
+
|
|
3716
|
+
}
|