iobroker.zigbee 3.2.5 → 3.3.1-alpha.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +16 -0
- package/admin/admin.js +376 -267
- package/admin/index_m.html +21 -32
- package/admin/tab_m.html +14 -2
- package/io-package.json +31 -31
- package/lib/commands.js +120 -76
- package/lib/exclude.js +1 -1
- package/lib/exposes.js +187 -77
- package/lib/groups.js +28 -15
- package/lib/{devices.js → legacy/devices.js} +27 -3
- package/lib/{states.js → legacy/states.js} +3 -3
- package/lib/localConfig.js +42 -0
- package/lib/models.js +615 -0
- package/lib/networkmap.js +15 -5
- package/lib/statescontroller.js +312 -297
- package/lib/utils.js +3 -4
- package/lib/zbBaseExtension.js +4 -0
- package/lib/zbDeviceAvailability.js +16 -23
- package/lib/zbDeviceConfigure.js +21 -8
- package/lib/zigbeecontroller.js +134 -88
- package/main.js +38 -42
- package/package.json +14 -15
package/admin/admin.js
CHANGED
|
@@ -19,10 +19,12 @@ let devices = [],
|
|
|
19
19
|
networkEvents,
|
|
20
20
|
responseCodes = false,
|
|
21
21
|
localConfigData = {},
|
|
22
|
+
shownMap = 0,
|
|
22
23
|
groups = {},
|
|
23
24
|
devGroups = {}, // eslint-disable-line prefer-const
|
|
24
25
|
binding = [],
|
|
25
26
|
excludes = [],
|
|
27
|
+
tabShown = 0,
|
|
26
28
|
coordinatorinfo = {
|
|
27
29
|
installSource: 'IADefault_1',
|
|
28
30
|
channel: '-1',
|
|
@@ -109,6 +111,7 @@ function UpdateAdapterAlive(state) {
|
|
|
109
111
|
$('#ErrorNotificationBtn').removeClass('disabled');
|
|
110
112
|
$('#show_errors_btn').removeClass('disabled');
|
|
111
113
|
$('#download_icons_btn').removeClass('disabled');
|
|
114
|
+
$('#rebuild_states_btn').removeClass('disabled');
|
|
112
115
|
$('#pairing').removeClass('disabled');
|
|
113
116
|
}
|
|
114
117
|
else {
|
|
@@ -121,6 +124,7 @@ function UpdateAdapterAlive(state) {
|
|
|
121
124
|
$('#show_errors_btn').addClass('disabled');
|
|
122
125
|
$('#pairing').addClass('disabled');
|
|
123
126
|
$('#download_icons_btn').addClass('disabled');
|
|
127
|
+
$('#rebuild_states_btn').addClass('disabled');
|
|
124
128
|
}
|
|
125
129
|
connectionStatus.connected = state;
|
|
126
130
|
}
|
|
@@ -237,12 +241,15 @@ function getModelData(data, models, keys) {
|
|
|
237
241
|
const e_btn_tip = `edit model ${key}`;
|
|
238
242
|
const d_btn = btnParam(d_btn_name, d_btn_tip, foldData.devices ? 'expand_less' : 'expand_more', false);
|
|
239
243
|
const e_btn = btnParam(e_btn_name, e_btn_tip, 'edit', 'green', false)
|
|
244
|
+
const legacy = model.setOptions?.options?.legacy ? 'Legacy' : 'Exposed'
|
|
245
|
+
|
|
240
246
|
LocalDataDisplayValues.buttonSet.add(d_btn_name);
|
|
241
247
|
LocalDataDisplayValues.buttonSet.add(e_btn_name);
|
|
242
|
-
const devtxt = (model.devices.length
|
|
248
|
+
const devtxt = (model.devices.length) ? `${model.devices.length} ${model.model.type}${model.devices.length > 1 ? 's' : ''}` : '';
|
|
243
249
|
Html.push(`<tr id="datarowodd">
|
|
244
|
-
<td rowspan="${numrows}" width="
|
|
245
|
-
<td
|
|
250
|
+
<td rowspan="${numrows}" width="10%"><img src=${model.model.icon} class="dev_list"></td>
|
|
251
|
+
<td rowspan="${numrows}" width="15%">${legacy} model<br>${key}</td>
|
|
252
|
+
<td colspan="3">${devtxt}</td>
|
|
246
253
|
<td>${d_btn} ${e_btn}</td></tr>`)
|
|
247
254
|
let cnt = 0;
|
|
248
255
|
if (foldData.devices) {
|
|
@@ -258,7 +265,7 @@ function getModelData(data, models, keys) {
|
|
|
258
265
|
//const bn = btnParam(`d_delete_${devieee}`, `delete device ${devieee}`, 'delete', 'red darken-4', false);
|
|
259
266
|
const bna = btnParam(`d_delall_${k}-${devieee}`, `completely delete device ${devieee}`, 'delete_forever', 'red accent-4', false);
|
|
260
267
|
const bta = !dev.common.deactivated ? btnParam(`d_disen_${k}-${devieee}`, `disable device ${devieee}`, 'power_settings_new', 'green accent-4', false) : btnParam(`d_disen_${k}-${devieee}`, `enable device ${devieee}`, 'power_settings_new', 'red accent-4', false);
|
|
261
|
-
Html.push(`<tr id="datarow${isOdd ? 'opt':'even'}${dev.common.deactivated ? '_red' : ''}"><td width="1%"><i class="material-icons small">devices</i></td><td
|
|
268
|
+
Html.push(`<tr id="datarow${isOdd ? 'opt':'even'}${dev.common.deactivated ? '_red' : ''}"><td width="1%"><i class="material-icons small">devices</i></td><td>${devieee}</td><td>${dev.common.name}</td><td width="10%">${bna}${bta}</td></tr>`)
|
|
262
269
|
isOdd = !isOdd;
|
|
263
270
|
}
|
|
264
271
|
}
|
|
@@ -266,10 +273,10 @@ function getModelData(data, models, keys) {
|
|
|
266
273
|
const o_btn_name = `o_toggle_${k}`;
|
|
267
274
|
const o_btn_tip = `fold / unfold options for Model ${key}`;
|
|
268
275
|
LocalDataDisplayValues.buttonSet.add(o_btn_name);
|
|
269
|
-
const opttxt = (numOptions > 0
|
|
276
|
+
const opttxt = (numOptions > 0) ? `${numOptions} global option${numOptions > 1 ? 's' : ''}` :''
|
|
270
277
|
Html.push(`<tr id="datarowodd">
|
|
271
|
-
<td colspan="
|
|
272
|
-
<td>${btnParam(o_btn_name, o_btn_tip, foldData.options ? 'expand_less' : 'expand_more')}</td></tr>`)
|
|
278
|
+
<td colspan="3">${opttxt}</td>
|
|
279
|
+
<td>${btnParam(o_btn_name, o_btn_tip, foldData.options ? 'expand_less' : 'expand_more')} ${e_btn}</td></tr>`)
|
|
273
280
|
if (foldData.options) {
|
|
274
281
|
let isOdd = false;
|
|
275
282
|
for (const key of Object.keys(model.setOptions)) {
|
|
@@ -278,7 +285,7 @@ function getModelData(data, models, keys) {
|
|
|
278
285
|
for (const ok of Object.keys(oo)) {
|
|
279
286
|
LocalDataDisplayValues.buttonSet.add(`o_delete_${k}-${ok}`);
|
|
280
287
|
const btn = btnParam(`o_delete_${k}-${ok}`, `delete option ${ok}`, 'delete', 'red darken-4', false);
|
|
281
|
-
Html.push(`<tr id="datarow${isOdd ? 'opt':'even'}"><td width="1%"><i class="material-icons small">blur_circular</i></td><td
|
|
288
|
+
Html.push(`<tr id="datarow${isOdd ? 'opt':'even'}"><td width="1%"><i class="material-icons small">blur_circular</i></td><td>${ok}</td><td${oo[ok] === undefined ? 'id="datared">"not set on model"' : '>'+oo[ok]}</td><td width="10%">${btn}</td></tr>`)
|
|
282
289
|
isOdd = !isOdd;
|
|
283
290
|
}
|
|
284
291
|
}
|
|
@@ -288,10 +295,10 @@ function getModelData(data, models, keys) {
|
|
|
288
295
|
if (key==='icon') {
|
|
289
296
|
const icontext = model.setOptions[key] === undefined ? 'id="datared">"not set on model"' : `>${model.setOptions[key]}`;
|
|
290
297
|
const icon = model.setOptions[key]=== undefined ? '' : `<img src=${model.setOptions[key]} height="32px" class="sml_list">`;
|
|
291
|
-
Html.push(`<tr id="datarow${isOdd ? 'opt':'even'}"><td width="1%"><i class="material-icons small">blur_circular</i></td><td
|
|
298
|
+
Html.push(`<tr id="datarow${isOdd ? 'opt':'even'}"><td width="1%"><i class="material-icons small">blur_circular</i></td><td>${key}</td><td valign="middle" ${icontext}</td><td width="10%">${btn}${icon}</td></tr>`)
|
|
292
299
|
}
|
|
293
300
|
else
|
|
294
|
-
Html.push(`<tr id="datarow${isOdd ? 'opt':'even'}"><td width="1%"><i class="material-icons small">blur_circular</i></td><td
|
|
301
|
+
Html.push(`<tr id="datarow${isOdd ? 'opt':'even'}"><td width="1%"><i class="material-icons small">blur_circular</i></td><td>${key}</td><td ${model.setOptions[key] === undefined ? 'id="datared">"not set on model"' : '>'+model.setOptions[key]}</td><td>${btn}</td></tr>`)
|
|
295
302
|
isOdd = !isOdd;
|
|
296
303
|
}
|
|
297
304
|
}
|
|
@@ -336,26 +343,26 @@ function getDeviceData(deviceList, withIcon) {
|
|
|
336
343
|
|
|
337
344
|
function sortAndFilter(filter, sort) {
|
|
338
345
|
const fFun = filter || LocalDataDisplayValues.filterMethod;
|
|
339
|
-
console.warn('once:='+JSON.stringify(models['m_0'].setOptions))
|
|
340
|
-
console.warn('twice:='+ JSON.stringify(models['m_1'].setOptions))
|
|
346
|
+
//console.warn('once:='+JSON.stringify(models['m_0'].setOptions))
|
|
347
|
+
//console.warn('twice:='+ JSON.stringify(models['m_1'].setOptions))
|
|
341
348
|
let filterMap = LocalDataDisplayValues.sortedKeys = Object.keys(models);
|
|
342
349
|
if (LocalDataDisplayValues.searchVal && LocalDataDisplayValues.searchVal.length) {
|
|
343
350
|
filterMap = filterMap.filter((a) => {
|
|
344
351
|
return models[a]?.model?.model?.toLowerCase().includes(LocalDataDisplayValues.searchVal)
|
|
345
352
|
});
|
|
346
|
-
console.warn(`${JSON.stringify(LocalDataDisplayValues.searchVal)} - ${JSON.stringify(models['m_1'].model)}`);
|
|
353
|
+
//console.warn(`${JSON.stringify(LocalDataDisplayValues.searchVal)} - ${JSON.stringify(models['m_1'].model)}`);
|
|
347
354
|
}
|
|
348
355
|
if (typeof fFun == 'function') {
|
|
349
|
-
console.warn(`${JSON.stringify(filterMap)} - ${JSON.stringify(models['m_1'].model)}`);
|
|
356
|
+
//console.warn(`${JSON.stringify(filterMap)} - ${JSON.stringify(models['m_1'].model)}`);
|
|
350
357
|
filterMap = filterMap.filter(fFun);
|
|
351
358
|
}
|
|
352
|
-
console.warn(JSON.stringify(filterMap));
|
|
359
|
+
//console.warn(JSON.stringify(filterMap));
|
|
353
360
|
const sFun = sort || LocalDataDisplayValues.sortMethod;
|
|
354
361
|
if (typeof sFun == 'function') {
|
|
355
|
-
console.warn(`${JSON.stringify(filterMap)} - ${JSON.stringify(models['m_1'].model)}`);
|
|
362
|
+
//console.warn(`${JSON.stringify(filterMap)} - ${JSON.stringify(models['m_1'].model)}`);
|
|
356
363
|
filterMap = filterMap.sort(sFun);
|
|
357
364
|
}
|
|
358
|
-
console.warn(JSON.stringify(filterMap));
|
|
365
|
+
//console.warn(JSON.stringify(filterMap));
|
|
359
366
|
if (typeof filter == 'function') LocalDataDisplayValues.filterMethod = filter;
|
|
360
367
|
if (typeof sort == 'function') LocalDataDisplayValues.sortMethod = sort;
|
|
361
368
|
return filterMap;
|
|
@@ -373,7 +380,7 @@ function showLocalData() {
|
|
|
373
380
|
const Html = [];
|
|
374
381
|
|
|
375
382
|
if (sm) {
|
|
376
|
-
Html.push(`<table style="width:100%"><tr id="datatable"><th rowspan="${RowSpan}"> </th><th colspan=
|
|
383
|
+
Html.push(`<table style="width:100%"><tr id="datatable"><th rowspan="${RowSpan}" width="10px"> </th><th colspan=5></th><th></th><th rowspan="${RowSpan}" width="10px""> </th></tr>`);
|
|
377
384
|
Html.push(ModelHtml.join(''));
|
|
378
385
|
}
|
|
379
386
|
/*else {
|
|
@@ -419,16 +426,16 @@ function showLocalData() {
|
|
|
419
426
|
editDeviceOptions(models[key], true);
|
|
420
427
|
})
|
|
421
428
|
if (item.startsWith('o_delete_')) {
|
|
422
|
-
console.warn(`adding click to ${item}`)
|
|
429
|
+
//console.warn(`adding click to ${item}`)
|
|
423
430
|
$(`#${item}`).click(function () {
|
|
424
|
-
console.warn(`clicked ${item}`);
|
|
431
|
+
//console.warn(`clicked ${item}`);
|
|
425
432
|
const keys = item.replace('o_delete_', '').split('-');
|
|
426
433
|
const model = models[keys[0]]?.model.model;
|
|
427
434
|
const option = keys[1];
|
|
428
435
|
const sOptions = models[keys[0]]?.setOptions || {};
|
|
429
436
|
const options = models[keys[0]]?.setOptions?.options || {};
|
|
430
437
|
//options[option] = '##REMOVE##';
|
|
431
|
-
console.warn(`clicked ${item} - options are ${JSON.stringify(options)}`);
|
|
438
|
+
//console.warn(`clicked ${item} - options are ${JSON.stringify(options)}`);
|
|
432
439
|
delete options[option];
|
|
433
440
|
updateLocalConfigItems(model, sOptions || {}, true);
|
|
434
441
|
showLocalData();
|
|
@@ -440,15 +447,15 @@ function showLocalData() {
|
|
|
440
447
|
const option = keys[1];
|
|
441
448
|
const options = models[keys[0]].setOptions;
|
|
442
449
|
options[option] = '##REMOVE##';
|
|
443
|
-
console.warn(`clicked ${item} - options are ${JSON.stringify(options)}`);
|
|
450
|
+
//console.warn(`clicked ${item} - options are ${JSON.stringify(options)}`);
|
|
444
451
|
updateLocalConfigItems(model, options || {}, true)
|
|
445
452
|
delete options[option];
|
|
446
453
|
showLocalData();
|
|
447
454
|
})
|
|
448
455
|
if (item.startsWith('d_disen_')) {
|
|
449
|
-
console.warn(`adding click to ${item}`)
|
|
456
|
+
//console.warn(`adding click to ${item}`)
|
|
450
457
|
$(`#${item}`).click(function () {
|
|
451
|
-
console.warn(`clicked ${item}`);
|
|
458
|
+
//console.warn(`clicked ${item}`);
|
|
452
459
|
const keys = item.replace('d_disen_', '').split('-');
|
|
453
460
|
const model = models[keys[0]];
|
|
454
461
|
const device = model.devices.find( (d) => d.native.id === keys[1]);
|
|
@@ -460,13 +467,13 @@ function showLocalData() {
|
|
|
460
467
|
});
|
|
461
468
|
}
|
|
462
469
|
if (item.startsWith('d_delall_')) {
|
|
463
|
-
console.warn(`adding click to ${item}`)
|
|
470
|
+
//console.warn(`adding click to ${item}`)
|
|
464
471
|
$(`#${item}`).click(function () {
|
|
465
|
-
console.warn(`clicked ${item}`);
|
|
472
|
+
//console.warn(`clicked ${item}`);
|
|
466
473
|
const keys = item.replace('d_delall_', '').split('-');
|
|
467
474
|
const model = models[keys[0]];
|
|
468
475
|
const device = model.devices.find( (d) => d.native.id === keys[1]);
|
|
469
|
-
console.warn(`setting delete confirmation with ${keys[1]} ${models[keys[0]].devices?.length} ${models[keys[0]]?.model.model} `);
|
|
476
|
+
//console.warn(`setting delete confirmation with ${keys[1]} ${models[keys[0]].devices?.length} ${models[keys[0]]?.model.model} `);
|
|
470
477
|
deleteConfirmation(keys[1], device.common.name, keys[1], models[keys[0]]?.devices?.length <=1 ? models[keys[0]]?.model?.model : undefined);
|
|
471
478
|
});
|
|
472
479
|
}
|
|
@@ -1002,7 +1009,7 @@ function cleanConfirmation() {
|
|
|
1002
1009
|
$('#modalclean a.btn[name=\'yes\']').unbind('click');
|
|
1003
1010
|
$('#modalclean a.btn[name=\'yes\']').click(() => {
|
|
1004
1011
|
const force = $('#cforce').prop('checked');
|
|
1005
|
-
|
|
1012
|
+
modifyDeviceStates('clean', force, `${force ? 'Completely r' : 'R'}emoving orphaned states.`);
|
|
1006
1013
|
});
|
|
1007
1014
|
$('#modalclean').modal('open');
|
|
1008
1015
|
Materialize.updateTextFields();
|
|
@@ -1029,7 +1036,7 @@ function editGroupMembers(id, name) {
|
|
|
1029
1036
|
}
|
|
1030
1037
|
$('#modaledit').find('.endpoints_for_groups').html(html.join(''));
|
|
1031
1038
|
for (const groupable of groupables) {
|
|
1032
|
-
console.warn(`list 2 select called with ${groupable.ep.ID}, groups ${JSON.stringify(groups)}, groupable ${JSON.stringify(groupable)}`);
|
|
1039
|
+
//console.warn(`list 2 select called with ${groupable.ep.ID}, groups ${JSON.stringify(groups)}, groupable ${JSON.stringify(groupable)}`);
|
|
1033
1040
|
list2select(`#gk_${groupable.ep.ID || -1}`, groups, groupable.memberOf || []);
|
|
1034
1041
|
}
|
|
1035
1042
|
}
|
|
@@ -1087,7 +1094,7 @@ function deleteZigbeeDevice(id, force, devOpts, modelOpts) {
|
|
|
1087
1094
|
sendToWrapper(namespace, 'deleteZigbeeDevice', {id: id, force: force, dev:devOpts, model:modelOpts}, function (msg) {
|
|
1088
1095
|
closeWaitingDialog();
|
|
1089
1096
|
if (msg) {
|
|
1090
|
-
if (msg.error) {
|
|
1097
|
+
if (msg.error && msg.error.length) {
|
|
1091
1098
|
showMessage(msg.error, _('Error'));
|
|
1092
1099
|
} else {
|
|
1093
1100
|
getDevices();
|
|
@@ -1098,8 +1105,8 @@ function deleteZigbeeDevice(id, force, devOpts, modelOpts) {
|
|
|
1098
1105
|
}
|
|
1099
1106
|
|
|
1100
1107
|
|
|
1101
|
-
function
|
|
1102
|
-
sendToWrapper(namespace, '
|
|
1108
|
+
function modifyDeviceStates(action, force, message, timeout) {
|
|
1109
|
+
sendToWrapper(namespace, 'modifyDeviceStates', { action, force}, function (msg) {
|
|
1103
1110
|
closeWaitingDialog();
|
|
1104
1111
|
if (msg) {
|
|
1105
1112
|
if (msg.error) {
|
|
@@ -1112,7 +1119,7 @@ function cleanDeviceStates(force) {
|
|
|
1112
1119
|
}
|
|
1113
1120
|
}
|
|
1114
1121
|
});
|
|
1115
|
-
showWaitingDialog(
|
|
1122
|
+
showWaitingDialog(message, timeout);
|
|
1116
1123
|
}
|
|
1117
1124
|
|
|
1118
1125
|
function renameDevice(id, name) {
|
|
@@ -1490,12 +1497,12 @@ async function editDeviceOptions(id, isModel) {
|
|
|
1490
1497
|
device_options[key] = { key:optionName, value:'', isCustom:optionName==='custom', expose:getExposeFromOptions(optionName)};
|
|
1491
1498
|
idx = dialogData.availableOptions.indexOf(optionName);
|
|
1492
1499
|
if (idx > -1 && !device_options[key].isCustom) dialogData.availableOptions.splice(idx, 1);
|
|
1493
|
-
console.warn(`addOption added ${JSON.stringify(device_options)}`)
|
|
1500
|
+
//console.warn(`addOption added ${JSON.stringify(device_options)}`)
|
|
1494
1501
|
}
|
|
1495
1502
|
|
|
1496
1503
|
|
|
1497
1504
|
function updateOptions(candidates) {
|
|
1498
|
-
console.warn(`update Options with ${JSON.stringify(candidates)}`)
|
|
1505
|
+
//console.warn(`update Options with ${JSON.stringify(candidates)}`)
|
|
1499
1506
|
if (candidates.length > 0) {
|
|
1500
1507
|
$('#chooseimage').find('.new_options_available').removeClass('hide');
|
|
1501
1508
|
list2select('#option_Selector', candidates, [], (key, val) => { return val; }, (key, val) => { return val; })
|
|
@@ -1509,7 +1516,7 @@ async function editDeviceOptions(id, isModel) {
|
|
|
1509
1516
|
for (const k of Object.keys(device_options)) {
|
|
1510
1517
|
const expose = device_options[k].expose === undefined ? getExposeFromOptions(device_options[k].key) : device_options[k].expose;
|
|
1511
1518
|
const disabled = device_options[k]?.isCustom ? '' : 'disabled ';
|
|
1512
|
-
console.warn(`option for ${k} is ${JSON.stringify(device_options[k])}`);
|
|
1519
|
+
//console.warn(`option for ${k} is ${JSON.stringify(device_options[k])}`);
|
|
1513
1520
|
html_options.push(`<div class="row">`);
|
|
1514
1521
|
switch (expose.type) {
|
|
1515
1522
|
case 'numeric':
|
|
@@ -1545,7 +1552,7 @@ async function editDeviceOptions(id, isModel) {
|
|
|
1545
1552
|
const oval = $(`#option_value_${key}`).html();
|
|
1546
1553
|
const val = $(`#option_value_${key}`).html()=== dok.vOn ? dok.vOff : dok.vOn;
|
|
1547
1554
|
dok.value = val;
|
|
1548
|
-
console.warn(`${item} clicked: ${JSON.stringify(dok)} => ${val} from ${oval}`);
|
|
1555
|
+
//console.warn(`${item} clicked: ${JSON.stringify(dok)} => ${val} from ${oval}`);
|
|
1549
1556
|
$(`#${item}`).html(val);
|
|
1550
1557
|
});
|
|
1551
1558
|
}
|
|
@@ -1557,7 +1564,7 @@ async function editDeviceOptions(id, isModel) {
|
|
|
1557
1564
|
if (device_options[k].expose?.type != 'binary') {
|
|
1558
1565
|
const value = $(`#option_value_${k}.value`);
|
|
1559
1566
|
/* if (value.attr('type') === 'checkbox') {
|
|
1560
|
-
console.warn(`oval for ${k} : ${device_options[k].value}`);
|
|
1567
|
+
//console.warn(`oval for ${k} : ${device_options[k].value}`);
|
|
1561
1568
|
value.prop('checked', Boolean(device_options[k].value));
|
|
1562
1569
|
}
|
|
1563
1570
|
else*/
|
|
@@ -1574,7 +1581,7 @@ async function editDeviceOptions(id, isModel) {
|
|
|
1574
1581
|
|
|
1575
1582
|
function getExposeFromOptions(option) {
|
|
1576
1583
|
const rv = dialogData.model.optionExposes.find((expose) => expose.name === option);
|
|
1577
|
-
console.warn(`GEFO: ${option} results in ${JSON.stringify(rv)}`);
|
|
1584
|
+
//console.warn(`GEFO: ${option} results in ${JSON.stringify(rv)}`);
|
|
1578
1585
|
if (rv) return rv;
|
|
1579
1586
|
return { type:option === 'legacy' ? 'binary' : 'string' };
|
|
1580
1587
|
}
|
|
@@ -1582,15 +1589,15 @@ async function editDeviceOptions(id, isModel) {
|
|
|
1582
1589
|
function getOptionsFromUI(_do, _so) {
|
|
1583
1590
|
const _no = {};
|
|
1584
1591
|
let changed = false;
|
|
1585
|
-
console.warn(`${changed} : ${JSON.stringify(_do)} - ${JSON.stringify(_no)}`)
|
|
1592
|
+
//console.warn(`${changed} : ${JSON.stringify(_do)} - ${JSON.stringify(_no)}`)
|
|
1586
1593
|
for (const k of Object.keys(_do)) {
|
|
1587
1594
|
const key = $(`#option_key_${k}`).val();
|
|
1588
1595
|
if (_do[k].isCustom) _do[k].key = key;
|
|
1589
1596
|
else if (_do[k].key != key) {
|
|
1590
|
-
console.warn(`_illegal Keys: ${key}, ${_do[k].key}`)
|
|
1597
|
+
//console.warn(`_illegal Keys: ${key}, ${_do[k].key}`)
|
|
1591
1598
|
continue;
|
|
1592
1599
|
}
|
|
1593
|
-
console.warn(`_legal Keys: ${key}, ${_do[k].key}`)
|
|
1600
|
+
//console.warn(`_legal Keys: ${key}, ${_do[k].key}`)
|
|
1594
1601
|
if (_do[k].expose?.type === 'binary') {
|
|
1595
1602
|
_do[k].value = $(`#option_value_${k}`).html();
|
|
1596
1603
|
}
|
|
@@ -1599,13 +1606,13 @@ async function editDeviceOptions(id, isModel) {
|
|
|
1599
1606
|
_do[k].value = $(`#option_value_${k}`).val();
|
|
1600
1607
|
}
|
|
1601
1608
|
if (_do[k].key.length > 0) {
|
|
1602
|
-
console.warn(`dok: ${_do[k].key} : ${_do[k].value}`);
|
|
1609
|
+
//console.warn(`dok: ${_do[k].key} : ${_do[k].value}`);
|
|
1603
1610
|
_no[key] = _do[k].value;
|
|
1604
1611
|
changed |= (_no[key] != _so[key]);
|
|
1605
1612
|
}
|
|
1606
1613
|
}
|
|
1607
1614
|
changed |= (Object.keys(_no).length != Object.keys(_so).length);
|
|
1608
|
-
console.warn(`${changed ? 'changed': 'unchanged'} : ${JSON.stringify(_so)} - ${JSON.stringify(_no)}`)
|
|
1615
|
+
//console.warn(`${changed ? 'changed': 'unchanged'} : ${JSON.stringify(_so)} - ${JSON.stringify(_no)}`)
|
|
1609
1616
|
if (changed) return _no;
|
|
1610
1617
|
return undefined;
|
|
1611
1618
|
}
|
|
@@ -1654,6 +1661,7 @@ async function editDeviceOptions(id, isModel) {
|
|
|
1654
1661
|
else dialogData.setOptions[k] = id.setOptions[k];
|
|
1655
1662
|
dialogData.name = id.setOptions.name || id.name || 'unset';
|
|
1656
1663
|
dialogData.icon = id.setOptions.icon || model.icon || 'img/dummyDevice.jpg';
|
|
1664
|
+
dialogData.defaultIcon = model.icon || `img/${model.model.replace(/\//g, '-')}.png`;
|
|
1657
1665
|
dialogData.legacyIcon = id.devices[0].legacyIcon;
|
|
1658
1666
|
id = id.model.model;
|
|
1659
1667
|
} else
|
|
@@ -1664,7 +1672,7 @@ async function editDeviceOptions(id, isModel) {
|
|
|
1664
1672
|
dialogData.availableOptions.push(...adapterDefinedOptions)
|
|
1665
1673
|
dialogData.name = dev.common.name;
|
|
1666
1674
|
dialogData.icon = dev.common.icon || dev.icon;
|
|
1667
|
-
dialogData.
|
|
1675
|
+
dialogData.defaultIcon = (dev.common.type === 'group' ? dev.common.modelIcon : `img/${dev.common.type.replace(/\//g, '-')}.png`);
|
|
1668
1676
|
dialogData.legacyIcon = dev.legacyIcon;
|
|
1669
1677
|
}
|
|
1670
1678
|
|
|
@@ -1709,7 +1717,7 @@ async function editDeviceOptions(id, isModel) {
|
|
|
1709
1717
|
for (const key in msg.options)
|
|
1710
1718
|
{
|
|
1711
1719
|
const idx = dialogData.availableOptions.indexOf(key);
|
|
1712
|
-
console.warn(`key ${key} : index : ${idx}`);
|
|
1720
|
+
//console.warn(`key ${key} : index : ${idx}`);
|
|
1713
1721
|
if (idx > -1) dialogData.availableOptions.splice(idx,1);
|
|
1714
1722
|
received_options[key]=msg.options[key];
|
|
1715
1723
|
device_options[`o${cnt}`] = { key:key, value:msg.options[key]}
|
|
@@ -1759,7 +1767,7 @@ function HtmlFromInDebugMessages(messages, devID, filter) {
|
|
|
1759
1767
|
const buttonList = [];
|
|
1760
1768
|
const idRed = ' id="dbgred"'
|
|
1761
1769
|
if (dbgMsghide.has('i_'+devID)) {
|
|
1762
|
-
console.warn('in all filtered out')
|
|
1770
|
+
//console.warn('in all filtered out')
|
|
1763
1771
|
Html.push(' ')
|
|
1764
1772
|
} else for (const item of messages) {
|
|
1765
1773
|
if (item.states.length > 0) {
|
|
@@ -1798,7 +1806,7 @@ function HtmlFromOutDebugMessages(messages, devID, filter) {
|
|
|
1798
1806
|
let isodd=true;
|
|
1799
1807
|
const buttonList = [];
|
|
1800
1808
|
if (dbgMsghide.has('o_'+devID)) {
|
|
1801
|
-
console.warn('out all filtered out')
|
|
1809
|
+
//console.warn('out all filtered out')
|
|
1802
1810
|
Html.push(' ')
|
|
1803
1811
|
}
|
|
1804
1812
|
else for (const item of messages) {
|
|
@@ -1834,7 +1842,7 @@ function HtmlFromOutDebugMessages(messages, devID, filter) {
|
|
|
1834
1842
|
}
|
|
1835
1843
|
|
|
1836
1844
|
function displayDebugMessages(msg) {
|
|
1837
|
-
console.warn('displayDebugMessages called with '+ JSON.stringify(msg));
|
|
1845
|
+
//console.warn('displayDebugMessages called with '+ JSON.stringify(msg));
|
|
1838
1846
|
const buttonNames = [];
|
|
1839
1847
|
const idButtons = [];
|
|
1840
1848
|
if (msg.byId) {
|
|
@@ -2064,6 +2072,7 @@ function getDevices() {
|
|
|
2064
2072
|
}
|
|
2065
2073
|
|
|
2066
2074
|
function extractDevicesData(msg) {
|
|
2075
|
+
//console.warn(JSON.stringify(msg.errors));
|
|
2067
2076
|
devices = msg.devices ? msg.devices : [];
|
|
2068
2077
|
// check if stashed error messages are sent alongside
|
|
2069
2078
|
if (msg.clean)
|
|
@@ -2103,25 +2112,36 @@ function getNamedColors() {
|
|
|
2103
2112
|
});
|
|
2104
2113
|
}
|
|
2105
2114
|
|
|
2106
|
-
|
|
2115
|
+
let map_errors = [];
|
|
2107
2116
|
function getMap(rebuild) {
|
|
2108
|
-
$('#refresh').addClass('disabled');
|
|
2109
|
-
if (isHerdsmanRunning) {
|
|
2117
|
+
if (rebuild) $('#refresh').addClass('disabled');
|
|
2118
|
+
if (isHerdsmanRunning || !rebuild) {
|
|
2110
2119
|
sendToWrapper(namespace, 'getMap', { forcebuild:rebuild}, function (msg) {
|
|
2111
2120
|
$('#refresh').removeClass('disabled');
|
|
2112
2121
|
if (msg) {
|
|
2113
|
-
if (msg.
|
|
2114
|
-
|
|
2115
|
-
|
|
2116
|
-
|
|
2117
|
-
|
|
2118
|
-
|
|
2119
|
-
|
|
2120
|
-
|
|
2121
|
-
|
|
2122
|
+
if (!msg.hasMap) $('#refresh').removeClass('hide');
|
|
2123
|
+
else {
|
|
2124
|
+
$('#refresh').addClass('hide');
|
|
2125
|
+
if (msg.error) {
|
|
2126
|
+
//errorData.push(msg.error);
|
|
2127
|
+
isHerdsmanRunning = false;
|
|
2128
|
+
updateStartButton();
|
|
2129
|
+
} else {
|
|
2130
|
+
isHerdsmanRunning = true;
|
|
2131
|
+
updateStartButton();
|
|
2132
|
+
if (msg.errors.length > 0 && $('#errorCollectionOn').is(':checked')) {
|
|
2133
|
+
$('#map_errors_btn').removeClass('hide');
|
|
2134
|
+
$('#map_errors_btn').unbind('click');
|
|
2135
|
+
map_errors=msg.errors;
|
|
2136
|
+
$('#map_errors_btn').click(() => {
|
|
2137
|
+
showMessage(map_errors.join('<br>'), 'Map generation messages');
|
|
2138
|
+
$('#map_errors_btn').addClass('hide');
|
|
2139
|
+
})
|
|
2140
|
+
}
|
|
2141
|
+
if (map?.timestamp != msg?.timestamp)
|
|
2142
|
+
map = msg;
|
|
2143
|
+
if (rebuild) showNetworkMap(devices, map);
|
|
2122
2144
|
}
|
|
2123
|
-
map = msg;
|
|
2124
|
-
showNetworkMap(devices, map);
|
|
2125
2145
|
}
|
|
2126
2146
|
}
|
|
2127
2147
|
});
|
|
@@ -2207,6 +2227,7 @@ function load(settings, onChange) {
|
|
|
2207
2227
|
//const keepAliveHandle = startKeepalive();
|
|
2208
2228
|
keepAlive(() => {
|
|
2209
2229
|
getDevices();
|
|
2230
|
+
getMap(false);
|
|
2210
2231
|
getNamedColors();
|
|
2211
2232
|
readNVRamBackup(false);
|
|
2212
2233
|
sendToWrapper(namespace, 'getGroups', {}, function (data) {
|
|
@@ -2257,26 +2278,32 @@ function load(settings, onChange) {
|
|
|
2257
2278
|
});
|
|
2258
2279
|
$('#show_errors_btn').click(function () {
|
|
2259
2280
|
const errMsgTable = [];
|
|
2260
|
-
console.warn(JSON.stringify(errorData));
|
|
2281
|
+
//console.warn(JSON.stringify(errorData));
|
|
2261
2282
|
if (Object.keys(errorData.errors).length > 0) {
|
|
2262
|
-
errMsgTable.push(`<table><tr><th>Message</th><th>#</th><th>last seen</th></tr>`)
|
|
2283
|
+
errMsgTable.push(`<table><tr><th>Message</th><th>#</th><th>first seen</th><th>last seen</th></tr>`)
|
|
2263
2284
|
for (const err of Object.values(errorData.errors))
|
|
2264
|
-
if (err
|
|
2285
|
+
if (err && err.ts && err.count) {
|
|
2286
|
+
const erridx = err.ts.length > 1 ? 1 : 0
|
|
2287
|
+
errMsgTable.push(`<tr><td>${err.message}</td><td>${err.count}</td><td>${new Date(err.ts[0]).toLocaleTimeString()}</td><td>${new Date(err.ts[erridx]).toLocaleTimeString()}</td></tr>`)
|
|
2288
|
+
}
|
|
2265
2289
|
errMsgTable.push('</table>');
|
|
2266
2290
|
}
|
|
2267
2291
|
if (Object.keys(errorData.unknownModels).length > 0) {
|
|
2268
|
-
errMsgTable.push(`<table><tr><th>Unknown Models</th><th>#</th><th>last seen</th></tr>`)
|
|
2292
|
+
errMsgTable.push(`<table><tr><th>Unknown Models</th><th>#</th><th>first seen</th><th>last seen</th></tr>`)
|
|
2269
2293
|
for (const err of Object.values(errorData.unknownModels))
|
|
2270
|
-
|
|
2294
|
+
if (err && err.ts && err.count) {
|
|
2295
|
+
const erridx = err.ts.length > 1 ? 1 : 0
|
|
2296
|
+
errMsgTable.push(`<tr><td>${err.message}</td><td>${err.count}</td><td>${new Date(err.ts[0]).toLocaleTimeString()}</td><td>${new Date(err.ts[erridx]).toLocaleTimeString()}</td></tr>`)
|
|
2297
|
+
}
|
|
2271
2298
|
errMsgTable.push('</table>');
|
|
2272
2299
|
}
|
|
2273
|
-
console.warn(JSON.stringify(errMsgTable));
|
|
2300
|
+
//console.warn(JSON.stringify(errMsgTable));
|
|
2274
2301
|
showMessage(errMsgTable.join(''), 'Stashed error messages', '<a id="delete_errors_btn" class="btn-floating waves-effect waves-light tooltipped center-align hoverable translateT" title="delete Errors"></i class="material-icons icon-black">delete_sweep</i></a>');
|
|
2275
2302
|
$('#delete_errors_btn').unbind('click')
|
|
2276
2303
|
$('#delete_errors_btn').click(function () {
|
|
2277
2304
|
sendToWrapper(namespace, 'clearErrors', {}, function(msg) {
|
|
2278
2305
|
if (msg) {
|
|
2279
|
-
console.warn('msg is ' + JSON.stringify(msg));
|
|
2306
|
+
//console.warn('msg is ' + JSON.stringify(msg));
|
|
2280
2307
|
errorData = msg;
|
|
2281
2308
|
$('#show_errors_btn').addClass('hide');
|
|
2282
2309
|
}
|
|
@@ -2288,6 +2315,20 @@ function load(settings, onChange) {
|
|
|
2288
2315
|
$('#download_icons_btn').click(function () {
|
|
2289
2316
|
showMessage(downloadIcons());
|
|
2290
2317
|
});
|
|
2318
|
+
$('#rebuild_states_btn').click(function () {
|
|
2319
|
+
const text = translateWord('Do you really want to recreate all states ?');
|
|
2320
|
+
$('#modalrebuild').find('p').text(text);
|
|
2321
|
+
$('#cforce_rebuild').prop('checked', true);
|
|
2322
|
+
$('#cforce_rebuild').removeClass('hide');
|
|
2323
|
+
$('#cforcediv').removeClass('hide');
|
|
2324
|
+
$('#modalrebuild a.btn[name=\'yes\']').unbind('click');
|
|
2325
|
+
$('#modalrebuild a.btn[name=\'yes\']').click(() => {
|
|
2326
|
+
const force = $('#cforce_rebuild').prop('checked');
|
|
2327
|
+
modifyDeviceStates('rebuild', force, `${force ? 'Completely r':'R'}ebuilding all device states`, 10);
|
|
2328
|
+
});
|
|
2329
|
+
$('#modalrebuild').modal('open');
|
|
2330
|
+
Materialize.updateTextFields();
|
|
2331
|
+
});
|
|
2291
2332
|
$('#fw_check_btn').click(function () {
|
|
2292
2333
|
checkFwUpdate();
|
|
2293
2334
|
});
|
|
@@ -2304,7 +2345,7 @@ function load(settings, onChange) {
|
|
|
2304
2345
|
});
|
|
2305
2346
|
|
|
2306
2347
|
$('#refresh').click(function () {
|
|
2307
|
-
getMap(
|
|
2348
|
+
getMap(true);
|
|
2308
2349
|
});
|
|
2309
2350
|
$('#regenerate').click(function () {
|
|
2310
2351
|
getMap(true);
|
|
@@ -2374,7 +2415,17 @@ function load(settings, onChange) {
|
|
|
2374
2415
|
Materialize.updateTextFields();
|
|
2375
2416
|
$('.collapsible').collapsible();
|
|
2376
2417
|
|
|
2377
|
-
|
|
2418
|
+
function new_tab_show_callback() {
|
|
2419
|
+
|
|
2420
|
+
tabShown = M.Tabs.getInstance($('.tabs')).index;
|
|
2421
|
+
if (tabShown === 1 && shownMap === 0) {
|
|
2422
|
+
console.log(`tabShown set to ${tabShown} - showing map for the first time`);
|
|
2423
|
+
showNetworkMap(devices, map);
|
|
2424
|
+
}
|
|
2425
|
+
else console.log(`tabShown set to ${tabShown}`);
|
|
2426
|
+
}
|
|
2427
|
+
|
|
2428
|
+
Materialize.Tabs.init($('.tabs'), {duration: 600, onShow: new_tab_show_callback});
|
|
2378
2429
|
$('#device-search').keyup(function (event) {
|
|
2379
2430
|
doFilter(event.target.value.toLowerCase());
|
|
2380
2431
|
});
|
|
@@ -2670,10 +2721,25 @@ socket.on('stateChange', function (id, state) {
|
|
|
2670
2721
|
if (state.val === 'Closing network.') {
|
|
2671
2722
|
getDevices();
|
|
2672
2723
|
}
|
|
2724
|
+
if (state.val.startsWith('Map')) {
|
|
2725
|
+
if (state.val === 'Map invalidated.') {
|
|
2726
|
+
$('#refresh').removeClass('hide');
|
|
2727
|
+
return;
|
|
2728
|
+
}
|
|
2729
|
+
const numDev = Number(state.val.split(':').pop()) || 0;
|
|
2730
|
+
if (numDev > 0) {
|
|
2731
|
+
$(`#map_generating_btn`).removeClass('hide');
|
|
2732
|
+
if (numDev < 10) $(`#map_generating_btn`).html(`<i class="material-icons large icon-blue">filter_${numDev}</i>`);
|
|
2733
|
+
else $(`#map_generating_btn`).html(`<i class="material-icons large icon-blue">${numDev%2 ? 'filter_9_plus' : 'queue'}</i>`);
|
|
2734
|
+
}
|
|
2735
|
+
else {
|
|
2736
|
+
$('#map_generating_btn').addClass('hide');
|
|
2737
|
+
}
|
|
2738
|
+
}
|
|
2673
2739
|
}
|
|
2674
2740
|
} else if (id.match(/\.info\.lasterror$/)) {
|
|
2675
2741
|
try {
|
|
2676
|
-
console.warn(`lasterror is ${JSON.stringify(state)}`)
|
|
2742
|
+
//console.warn(`lasterror is ${JSON.stringify(state)}`)
|
|
2677
2743
|
const errobj = JSON.parse(state.val);
|
|
2678
2744
|
let changed = false;
|
|
2679
2745
|
if (errobj.error) {
|
|
@@ -2768,94 +2834,151 @@ function showNetworkMap(devices, map) {
|
|
|
2768
2834
|
// create an array with edges
|
|
2769
2835
|
const edges = [];
|
|
2770
2836
|
|
|
2771
|
-
if (map.lqis == undefined || map.lqis.length === 0) { // first init
|
|
2772
|
-
|
|
2773
|
-
|
|
2774
|
-
|
|
2837
|
+
// if (map.lqis == undefined || map.lqis.length === 0) { // first init
|
|
2838
|
+
$('#filterParent, #filterSibl, #filterPrvChild, #filterMesh, #physicsOn').unbind('change');
|
|
2839
|
+
$('#filterParent, #filterSibl, #filterPrvChild, #filterMesh, #physicsOn').change(function () {
|
|
2840
|
+
updateMapFilter();
|
|
2841
|
+
});
|
|
2842
|
+
// }
|
|
2843
|
+
if (tabShown != 1) {
|
|
2844
|
+
console.log(`tabShown is ${tabShown} - map is not visible so we dont generate it.`);
|
|
2845
|
+
return;
|
|
2775
2846
|
}
|
|
2776
2847
|
|
|
2777
|
-
|
|
2778
|
-
|
|
2779
|
-
|
|
2780
|
-
|
|
2781
|
-
|
|
2782
|
-
|
|
2783
|
-
|
|
2784
|
-
|
|
2785
|
-
|
|
2786
|
-
|
|
2787
|
-
|
|
2788
|
-
|
|
2789
|
-
|
|
2790
|
-
|
|
2791
|
-
|
|
2848
|
+
console.log(`showNetwork Map (previous: ${shownMap} - new: ${map.timestamp} for ${devices.length} devices.`);
|
|
2849
|
+
if (devices.length == 0) return;
|
|
2850
|
+
if (shownMap != map.timestamp) {
|
|
2851
|
+
shownMap = map.timestamp;
|
|
2852
|
+
|
|
2853
|
+
const createNode = function (dev, mapEntry) {
|
|
2854
|
+
if (dev.common && (dev.common.type == 'group' || dev.common.deactivated)) return undefined;
|
|
2855
|
+
const extInfo = (mapEntry && mapEntry.networkAddress) ? `\n (nwkAddr: 0x${mapEntry.networkAddress.toString(16)} | ${mapEntry.networkAddress})` : '';
|
|
2856
|
+
const t = dev._id.replace(namespace + '.', '');
|
|
2857
|
+
const node = {
|
|
2858
|
+
id: dev._id,
|
|
2859
|
+
label: (dev.link_quality > 0 ? dev.common.name : `${dev.common.name}\n(disconnected)`),
|
|
2860
|
+
title: `${t} ${extInfo}`,
|
|
2861
|
+
shape: 'circularImage',
|
|
2862
|
+
image: dev.common.icon || dev.icon,
|
|
2863
|
+
imagePadding: {top: 5, bottom: 5, left: 5, right: 5},
|
|
2864
|
+
color: {background: '#cccccc', highlight: {background: 'white'}},
|
|
2865
|
+
font: {color: '#00bb00'},
|
|
2866
|
+
borderWidth: 1,
|
|
2867
|
+
borderWidthSelected: 4,
|
|
2868
|
+
};
|
|
2869
|
+
if (dev.common && dev.common.type === 'Coordinator') {
|
|
2870
|
+
// node.shape = 'star';
|
|
2871
|
+
node.image = 'zigbee.png';
|
|
2872
|
+
node.label = 'Coordinator';
|
|
2873
|
+
// delete node.color;
|
|
2874
|
+
}
|
|
2875
|
+
//console.warn(`node for device ${JSON.stringify(node)}`)
|
|
2876
|
+
return node;
|
|
2792
2877
|
};
|
|
2793
|
-
if (dev.common && dev.common.type === 'Coordinator') {
|
|
2794
|
-
// node.shape = 'star';
|
|
2795
|
-
node.image = 'zigbee.png';
|
|
2796
|
-
node.label = 'Coordinator';
|
|
2797
|
-
// delete node.color;
|
|
2798
|
-
}
|
|
2799
|
-
//console.warn(`node for device ${JSON.stringify(node)}`)
|
|
2800
|
-
return node;
|
|
2801
|
-
};
|
|
2802
2878
|
|
|
2803
|
-
|
|
2804
|
-
|
|
2805
|
-
|
|
2806
|
-
|
|
2807
|
-
|
|
2808
|
-
|
|
2879
|
+
if (map.lqis) {
|
|
2880
|
+
map.lqis.forEach((mapEntry) => {
|
|
2881
|
+
const dev = getDeviceByIEEE(mapEntry.ieeeAddr);
|
|
2882
|
+
if (!dev) {
|
|
2883
|
+
return;
|
|
2884
|
+
}
|
|
2809
2885
|
|
|
2810
|
-
|
|
2811
|
-
|
|
2812
|
-
|
|
2813
|
-
|
|
2814
|
-
|
|
2886
|
+
let node;
|
|
2887
|
+
if (!nodes.hasOwnProperty(mapEntry.ieeeAddr)) { // add node only once
|
|
2888
|
+
node = createNode(dev, mapEntry);
|
|
2889
|
+
if (node) {
|
|
2890
|
+
nodes[mapEntry.ieeeAddr] = node;
|
|
2891
|
+
}
|
|
2892
|
+
} else {
|
|
2893
|
+
node = nodes[mapEntry.ieeeAddr];
|
|
2815
2894
|
}
|
|
2816
|
-
|
|
2817
|
-
|
|
2818
|
-
|
|
2819
|
-
|
|
2820
|
-
|
|
2821
|
-
|
|
2822
|
-
|
|
2823
|
-
|
|
2824
|
-
|
|
2825
|
-
|
|
2826
|
-
|
|
2827
|
-
|
|
2828
|
-
const reverse = edges.find((edge) => {
|
|
2829
|
-
return (edge.to == from && edge.from == to);
|
|
2830
|
-
});
|
|
2895
|
+
if (node) {
|
|
2896
|
+
const parentDev = getDeviceByIEEE(mapEntry.parent);
|
|
2897
|
+
const to = parentDev ? parentDev._id : undefined;
|
|
2898
|
+
const from = dev._id;
|
|
2899
|
+
let label = mapEntry.lqi.toString();
|
|
2900
|
+
let linkColor = '#0000ff';
|
|
2901
|
+
let edge = edges.find((edge) => {
|
|
2902
|
+
return (edge.to == to && edge.from == from);
|
|
2903
|
+
});
|
|
2904
|
+
const reverse = edges.find((edge) => {
|
|
2905
|
+
return (edge.to == from && edge.from == to);
|
|
2906
|
+
});
|
|
2831
2907
|
|
|
2832
|
-
|
|
2833
|
-
|
|
2834
|
-
|
|
2835
|
-
|
|
2836
|
-
|
|
2908
|
+
if (mapEntry.relationship === 0 || mapEntry.relationship === 1) { // 0 - parent, 1 - child
|
|
2909
|
+
// // parent/child
|
|
2910
|
+
if (mapEntry.status !== 'online') {
|
|
2911
|
+
label = label + ' (off)';
|
|
2912
|
+
linkColor = '#660000';
|
|
2913
|
+
}
|
|
2914
|
+
if (mapEntry.lqi < 10) {
|
|
2915
|
+
linkColor = '#ff0000';
|
|
2916
|
+
}
|
|
2917
|
+
} else if (mapEntry.relationship === 2) { // sibling
|
|
2918
|
+
linkColor = '#00bb00';
|
|
2919
|
+
} else if (mapEntry.relationship === 3 && !reverse) { // unknown
|
|
2920
|
+
linkColor = '#aaaaff';
|
|
2921
|
+
} else if (mapEntry.relationship === 4) { // previous child
|
|
2922
|
+
linkColor = '#555555';
|
|
2837
2923
|
}
|
|
2838
|
-
if (
|
|
2839
|
-
|
|
2924
|
+
if (reverse) {
|
|
2925
|
+
// update reverse edge
|
|
2926
|
+
edge = reverse;
|
|
2927
|
+
edge.label += '\n' + label;
|
|
2928
|
+
edge.arrows.from = {enabled: false, scaleFactor: 0.5}; // start hidden if node is not selected
|
|
2929
|
+
if (mapEntry.relationship == 1) { //
|
|
2930
|
+
edge.color.color = linkColor;
|
|
2931
|
+
edge.color.highlight = linkColor;
|
|
2932
|
+
}
|
|
2933
|
+
} else if (!edge) {
|
|
2934
|
+
edge = {
|
|
2935
|
+
from: from,
|
|
2936
|
+
to: to,
|
|
2937
|
+
label: label,
|
|
2938
|
+
font: {
|
|
2939
|
+
align: 'middle',
|
|
2940
|
+
size: 0, // start hidden
|
|
2941
|
+
color: linkColor
|
|
2942
|
+
},
|
|
2943
|
+
arrows: {to: {enabled: false, scaleFactor: 0.5}},
|
|
2944
|
+
//arrowStrikethrough: false,
|
|
2945
|
+
color: {
|
|
2946
|
+
color: linkColor,
|
|
2947
|
+
opacity: 0, // start hidden
|
|
2948
|
+
highlight: linkColor
|
|
2949
|
+
},
|
|
2950
|
+
chosen: {
|
|
2951
|
+
edge: (values) => {
|
|
2952
|
+
values.opacity = 1.0;
|
|
2953
|
+
values.toArrow = true; // always existing
|
|
2954
|
+
values.fromArrow = values.fromArrowScale != 1 ? true : false; // simplified, arrow existing if scale is not default value
|
|
2955
|
+
},
|
|
2956
|
+
label: () => {
|
|
2957
|
+
// see onMapSelect workaround
|
|
2958
|
+
// values.size = 10;
|
|
2959
|
+
}
|
|
2960
|
+
},
|
|
2961
|
+
selectionWidth: 0,
|
|
2962
|
+
physics: mapEntry.relationship === 1 ? true : false,
|
|
2963
|
+
relationship: mapEntry.relationship
|
|
2964
|
+
};
|
|
2965
|
+
edges.push(edge);
|
|
2840
2966
|
}
|
|
2841
|
-
} else if (mapEntry.relationship === 2) { // sibling
|
|
2842
|
-
linkColor = '#00bb00';
|
|
2843
|
-
} else if (mapEntry.relationship === 3 && !reverse) { // unknown
|
|
2844
|
-
linkColor = '#aaaaff';
|
|
2845
|
-
} else if (mapEntry.relationship === 4) { // previous child
|
|
2846
|
-
linkColor = '#555555';
|
|
2847
2967
|
}
|
|
2848
|
-
|
|
2849
|
-
|
|
2850
|
-
|
|
2851
|
-
|
|
2852
|
-
|
|
2853
|
-
|
|
2854
|
-
|
|
2855
|
-
|
|
2856
|
-
|
|
2857
|
-
|
|
2858
|
-
|
|
2968
|
+
});
|
|
2969
|
+
}
|
|
2970
|
+
/*
|
|
2971
|
+
if (map.routing) {
|
|
2972
|
+
map.routing.forEach((route)=>{
|
|
2973
|
+
if (!route.nextHop) return;
|
|
2974
|
+
const routeSource = getDeviceByNetwork(route.nextHop);
|
|
2975
|
+
const routeDest = getDeviceByNetwork(route.destination);
|
|
2976
|
+
if (routeSource && routeDest) {
|
|
2977
|
+
const to = routeDest._id;
|
|
2978
|
+
const from = routeSource._id;
|
|
2979
|
+
const label = route.status;
|
|
2980
|
+
const linkColor = '#ff55ff';
|
|
2981
|
+
const edge = {
|
|
2859
2982
|
from: from,
|
|
2860
2983
|
to: to,
|
|
2861
2984
|
label: label,
|
|
@@ -2864,13 +2987,14 @@ function showNetworkMap(devices, map) {
|
|
|
2864
2987
|
size: 0, // start hidden
|
|
2865
2988
|
color: linkColor
|
|
2866
2989
|
},
|
|
2867
|
-
arrows: {to: {enabled: false, scaleFactor: 0.5}},
|
|
2990
|
+
arrows: { to: { enabled: false, scaleFactor: 0.5 }},
|
|
2868
2991
|
//arrowStrikethrough: false,
|
|
2869
2992
|
color: {
|
|
2870
2993
|
color: linkColor,
|
|
2871
|
-
opacity: 0, // start hidden
|
|
2994
|
+
//opacity: 0, // start hidden
|
|
2872
2995
|
highlight: linkColor
|
|
2873
2996
|
},
|
|
2997
|
+
dashes: true,
|
|
2874
2998
|
chosen: {
|
|
2875
2999
|
edge: (values) => {
|
|
2876
3000
|
values.opacity = 1.0;
|
|
@@ -2878,129 +3002,82 @@ function showNetworkMap(devices, map) {
|
|
|
2878
3002
|
values.fromArrow = values.fromArrowScale != 1 ? true : false; // simplified, arrow existing if scale is not default value
|
|
2879
3003
|
},
|
|
2880
3004
|
label: () => {
|
|
2881
|
-
|
|
3005
|
+
// see onMapSelect workaround
|
|
2882
3006
|
// values.size = 10;
|
|
2883
3007
|
}
|
|
2884
3008
|
},
|
|
2885
3009
|
selectionWidth: 0,
|
|
2886
|
-
physics:
|
|
2887
|
-
relationship: mapEntry.relationship
|
|
3010
|
+
physics: false,
|
|
2888
3011
|
};
|
|
2889
3012
|
edges.push(edge);
|
|
2890
3013
|
}
|
|
2891
|
-
}
|
|
2892
|
-
}
|
|
2893
|
-
|
|
2894
|
-
/*
|
|
2895
|
-
if (map.routing) {
|
|
2896
|
-
map.routing.forEach((route)=>{
|
|
2897
|
-
if (!route.nextHop) return;
|
|
2898
|
-
const routeSource = getDeviceByNetwork(route.nextHop);
|
|
2899
|
-
const routeDest = getDeviceByNetwork(route.destination);
|
|
2900
|
-
if (routeSource && routeDest) {
|
|
2901
|
-
const to = routeDest._id;
|
|
2902
|
-
const from = routeSource._id;
|
|
2903
|
-
const label = route.status;
|
|
2904
|
-
const linkColor = '#ff55ff';
|
|
2905
|
-
const edge = {
|
|
2906
|
-
from: from,
|
|
2907
|
-
to: to,
|
|
2908
|
-
label: label,
|
|
2909
|
-
font: {
|
|
2910
|
-
align: 'middle',
|
|
2911
|
-
size: 0, // start hidden
|
|
2912
|
-
color: linkColor
|
|
2913
|
-
},
|
|
2914
|
-
arrows: { to: { enabled: false, scaleFactor: 0.5 }},
|
|
2915
|
-
//arrowStrikethrough: false,
|
|
2916
|
-
color: {
|
|
2917
|
-
color: linkColor,
|
|
2918
|
-
//opacity: 0, // start hidden
|
|
2919
|
-
highlight: linkColor
|
|
2920
|
-
},
|
|
2921
|
-
dashes: true,
|
|
2922
|
-
chosen: {
|
|
2923
|
-
edge: (values) => {
|
|
2924
|
-
values.opacity = 1.0;
|
|
2925
|
-
values.toArrow = true; // always existing
|
|
2926
|
-
values.fromArrow = values.fromArrowScale != 1 ? true : false; // simplified, arrow existing if scale is not default value
|
|
2927
|
-
},
|
|
2928
|
-
label: () => {
|
|
2929
|
-
// see onMapSelect workaround
|
|
2930
|
-
// values.size = 10;
|
|
2931
|
-
}
|
|
2932
|
-
},
|
|
2933
|
-
selectionWidth: 0,
|
|
2934
|
-
physics: false,
|
|
2935
|
-
};
|
|
2936
|
-
edges.push(edge);
|
|
2937
|
-
}
|
|
2938
|
-
});
|
|
2939
|
-
}
|
|
2940
|
-
*/
|
|
3014
|
+
});
|
|
3015
|
+
}
|
|
3016
|
+
*/
|
|
2941
3017
|
|
|
2942
|
-
|
|
2943
|
-
|
|
2944
|
-
|
|
2945
|
-
|
|
2946
|
-
|
|
2947
|
-
|
|
2948
|
-
|
|
2949
|
-
|
|
3018
|
+
const nodesArray = Object.values(nodes);
|
|
3019
|
+
// add devices without network links to map
|
|
3020
|
+
devices.forEach((dev) => {
|
|
3021
|
+
const node = nodesArray.find((node) => {
|
|
3022
|
+
return node.id == dev._id;
|
|
3023
|
+
});
|
|
3024
|
+
if (!node) {
|
|
3025
|
+
const node = createNode(dev);
|
|
2950
3026
|
|
|
2951
|
-
|
|
2952
|
-
|
|
2953
|
-
|
|
2954
|
-
|
|
3027
|
+
if (node) {
|
|
3028
|
+
node.font = {color: '#ff0000'};
|
|
3029
|
+
if (dev.info && dev.info.device && dev.info.device.type == 'Coordinator') {
|
|
3030
|
+
node.font = {color: '#00ff00'};
|
|
3031
|
+
}
|
|
3032
|
+
nodesArray.push(node);
|
|
2955
3033
|
}
|
|
2956
|
-
nodesArray.push(node);
|
|
2957
3034
|
}
|
|
2958
|
-
}
|
|
2959
|
-
});
|
|
3035
|
+
});
|
|
2960
3036
|
|
|
2961
|
-
|
|
2962
|
-
|
|
2963
|
-
|
|
2964
|
-
|
|
2965
|
-
|
|
2966
|
-
|
|
2967
|
-
|
|
3037
|
+
// create a network
|
|
3038
|
+
const container = document.getElementById('map');
|
|
3039
|
+
mapEdges = new vis.DataSet(edges);
|
|
3040
|
+
const data = {
|
|
3041
|
+
nodes: nodesArray,
|
|
3042
|
+
edges: mapEdges
|
|
3043
|
+
};
|
|
2968
3044
|
|
|
2969
|
-
|
|
2970
|
-
|
|
2971
|
-
|
|
2972
|
-
|
|
2973
|
-
|
|
2974
|
-
|
|
2975
|
-
|
|
2976
|
-
|
|
2977
|
-
|
|
2978
|
-
|
|
2979
|
-
|
|
2980
|
-
|
|
2981
|
-
|
|
2982
|
-
|
|
2983
|
-
|
|
2984
|
-
|
|
2985
|
-
|
|
3045
|
+
network = new vis.Network(container, data, networkOptions);
|
|
3046
|
+
|
|
3047
|
+
const onMapSelect = function (event) {
|
|
3048
|
+
// workaround for https://github.com/almende/vis/issues/4112
|
|
3049
|
+
// may be moved to edge.chosen.label if fixed
|
|
3050
|
+
function doSelection(select, edges, data) {
|
|
3051
|
+
edges.forEach((edgeId => {
|
|
3052
|
+
const id = (typeof edgeId === 'string') ? edgeId : edgeId.id;
|
|
3053
|
+
const options = data.edges._data.get(id);
|
|
3054
|
+
if (select) {
|
|
3055
|
+
options.font.size = 15;
|
|
3056
|
+
} else {
|
|
3057
|
+
options.font.size = 0;
|
|
3058
|
+
}
|
|
3059
|
+
network.clustering.updateEdge(id, options);
|
|
3060
|
+
}));
|
|
3061
|
+
}
|
|
2986
3062
|
|
|
2987
|
-
|
|
2988
|
-
|
|
2989
|
-
|
|
2990
|
-
|
|
2991
|
-
|
|
2992
|
-
|
|
2993
|
-
|
|
2994
|
-
|
|
2995
|
-
|
|
2996
|
-
|
|
2997
|
-
|
|
2998
|
-
|
|
2999
|
-
|
|
3000
|
-
|
|
3001
|
-
|
|
3002
|
-
|
|
3003
|
-
|
|
3063
|
+
if (event.hasOwnProperty('previousSelection')) { // unselect previous selected
|
|
3064
|
+
doSelection(false, event.previousSelection.edges, this.body.data);
|
|
3065
|
+
}
|
|
3066
|
+
doSelection(true, event.edges, this.body.data);
|
|
3067
|
+
/*
|
|
3068
|
+
if (event.nodes) {
|
|
3069
|
+
event.nodes.forEach((node)=>{
|
|
3070
|
+
//const options = network.clustering.findNode[node];
|
|
3071
|
+
network.clustering.updateClusteredNode(
|
|
3072
|
+
node, {size: 50}
|
|
3073
|
+
);
|
|
3074
|
+
});
|
|
3075
|
+
}
|
|
3076
|
+
*/
|
|
3077
|
+
};
|
|
3078
|
+
network.on('selectNode', onMapSelect);
|
|
3079
|
+
network.on('deselectNode', onMapSelect);
|
|
3080
|
+
}
|
|
3004
3081
|
redrawMap();
|
|
3005
3082
|
updateMapFilter();
|
|
3006
3083
|
|
|
@@ -3651,19 +3728,19 @@ function selectBackup() {
|
|
|
3651
3728
|
const candidates = {};
|
|
3652
3729
|
for (const fn of msg.files) {
|
|
3653
3730
|
const m = fn.matchAll(/backup_([0-9]+)_([0-9]+)_([0-9]+)-([0-9]+)_([0-9]+)_([0-9]+)/gm);
|
|
3654
|
-
console.warn(`m is ${JSON.stringify(m)}`);
|
|
3731
|
+
//console.warn(`m is ${JSON.stringify(m)}`);
|
|
3655
3732
|
if (m) {
|
|
3656
3733
|
candidates[`${m[3]}.${m[2]}.${m[1]} ${m[4]}:${m[5]}`] = fn;
|
|
3657
3734
|
}
|
|
3658
3735
|
}
|
|
3659
|
-
console.warn('candidates is ' + JSON.stringify(candidates));
|
|
3736
|
+
//console.warn('candidates is ' + JSON.stringify(candidates));
|
|
3660
3737
|
list2select('#backup_Selector', msg.files, [], (key, val) => { return val; }, (key, val) => { return val; })
|
|
3661
3738
|
$('#modalrestore').modal('open');
|
|
3662
3739
|
const btn = $('#modalrestore .modal-content a.btn-large');
|
|
3663
3740
|
btn.unbind('click')
|
|
3664
3741
|
btn.click(function (e) {
|
|
3665
3742
|
const name = $('#backup_Selector').val();
|
|
3666
|
-
console.warn(` filename is ${name}`);
|
|
3743
|
+
//console.warn(` filename is ${name}`);
|
|
3667
3744
|
$('#modalrestore').modal('close');
|
|
3668
3745
|
showWaitingDialog(`Attempting to restore the backup from ${name}`, 180000);
|
|
3669
3746
|
const start = Date.now();
|
|
@@ -4164,7 +4241,7 @@ function showWaitingDialog(text, timeout) {
|
|
|
4164
4241
|
clearTimeout(waitingTimeout);
|
|
4165
4242
|
$('#modalWaiting').modal('close');
|
|
4166
4243
|
}, timeout * 1000);
|
|
4167
|
-
$('#waiting_message').text(text);
|
|
4244
|
+
$('#waiting_message').text(translateWord(text));
|
|
4168
4245
|
$('#modalWaiting').modal('open');
|
|
4169
4246
|
}
|
|
4170
4247
|
|
|
@@ -4460,6 +4537,14 @@ function doSort() {
|
|
|
4460
4537
|
shuffleInstance.sort({
|
|
4461
4538
|
by: sortByTitle
|
|
4462
4539
|
});
|
|
4540
|
+
} else if (sortOrder === 'range') {
|
|
4541
|
+
shuffleInstance.sort({
|
|
4542
|
+
by: sortByRange
|
|
4543
|
+
});
|
|
4544
|
+
} else if (sortOrder === 'load') {
|
|
4545
|
+
shuffleInstance.sort({
|
|
4546
|
+
by: sortByLoad
|
|
4547
|
+
});
|
|
4463
4548
|
}
|
|
4464
4549
|
}
|
|
4465
4550
|
}
|
|
@@ -4467,6 +4552,30 @@ function doSort() {
|
|
|
4467
4552
|
function sortByTitle(element) {
|
|
4468
4553
|
return element.querySelector('.card-title').textContent.toLowerCase().trim();
|
|
4469
4554
|
}
|
|
4555
|
+
function sortByRange(element) {
|
|
4556
|
+
try {
|
|
4557
|
+
const lqNode = element.querySelector('[id$="_link_quality"]');
|
|
4558
|
+
if (!lqNode) return 0; // kein Wert -> ans Ende
|
|
4559
|
+
const txt = lqNode.textContent || lqNode.innerText || '';
|
|
4560
|
+
const m = txt.match(/-?\d+(.\d+)?/);
|
|
4561
|
+
const val = m ? parseFloat(m[0]) : 0;
|
|
4562
|
+
return -val;
|
|
4563
|
+
} catch (e) {
|
|
4564
|
+
return 0;
|
|
4565
|
+
}
|
|
4566
|
+
}
|
|
4567
|
+
function sortByLoad(element) {
|
|
4568
|
+
try {
|
|
4569
|
+
const battNode = element.querySelector('[id$="_battery"]');
|
|
4570
|
+
if (!battNode) return 0;
|
|
4571
|
+
const txt = battNode.textContent || battNode.innerText || '';
|
|
4572
|
+
const m = txt.match(/-?\d+(.\d+)?/);
|
|
4573
|
+
const val = m ? parseFloat(m[0]) : 0;
|
|
4574
|
+
return -val;
|
|
4575
|
+
} catch (e) {
|
|
4576
|
+
return 0;
|
|
4577
|
+
}
|
|
4578
|
+
}
|
|
4470
4579
|
|
|
4471
4580
|
|
|
4472
4581
|
function updateDevice(id) {
|
|
@@ -4500,7 +4609,7 @@ function removeDevice(id) {
|
|
|
4500
4609
|
|
|
4501
4610
|
function swapActive(id) {
|
|
4502
4611
|
const dev = getDeviceByID(id) || getDeviceByIEEE(`0x${id}`);
|
|
4503
|
-
console.warn(`swap_active for ${id} -> ${JSON.stringify(dev)}`);
|
|
4612
|
+
//console.warn(`swap_active for ${id} -> ${JSON.stringify(dev)}`);
|
|
4504
4613
|
if (dev && dev.common) {
|
|
4505
4614
|
dev.common.deactivated = !(dev.common.deactivated);
|
|
4506
4615
|
sendToWrapper(namespace, 'setDeviceActivated', {id: id, deactivated: dev.common.deactivated}, function () {
|