iobroker.zigbee 1.8.1 → 1.8.3

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.
Files changed (89) hide show
  1. package/.eslintignore +2 -0
  2. package/.eslintrc.json +37 -0
  3. package/.github/FUNDING.yml +3 -0
  4. package/.github/auto-merge.yml +17 -0
  5. package/.github/dependabot.yml +24 -0
  6. package/.github/stale.yml +13 -0
  7. package/.github/workflows/codeql.yml +41 -0
  8. package/.github/workflows/dependabot-automerge.yml +22 -0
  9. package/.github/workflows/test-and-release.yml +149 -0
  10. package/.releaseconfig.json +3 -0
  11. package/.travis/wiki.sh +28 -0
  12. package/.travis.yml +41 -0
  13. package/README.md +31 -8
  14. package/admin/admin.js +466 -482
  15. package/admin/i18n/de/translations.json +2 -2
  16. package/admin/index_m.html +1 -1
  17. package/admin/tab_m.html +3 -44
  18. package/admin/words.js +2 -2
  19. package/gulpfile.js +464 -0
  20. package/io-package.json +18 -25
  21. package/lib/backup.js +2 -2
  22. package/lib/binding.js +37 -32
  23. package/lib/colors.js +158 -163
  24. package/lib/commands.js +90 -99
  25. package/lib/developer.js +12 -9
  26. package/lib/devices.js +179 -169
  27. package/lib/exclude.js +36 -30
  28. package/lib/exposes.js +139 -163
  29. package/lib/groups.js +83 -81
  30. package/lib/json.js +6 -5
  31. package/lib/networkmap.js +3 -2
  32. package/lib/ota.js +18 -34
  33. package/lib/rgb.js +72 -114
  34. package/lib/seriallist.js +20 -25
  35. package/lib/states.js +526 -511
  36. package/lib/statescontroller.js +183 -206
  37. package/lib/utils.js +23 -24
  38. package/lib/zbBaseExtension.js +4 -4
  39. package/lib/zbDelayedAction.js +13 -5
  40. package/lib/zbDeviceAvailability.js +65 -69
  41. package/lib/zbDeviceConfigure.js +21 -9
  42. package/lib/zbDeviceEvent.js +4 -3
  43. package/lib/zigbeecontroller.js +103 -109
  44. package/main.js +147 -163
  45. package/package.json +15 -29
  46. package/test/integration.js +5 -0
  47. package/test/mocha.custom.opts +2 -0
  48. package/test/mocha.setup.js +14 -0
  49. package/test/package.js +5 -0
  50. package/test/unit.js +5 -0
  51. package/docs/de/img/CC2531.png +0 -0
  52. package/docs/de/img/CC2538_CC2592_PA.PNG +0 -0
  53. package/docs/de/img/CC2591.png +0 -0
  54. package/docs/de/img/boards.jpg +0 -0
  55. package/docs/de/img/cc26x2r.PNG +0 -0
  56. package/docs/de/img/results.jpg +0 -0
  57. package/docs/de/img/sku_429478_2.png +0 -0
  58. package/docs/de/img/sku_429601_2.png +0 -0
  59. package/docs/de/readme.md +0 -27
  60. package/docs/en/img/CC2531.png +0 -0
  61. package/docs/en/img/CC2591.png +0 -0
  62. package/docs/en/img/deconz.png +0 -0
  63. package/docs/en/img/sku_429478_2.png +0 -0
  64. package/docs/en/img/sku_429601_2.png +0 -0
  65. package/docs/en/readme.md +0 -30
  66. package/docs/flashing_via_arduino_(en).md +0 -110
  67. package/docs/ru/img/CC2531.png +0 -0
  68. package/docs/ru/img/CC2591.png +0 -0
  69. package/docs/ru/img/sku_429478_2.png +0 -0
  70. package/docs/ru/img/sku_429601_2.png +0 -0
  71. package/docs/ru/readme.md +0 -28
  72. package/docs/tutorial/CC2530_20190425.zip +0 -0
  73. package/docs/tutorial/CC2530_CC2591_20190515.zip +0 -0
  74. package/docs/tutorial/CC2530_CC2592_20190515.zip +0 -0
  75. package/docs/tutorial/CC2531_20190425.zip +0 -0
  76. package/docs/tutorial/adm5_1.PNG +0 -0
  77. package/docs/tutorial/adm5_2.PNG +0 -0
  78. package/docs/tutorial/cat.PNG +0 -0
  79. package/docs/tutorial/groups-1.png +0 -0
  80. package/docs/tutorial/groups-2.png +0 -0
  81. package/docs/tutorial/inst.PNG +0 -0
  82. package/docs/tutorial/reflash-finish.PNG +0 -0
  83. package/docs/tutorial/reflash-step0.png +0 -0
  84. package/docs/tutorial/reflash-step1.PNG +0 -0
  85. package/docs/tutorial/reflash-step2.PNG +0 -0
  86. package/docs/tutorial/settings.png +0 -0
  87. package/docs/tutorial/tab-dev-1.png +0 -0
  88. package/docs/tutorial/zigbee.png +0 -0
  89. package/docs/tutorial/zigbee15.png +0 -0
package/admin/admin.js CHANGED
@@ -38,7 +38,7 @@ function getDeviceByID(ID) {
38
38
  return devices.find((devInfo) => {
39
39
  try {
40
40
  return devInfo._id == ID;
41
- } catch (e) {
41
+ } catch (e) {
42
42
  //console.log("No dev with ieee " + ieeeAddr);
43
43
  }
44
44
  });
@@ -48,18 +48,17 @@ function getDevice(ieeeAddr) {
48
48
  return devices.find((devInfo) => {
49
49
  try {
50
50
  return devInfo.info.device._ieeeAddr == ieeeAddr;
51
- } catch (e) {
51
+ } catch (e) {
52
52
  //console.log("No dev with ieee " + ieeeAddr);
53
53
  }
54
54
  });
55
55
  }
56
-
57
56
  // eslint-disable-next-line no-unused-vars
58
57
  function getDeviceByNetwork(nwk) {
59
58
  return devices.find((devInfo) => {
60
59
  try {
61
60
  return devInfo.info.device._networkAddress == nwk;
62
- } catch (e) {
61
+ } catch (e) {
63
62
  //console.log("No dev with nwkAddr " + nwk);
64
63
  }
65
64
  });
@@ -126,12 +125,12 @@ function getCoordinatorCard(dev) {
126
125
  }
127
126
 
128
127
  function getGroupCard(dev) {
129
- const id = (dev._id ? dev._id : ''),
128
+ const id = (dev._id ? dev._id: ''),
130
129
  title = dev.common.name,
131
130
  lq = '<div class="col tool"><i class="material-icons icon-green">check_circle</i></div>',
132
131
  rooms = [],
133
- numid = parseInt(id.replace(namespace + '.group_', '')),
134
- lang = systemLang || 'en';
132
+ numid = parseInt(id.replace(namespace+'.group_', '')),
133
+ lang = systemLang || 'en';
135
134
  for (const r in dev.rooms) {
136
135
  if (dev.rooms[r].hasOwnProperty(lang)) {
137
136
  rooms.push(dev.rooms[r][lang]);
@@ -144,20 +143,19 @@ function getGroupCard(dev) {
144
143
  let memberCount = 0;
145
144
  let info = `<div style="min-height:88px; font-size: 0.8em; height: 98px; overflow-y: auto" class="truncate">
146
145
  <ul>`;
147
- info = info.concat(`<li><span class="labelinfo">Group ${numid}</span></li>`);
146
+ info = info.concat(`<li><span class="labelinfo">Group ${numid}</span></li>`);
148
147
  if (dev.memberinfo === undefined) {
149
148
  info = info.concat(`<li><span class="labelinfo">No devices in group</span></li>`);
150
149
  } else {
151
- for (let m = 0; m < dev.memberinfo.length; m++) {
150
+ for (let m=0;m < dev.memberinfo.length; m++) {
152
151
  info = info.concat(`<li><span align:"left">${dev.memberinfo[m].device}.${dev.memberinfo[m].epid}</span><span align:"right"> ...${dev.memberinfo[m].ieee.slice(-4)}</span></li>`);
153
152
  }
154
- memberCount = (dev.memberinfo.length < 8 ? dev.memberinfo.length : 7);
155
- }
156
- ;
153
+ memberCount = (dev.memberinfo.length<8?dev.memberinfo.length:7);
154
+ };
157
155
  info = info.concat(` </ul>
158
156
  </div>`);
159
157
  const image = `<img src="img/group_${memberCount}.png" width="80px" onerror="this.onerror=null;this.src='img/unavailable.png';">`;
160
- const dashCard = getDashCard(dev, `img/group_${memberCount}.png`);
158
+ const dashCard = getDashCard(dev,`img/group_${memberCount}.png` );
161
159
  const card = `<div id="${id}" class="device group">
162
160
  <div class="card hoverable flipable">
163
161
  <div class="front face">${dashCard}</div>
@@ -204,7 +202,7 @@ function getCard(dev) {
204
202
  img_src = dev.icon || dev.common.icon,
205
203
  rooms = [],
206
204
  isActive = (dev.common.deactivated ? false : true),
207
- lang = systemLang || 'en';
205
+ lang = systemLang || 'en';
208
206
  for (const r in dev.rooms) {
209
207
  if (dev.rooms[r].hasOwnProperty(lang)) {
210
208
  rooms.push(dev.rooms[r][lang]);
@@ -218,25 +216,25 @@ function getCard(dev) {
218
216
  const modelUrl = (!type) ? '' : `<a href="https://www.zigbee2mqtt.io/devices/${type_url}.html" target="_blank" rel="noopener noreferrer">${type}</a>`;
219
217
  const image = `<img src="${img_src}" width="80px" onerror="this.onerror=null;this.src='img/unavailable.png';">`,
220
218
  nwk = (dev.info && dev.info.device) ? dev.info.device._networkAddress : undefined,
221
- battery_cls = (isActive ? getBatteryCls(dev.battery) : ''),
219
+ battery_cls = (isActive ? getBatteryCls(dev.battery):''),
222
220
  lqi_cls = getLQICls(dev.link_quality),
223
221
  battery = (dev.battery && isActive) ? `<div class="col tool"><i id="${rid}_battery_icon" class="material-icons ${battery_cls}">battery_std</i><div id="${rid}_battery" class="center" style="font-size:0.7em">${dev.battery}</div></div>` : '',
224
222
  lq = (dev.link_quality > 0 && isActive) ? `<div class="col tool"><i id="${rid}_link_quality_icon" class="material-icons ${lqi_cls}">network_check</i><div id="${rid}_link_quality" class="center" style="font-size:0.7em">${dev.link_quality}</div></div>` : '',
225
- status = (dev.link_quality > 0 && isActive) ? `<div class="col tool"><i class="material-icons icon-green">check_circle</i></div>` : (isActive ? `<div class="col tool"><i class="material-icons icon-black">leak_remove</i></div>` : ''),
223
+ status = (dev.link_quality > 0 && isActive) ? `<div class="col tool"><i class="material-icons icon-green">check_circle</i></div>` : (isActive ? `<div class="col tool"><i class="material-icons icon-black">leak_remove</i></div>`:''),
226
224
  info = `<div style="min-height:88px; font-size: 0.8em" class="truncate">
227
225
  <ul>
228
- <li><span class="labelinfo">ieee:</span><span>0x${id.replace(namespace + '.', '')}</span></li>
229
- <li><span class="labelinfo">nwk:</span><span>${(nwk) ? nwk.toString() + ' (0x' + nwk.toString(16) + ')' : ''}</span></li>
226
+ <li><span class="labelinfo">ieee:</span><span>0x${id.replace(namespace+'.', '')}</span></li>
227
+ <li><span class="labelinfo">nwk:</span><span>${(nwk) ? nwk.toString()+' (0x'+nwk.toString(16)+')' : ''}</span></li>
230
228
  <li><span class="labelinfo">model:</span><span>${modelUrl}</span></li>
231
229
  <li><span class="labelinfo">groups:</span><span>${dev.groupNames || ''}</span></li>
232
230
  </ul>
233
231
  </div>`,
234
232
  permitJoinBtn = (dev.info && dev.info.device._type == 'Router') ? '<button name="join" class="btn-floating btn-small waves-effect waves-light right hoverable green"><i class="material-icons tiny">leak_add</i></button>' : '',
235
- deactBtn = `<button name="swapactive" class="right btn-flat btn-small tooltipped" title="${(isActive ? 'Deactivate' : 'Activate')}"><i class="material-icons icon-${(isActive ? 'red' : 'green')}">power_settings_new</i></button>`,
233
+ deactBtn = `<button name="swapactive" class="right btn-flat btn-small tooltipped" title="${(isActive?'Deactivate':'Activate')}"><i class="material-icons icon-${(isActive?'red':'green')}">power_settings_new</i></button>`,
236
234
  infoBtn = (nwk) ? `<button name="info" class="left btn-flat btn-small"><i class="material-icons icon-blue">info</i></button>` : '';
237
235
  const dashCard = getDashCard(dev);
238
236
  const card = `<div id="${id}" class="device">
239
- <div class="card hoverable flipable ${isActive ? '' : 'bg_red'}">
237
+ <div class="card hoverable flipable ${isActive?'':'bg_red'}">
240
238
  <div class="front face">${dashCard}</div>
241
239
  <div class="back face">
242
240
  <div class="card-content zcard">
@@ -276,7 +274,6 @@ function getCard(dev) {
276
274
  </div>`;
277
275
  return card;
278
276
  }
279
-
280
277
  /*
281
278
  function openReval(e, id, name){
282
279
  const $card = $(e.target).closest('.card');
@@ -304,7 +301,7 @@ function openReval(e, id, name){
304
301
  });
305
302
  }
306
303
  */
307
- function closeReval(e, id) {
304
+ function closeReval(e, id){
308
305
  const $cardReveal = $(e.target).closest('.card-reveal');
309
306
  const $revealName = $cardReveal[0].getAttribute('name');
310
307
  if ($revealName == 'edit' && id) {
@@ -323,21 +320,21 @@ function closeReval(e, id) {
323
320
  translateY: 0,
324
321
  duration: 225,
325
322
  easing: 'easeInOutQuad',
326
- complete: function (anim) {
323
+ complete: function(anim) {
327
324
  const el = anim.animatables[0].target;
328
- $(el).css({display: 'none'});
325
+ $(el).css({ display: 'none'});
329
326
  $card.css('overflow', $card.data('initialOverflow'));
330
327
  }
331
328
  });
332
329
  }
333
330
 
334
331
  function deleteConfirmation(id, name) {
335
- const text = translateWord('Do you really want to delete device') + ' "' + name + '" (' + id + ')?';
332
+ const text = translateWord('Do you really want to delete device') + ' "'+name+'" ('+id+')?';
336
333
  $('#modaldelete').find('p').text(text);
337
334
  $('#force').prop('checked', false);
338
335
  $('#forcediv').removeClass('hide');
339
- $('#modaldelete a.btn[name=\'yes\']').unbind('click');
340
- $('#modaldelete a.btn[name=\'yes\']').click(() => {
336
+ $("#modaldelete a.btn[name='yes']").unbind('click');
337
+ $("#modaldelete a.btn[name='yes']").click(() => {
341
338
  const force = $('#force').prop('checked');
342
339
  deleteDevice(id, force);
343
340
  });
@@ -350,8 +347,8 @@ function cleanConfirmation() {
350
347
  $('#modalclean').find('p').text(text);
351
348
  $('#cforce').prop('checked', false);
352
349
  $('#cforcediv').removeClass('hide');
353
- $('#modalclean a.btn[name=\'yes\']').unbind('click');
354
- $('#modalclean a.btn[name=\'yes\']').click(() => {
350
+ $("#modalclean a.btn[name='yes']").unbind('click');
351
+ $("#modalclean a.btn[name='yes']").click(() => {
355
352
  const force = $('#cforce').prop('checked');
356
353
  cleanDeviceStates(force);
357
354
  });
@@ -359,75 +356,78 @@ function cleanConfirmation() {
359
356
  Materialize.updateTextFields();
360
357
  }
361
358
 
362
- function EndPointIDfromEndPoint(ep) {
363
- if (ep && ep.deviceIeeeAddress && ep.ID)
364
- return `${ep.deviceIeeeAddress}:${ep.ID}`;
365
- return 'unidentified';
359
+ function EndPointIDfromEndPoint(ep)
360
+ {
361
+ if (ep && ep.deviceIeeeAddress && ep.ID)
362
+ return `${ep.deviceIeeeAddress}:${ep.ID}`;
363
+ return 'unidentified';
366
364
  }
367
365
 
368
366
  function editName(id, name) {
369
- console.log('editName called with ' + name);
367
+ console.log('editName called with '+name);
370
368
  const dev = devices.find((d) => d._id == id);
371
- $('#modaledit').find('input[id=\'d_name\']').val(name);
369
+ $('#modaledit').find("input[id='d_name']").val(name);
372
370
  // if (dev.info && dev.info.device._type == 'Router') {
373
- const groupables = [];
374
- if (dev && dev.info && dev.info.endpoints) {
371
+ const groupables = [];
372
+ if (dev && dev.info && dev.info.endpoints) {
375
373
  for (const ep of dev.info.endpoints) {
376
- if (ep.inputClusters.includes(4)) {
377
- groupables.push({epid: EndPointIDfromEndPoint(ep), ep: ep, memberOf: []});
378
- }
374
+ if (ep.inputClusters.includes(4)) {
375
+ groupables.push({ epid:EndPointIDfromEndPoint(ep), ep:ep, memberOf:[]});
376
+ }
379
377
  }
380
- }
381
- const numEP = groupables.length;
378
+ }
379
+ const numEP = groupables.length;
382
380
  // console.log('groupables: '+JSON.stringify(groupables));
383
- $('#modaledit').find('.row.epid0').addClass('hide');
384
- $('#modaledit').find('.row.epid1').addClass('hide');
385
- $('#modaledit').find('.row.epid2').addClass('hide');
386
- $('#modaledit').find('.row.epid3').addClass('hide');
387
- if (numEP > 0) {
381
+ $('#modaledit').find('.row.epid0').addClass('hide');
382
+ $('#modaledit').find('.row.epid1').addClass('hide');
383
+ $('#modaledit').find('.row.epid2').addClass('hide');
384
+ $('#modaledit').find('.row.epid3').addClass('hide');
385
+ if (numEP > 0) {
388
386
  // go through all the groups. Find the ones to list for each groupable
389
387
  if (numEP == 1) {
390
- $('#modaledit').find('.endpointid').addClass('hide');
391
- } else {
392
- $('#modaledit').find('.endpointid').removeClass('hide');
388
+ $('#modaledit').find('.endpointid').addClass('hide');
389
+ }
390
+ else {
391
+ $('#modaledit').find('.endpointid').removeClass('hide');
393
392
  }
394
393
  for (const d of devices) {
395
- if (d && d.common && d.common.type == 'group') {
396
- if (d.hasOwnProperty('memberinfo')) {
397
- for (const member of d.memberinfo) {
398
- const epid = EndPointIDfromEndPoint(member.ep);
399
- for (var i = 0; i < groupables.length; i++) {
400
- if (groupables[i].epid == epid) {
401
- groupables[i].memberOf.push(d.native.id.replace('group_', ''));
402
- }
403
- }
404
- }
394
+ if (d && d.common && d.common.type == 'group') {
395
+ if (d.hasOwnProperty("memberinfo")) {
396
+ for (const member of d.memberinfo) {
397
+ const epid = EndPointIDfromEndPoint(member.ep)
398
+ for (var i=0;i<groupables.length;i++) {
399
+ if (groupables[i].epid == epid) {
400
+ groupables[i].memberOf.push(d.native.id.replace('group_', ''));
401
+ }
405
402
  }
403
+ }
406
404
  }
405
+ }
407
406
  }
408
- console.log('groupables: ' + JSON.stringify(groupables));
409
- for (var i = 0; i < groupables.length; i++) {
410
- if (i > 1) {
411
- $('#modaledit').find('translate.device_with_endpoint').innerHtml = name + ' ' + groupables[i].epid;
412
- }
413
- $('#modaledit').find('.row.epid' + i).removeClass('hide');
414
- list2select('#d_groups_ep' + i, groups, groupables[i].memberOf || []);
407
+ console.log("groupables: " + JSON.stringify(groupables));
408
+ for (var i = 0;i<groupables.length;i++)
409
+ {
410
+ if (i > 1) {
411
+ $('#modaledit').find("translate.device_with_endpoint").innerHtml = name + ' ' + groupables[i].epid;
412
+ }
413
+ $('#modaledit').find('.row.epid'+i).removeClass('hide');
414
+ list2select('#d_groups_ep'+i, groups, groupables[i].memberOf || []);
415
415
  }
416
- }
416
+ }
417
417
  // } else {
418
418
  // $('#modaledit').find('.input-field.endpoints').addClass('hide');
419
419
  // $('#modaledit').find('.input-field.groups').addClass('hide');
420
420
  // }
421
- $('#modaledit a.btn[name=\'save\']').unbind('click');
422
- $('#modaledit a.btn[name=\'save\']').click(() => {
423
- const newName = $('#modaledit').find('input[id=\'d_name\']').val();
421
+ $("#modaledit a.btn[name='save']").unbind('click');
422
+ $("#modaledit a.btn[name='save']").click(() => {
423
+ const newName = $('#modaledit').find("input[id='d_name']").val();
424
424
  const groupsbyid = {};
425
- if (groupables.length > 0) {
426
- for (var i = 0; i < groupables.length; i++) {
427
- const ng = $('#d_groups_ep' + i).val();
428
- if (ng.toString() != groupables[i].memberOf.toString())
429
- groupsbyid[groupables[i].ep.ID] = GenerateGroupChange(groupables[i].memberOf, ng);
430
- }
425
+ if (groupables.length > 0) {
426
+ for (var i = 0;i<groupables.length;i++) {
427
+ const ng = $('#d_groups_ep'+i).val();
428
+ if (ng.toString() != groupables[i].memberOf.toString())
429
+ groupsbyid[groupables[i].ep.ID] = GenerateGroupChange(groupables[i].memberOf, ng);
430
+ }
431
431
  }
432
432
  console.log('grpid ' + JSON.stringify(groupsbyid));
433
433
  updateDev(id, newName, groupsbyid);
@@ -436,13 +436,14 @@ function editName(id, name) {
436
436
  Materialize.updateTextFields();
437
437
  }
438
438
 
439
- function GenerateGroupChange(oldmembers, newmembers) {
440
- let grpchng = [];
441
- for (const oldg of oldmembers)
442
- if (!newmembers.includes(oldg)) grpchng.push('-' + oldg);
443
- for (const newg of newmembers)
444
- if (!oldmembers.includes(newg)) grpchng.push(newg);
445
- return grpchng;
439
+ function GenerateGroupChange(oldmembers, newmembers)
440
+ {
441
+ let grpchng = [];
442
+ for (const oldg of oldmembers)
443
+ if (!newmembers.includes(oldg)) grpchng.push('-'+oldg);
444
+ for (const newg of newmembers)
445
+ if (!oldmembers.includes(newg)) grpchng.push(newg)
446
+ return grpchng;
446
447
  }
447
448
 
448
449
  function deleteDevice(id, force) {
@@ -473,7 +474,6 @@ function cleanDeviceStates(force) {
473
474
  });
474
475
  showWaitingDialog('Device is being removed', 10);
475
476
  }
476
-
477
477
  function renameDevice(id, name) {
478
478
  sendTo(namespace, 'renameDevice', {id: id, name: name}, function (msg) {
479
479
  if (msg) {
@@ -490,7 +490,7 @@ function showDevices() {
490
490
  let html = '';
491
491
  const lang = systemLang || 'en';
492
492
  // sort by rooms
493
- devices.sort((a, b) => {
493
+ devices.sort((a, b)=>{
494
494
  const roomsA = [], roomsB = [];
495
495
  for (const r in a.rooms) {
496
496
  if (a.rooms[r].hasOwnProperty(lang)) {
@@ -517,28 +517,29 @@ function showDevices() {
517
517
  }
518
518
  return 0;
519
519
  });
520
- for (let i = 0; i < devices.length; i++) {
520
+ for (let i=0;i < devices.length; i++) {
521
521
  const d = devices[i];
522
522
  if (!d.info) {
523
- if (d.common && d.common.type == 'group') {
523
+ if (d.common && d.common.type == 'group')
524
+ {
524
525
  const card = getGroupCard(d);
525
526
  html += card;
526
527
  continue;
527
528
  }
528
- }
529
- ;
529
+ };
530
530
  if (d.info && d.info.device._type == 'Coordinator') {
531
531
  const card = getCoordinatorCard(d);
532
532
  html += card;
533
533
  } else {
534
- //if (d.groups && d.info && d.info.device._type == "Router") {
534
+ //if (d.groups && d.info && d.info.device._type == "Router") {
535
535
  if (d.groups) {
536
536
  // devGroups[d._id] = d.groups;
537
537
  if (typeof d.groups.map == 'function') {
538
- d.groupNames = d.groups.map(item => {
538
+ d.groupNames = d.groups.map(item=>{
539
539
  return groups[item] || '';
540
540
  }).join(', ');
541
- } else {
541
+ }
542
+ else {
542
543
  d.groupNames = '..';
543
544
  }
544
545
  }
@@ -550,13 +551,13 @@ function showDevices() {
550
551
  hookControls();
551
552
 
552
553
  // update rooms filter
553
- const allRooms = new Set(devices.map((item) => item.rooms).flat().map((room) => {
554
+ const allRooms = new Set(devices.map((item)=>item.rooms).flat().map((room)=>{
554
555
  if (room && room.hasOwnProperty(lang)) {
555
556
  return room[lang];
556
557
  } else {
557
558
  return room;
558
559
  }
559
- }).filter((item) => item != undefined));
560
+ }).filter((item)=>item != undefined));
560
561
  const roomSelector = $('#room-filter');
561
562
  roomSelector.empty();
562
563
  roomSelector.append(`<li class="device-order-item" data-type="All" tabindex="0"><a class="translate" data-lang="All">All</a></li>`);
@@ -567,69 +568,69 @@ function showDevices() {
567
568
  $('#room-filter-btn').text($(this).text());
568
569
  doFilter();
569
570
  });
570
- $('.flip').click(function () {
571
- const card = $(this).parents('.card');
572
- card.toggleClass('flipped');
571
+ $(".flip").click(function(){
572
+ const card = $(this).parents(".card");
573
+ card.toggleClass("flipped");
573
574
  });
574
575
  $('#rotate_btn').click(function () {
575
- $('.card.flipable').toggleClass('flipped');
576
+ $('.card.flipable').toggleClass("flipped");
576
577
  });
577
578
 
578
- shuffleInstance = new Shuffle($('#devices'), {
579
+ shuffleInstance = new Shuffle($("#devices"), {
579
580
  itemSelector: '.device',
580
581
  sizer: '.js-shuffle-sizer',
581
582
  });
582
583
  doFilter();
583
584
 
584
- const getDevName = function (dev_block) {
585
+ const getDevName = function(dev_block) {
585
586
  return dev_block.find('#dName').text();
586
587
  };
587
- const getDevId = function (dev_block) {
588
+ const getDevId = function(dev_block) {
588
589
  return dev_block.attr('id');
589
590
  };
590
- $('.card-reveal-buttons button[name=\'delete\']').click(function () {
591
+ $(".card-reveal-buttons button[name='delete']").click(function() {
591
592
  const dev_block = $(this).parents('div.device');
592
593
  deleteConfirmation(getDevId(dev_block), getDevName(dev_block));
593
594
  });
594
- $('.card-reveal-buttons button[name=\'deletegrp\']').click(function () {
595
+ $(".card-reveal-buttons button[name='deletegrp']").click(function() {
595
596
  const dev_block = $(this).parents('div.device');
596
- const id = dev_block.attr('id').replace(namespace + '.group_', '');
597
+ const id = dev_block.attr('id').replace(namespace+'.group_', '');
597
598
  deleteGroupConfirmation(id, getDevName(dev_block));
598
599
  });
599
- $('.card-reveal-buttons button[name=\'edit\']').click(function () {
600
+ $(".card-reveal-buttons button[name='edit']").click(function() {
600
601
  const dev_block = $(this).parents('div.device'),
601
602
  id = getDevId(dev_block),
602
603
  name = getDevName(dev_block);
603
604
  editName(id, name);
604
605
  });
605
- $('.card-reveal-buttons button[name=\'editgrp\']').click(function () {
606
+ $(".card-reveal-buttons button[name='editgrp']").click(function() {
606
607
  const dev_block = $(this).parents('div.device'),
607
- id = dev_block.attr('id').replace(namespace + '.group_', ''),
608
+ id = dev_block.attr('id').replace(namespace+'.group_', ''),
608
609
  name = getDevName(dev_block);
609
610
  editGroupName(id, name, false);
610
611
  });
611
- $('button.btn-floating[name=\'join\']').click(function () {
612
+ $("button.btn-floating[name='join']").click(function() {
612
613
  const dev_block = $(this).parents('div.device');
613
614
  if (!$('#pairing').hasClass('pulse'))
614
615
  joinProcess(getDevId(dev_block));
615
616
  showPairingProcess();
616
617
  });
617
- $('.card-reveal-buttons button[name=\'info\']').click(function () {
618
+ $(".card-reveal-buttons button[name='info']").click(function() {
618
619
  const dev_block = $(this).parents('div.device');
619
620
  showDevInfo(getDevId(dev_block));
620
621
  });
621
- $('a.btn[name=\'done\']').click((e) => {
622
+ $("a.btn[name='done']").click((e) => {
622
623
  const dev_block = $(this).parents('div.device');
623
624
  closeReval(e, getDevId(dev_block), getDevName(dev_block));
624
625
  });
625
- $('a.btn-flat[name=\'close\']').click((e) => {
626
+ $("a.btn-flat[name='close']").click((e) => {
626
627
  closeReval(e);
627
628
  });
628
- $('.card-reveal-buttons button[name=\'reconfigure\']').click(function () {
629
+ $(".card-reveal-buttons button[name='reconfigure']").click(function() {
629
630
  const dev_block = $(this).parents('div.device');
630
631
  reconfigureDlg(getDevId(dev_block));
631
632
  });
632
- $('.card-reveal-buttons button[name=\'swapactive\']').click(function () {
633
+ $(".card-reveal-buttons button[name='swapactive']").click(function() {
633
634
  const dev_block = $(this).parents('div.device');
634
635
  swapActive(getDevId(dev_block));
635
636
  });
@@ -640,10 +641,10 @@ function showDevices() {
640
641
 
641
642
  function checkFwUpdate() {
642
643
  const deviceCards = getDeviceCards();
643
- const getFwInfoNode = function (deviceCard) {
644
+ const getFwInfoNode = function(deviceCard) {
644
645
  return deviceCard.find('.fw_info');
645
646
  };
646
- const createBtn = function (icon, hint, disabled, color) {
647
+ const createBtn = function(icon, hint, disabled, color) {
647
648
  const disabledAttr = disabled ? '[disabled]="true"' : '';
648
649
  if (!color) {
649
650
  color = !disabled ? 'icon-green' : '';
@@ -651,14 +652,14 @@ function checkFwUpdate() {
651
652
  return `<button name="fw_update" class="left btn-flat btn-small" title="${hint}" ${disabledAttr}>
652
653
  <i class="material-icons ${color}">${icon}</i></button>`;
653
654
  };
654
- const callback = function (msg) {
655
+ const callback = function(msg) {
655
656
  if (msg) {
656
657
  const deviceCard = getDeviceCard(msg.device);
657
658
  const devId = getDevId(deviceCard.attr('id'));
658
659
  const fwInfoNode = getFwInfoNode(deviceCard);
659
660
  if (msg.status == 'available') {
660
661
  fwInfoNode.html(createBtn('system_update', 'Click to start firmware update', false));
661
- $(fwInfoNode).find('button[name=\'fw_update\']').click(() => {
662
+ $(fwInfoNode).find("button[name='fw_update']").click(() => {
662
663
  fwInfoNode.html(createBtn('check_circle', 'Firmware update started, check progress in logs.', true, 'icon-blue'));
663
664
  sendTo(namespace, 'startOta', {devId: devId}, (msg) => {
664
665
  fwInfoNode.html(createBtn('check_circle', 'Finished, see logs.', true));
@@ -668,13 +669,13 @@ function checkFwUpdate() {
668
669
  } else if (msg.status == 'not_available') {
669
670
  fwInfoNode.html(createBtn('check_circle', 'Up-to-date', true));
670
671
  } else if (msg.status == 'fail') {
671
- fwInfoNode.html(createBtn('check_circle', 'Firmware check failed, ' + msg.msg, true, 'icon-red'));
672
+ fwInfoNode.html(createBtn('check_circle', 'Firmware check failed, '+msg.msg, true, 'icon-red'));
672
673
  } else {
673
674
  fwInfoNode.html(createBtn('not_interested', 'No firmware update available', true));
674
675
  }
675
676
  }
676
677
  };
677
- for (let i = 0; i < deviceCards.length; i++) {
678
+ for (let i=0;i < deviceCards.length; i++) {
678
679
  const deviceCard = $(deviceCards[i]);
679
680
  const devId = getDevId(deviceCard.attr('id'));
680
681
  getFwInfoNode(deviceCard).html('<span class="left" style="padding-top:8px">checking...</span>');
@@ -745,7 +746,7 @@ function getDevices() {
745
746
  }
746
747
 
747
748
  function getDeviceCards() {
748
- return $('#devices .device').not('.group');
749
+ return $('#devices .device').not(".group");
749
750
  }
750
751
 
751
752
  function getDeviceCard(devId) {
@@ -809,35 +810,35 @@ function load(settings, onChange) {
809
810
  // Signal to admin, that no changes yet
810
811
  onChange(false);
811
812
 
812
- $('#state_cleanup_btn').click(function () {
813
+ $('#state_cleanup_btn').click(function() {
813
814
  cleanConfirmation();
814
815
  });
815
- $('#fw_check_btn').click(function () {
816
+ $('#fw_check_btn').click(function() {
816
817
  checkFwUpdate();
817
818
  });
818
- $('#touchlink_btn').click(function () {
819
+ $('#touchlink_btn').click(function() {
819
820
  touchlinkReset();
820
821
  showPairingProcess();
821
822
  });
822
- $('#pairing').click(function () {
823
+ $('#pairing').click(function() {
823
824
  if (!$('#pairing').hasClass('pulse'))
824
825
  letsPairing();
825
826
  showPairingProcess();
826
827
  });
827
828
 
828
- $('#refresh').click(function () {
829
+ $('#refresh').click(function() {
829
830
  getMap();
830
831
  });
831
832
 
832
- $('#reset-btn').click(function () {
833
+ $('#reset-btn').click(function() {
833
834
  resetConfirmation();
834
835
  });
835
836
 
836
- $('#viewconfig').click(function () {
837
+ $('#viewconfig').click(function() {
837
838
  showViewConfig();
838
839
  });
839
840
 
840
- $('#scan').click(function () {
841
+ $('#scan').click(function() {
841
842
  showChannels();
842
843
  });
843
844
 
@@ -846,18 +847,18 @@ function load(settings, onChange) {
846
847
  showGroups();
847
848
  });
848
849
 
849
- $('#add_group').click(function () {
850
+ $('#add_group').click(function() {
850
851
  // showGroupList(true);
851
- const maxind = parseInt(Object.getOwnPropertyNames(groups).reduce((a, b) => a > b ? a : b, 0));
852
- editGroupName(maxind + 1, 'Group ' + maxind + 1, true);
852
+ const maxind = parseInt(Object.getOwnPropertyNames(groups).reduce((a,b) => a>b ? a : b, 0));
853
+ editGroupName(maxind+1, 'Group ' + maxind+1, true);
853
854
  });
854
- $('#add_grp_btn').click(function () {
855
+ $('#add_grp_btn').click(function() {
855
856
  // showGroupList(true);
856
- const maxind = parseInt(Object.getOwnPropertyNames(groups).reduce((a, b) => a > b ? a : b, 0));
857
- editGroupName(maxind + 1, 'Group ' + maxind + 1, true);
857
+ const maxind = parseInt(Object.getOwnPropertyNames(groups).reduce((a,b) => a>b ? a : b, 0));
858
+ editGroupName(maxind+1, 'Group ' + maxind+1, true);
858
859
  });
859
860
 
860
- $(document).ready(function () {
861
+ $(document).ready(function() {
861
862
  $('.modal').modal({
862
863
  startingTop: '30%',
863
864
  endingTop: '10%',
@@ -881,7 +882,7 @@ function load(settings, onChange) {
881
882
  $('#pairing').attr('data-tooltip', transText);
882
883
  }
883
884
 
884
- $('ul.tabs').on('click', 'a', function (e) {
885
+ $('ul.tabs').on('click', 'a', function(e) {
885
886
  if ($(e.target).attr('id') == 'tabmap') {
886
887
  redrawMap();
887
888
  }
@@ -890,11 +891,11 @@ function load(settings, onChange) {
890
891
  }
891
892
  });
892
893
 
893
- $('#add_exclude').click(function () {
894
+ $('#add_exclude').click(function() {
894
895
  addExcludeDialog();
895
896
  });
896
897
 
897
- $('#add_binding').click(function () {
898
+ $('#add_binding').click(function() {
898
899
  addBindingDialog();
899
900
  });
900
901
 
@@ -933,7 +934,7 @@ function save(callback) {
933
934
  const $this = $(this);
934
935
  if (savedSettings.indexOf($this.attr('id')) === -1) return;
935
936
  if ($this.hasClass('validate') && $this.hasClass('invalid')) {
936
- showMessage('Invalid input for ' + $this.attr('id'), _('Error'));
937
+ showMessage('Invalid input for ' +$this.attr('id'), _('Error'));
937
938
  return;
938
939
  }
939
940
  if ($this.attr('type') === 'checkbox') {
@@ -947,7 +948,7 @@ function save(callback) {
947
948
 
948
949
 
949
950
  function getDevId(adapterDevId) {
950
- return adapterDevId.split('.').slice(0, 3).join('.');
951
+ return adapterDevId.split('.').slice(0,3).join('.');
951
952
  }
952
953
 
953
954
  // subscribe to changes
@@ -974,7 +975,7 @@ socket.on('stateChange', function (id, state) {
974
975
  } else {
975
976
  $('#pairing').addClass('pulse');
976
977
  $('#pairing').html(state.val);
977
- const percent = 100 - 100 * state.val / ($('#countDown').val() || 60);
978
+ const percent = 100-100*state.val/($('#countDown').val() || 60);
978
979
  $('#progress_line').css('width', `${percent}%`);
979
980
  }
980
981
  } else if (id.match(/\.info\.pairingMessage$/)) {
@@ -1020,7 +1021,6 @@ socket.on('objectChange', function (id, obj) {
1020
1021
  }
1021
1022
  }
1022
1023
  });
1023
-
1024
1024
  /*
1025
1025
  socket.emit('getObject', 'system.config', function (err, res) {
1026
1026
  if (!err && res && res.common) {
@@ -1033,47 +1033,45 @@ socket.emit('getObject', 'system.config', function (err, res) {
1033
1033
  function putEventToNode(devId) {
1034
1034
  if (network) {
1035
1035
  const nodesArray = Object.values(network.body.data.nodes._data);
1036
- const node = nodesArray.find((node) => {
1037
- return node.id == devId;
1038
- });
1036
+ const node = nodesArray.find((node) => { return node.id == devId; });
1039
1037
  if (node) {
1040
1038
  const exists = networkEvents.find((event) => {
1041
1039
  return event.node == node.id;
1042
1040
  });
1043
1041
  if (!exists) {
1044
1042
  networkEvents.push({node: node.id, radius: 0, forward: true});
1045
- // } else {
1046
- // exists.radius = 0;
1047
- // exists.forward = true;
1043
+ // } else {
1044
+ // exists.radius = 0;
1045
+ // exists.forward = true;
1048
1046
  }
1049
1047
  }
1050
1048
  }
1051
1049
  }
1052
1050
 
1053
- function showNetworkMap(devices, map) {
1051
+ function showNetworkMap(devices, map){
1054
1052
  // create an object with nodes
1055
1053
  const nodes = {};
1056
1054
  // create an array with edges
1057
1055
  const edges = [];
1058
1056
 
1059
1057
  if (map.lqis == undefined || map.lqis.length === 0) { // first init
1060
- $('#filterParent, #filterSibl, #filterPrvChild, #filterMesh').change(function () {
1058
+ $('#filterParent, #filterSibl, #filterPrvChild, #filterMesh').change(function() {
1061
1059
  updateMapFilter();
1062
1060
  });
1063
1061
  }
1064
1062
 
1065
- const createNode = function (dev, mapEntry) {
1063
+ const createNode = function(dev, mapEntry) {
1066
1064
  if (dev.common && dev.common.type == 'group') return undefined;
1067
1065
  const extInfo = (mapEntry && mapEntry.networkAddress) ? `\n (nwkAddr: 0x${mapEntry.networkAddress.toString(16)} | ${mapEntry.networkAddress})` : '';
1068
1066
  const node = {
1069
1067
  id: dev._id,
1070
- label: (dev.link_quality > 0 ? dev.common.name : `${dev.common.name}\n(disconnected)`),
1071
- title: dev._id.replace(namespace + '.', '') + extInfo,
1068
+ label: (dev.link_quality >0 ? dev.common.name:`${dev.common.name}\n(disconnected)`) ,
1069
+ title: dev._id.replace(namespace+'.', '') + extInfo,
1072
1070
  shape: 'circularImage',
1073
1071
  image: dev.icon,
1074
1072
  imagePadding: {top: 5, bottom: 5, left: 5, right: 5},
1075
1073
  color: {background: 'white', highlight: {background: 'white'}},
1076
- font: {color: '#007700'},
1074
+ font: {color:'#007700'},
1077
1075
  borderWidth: 1,
1078
1076
  borderWidthSelected: 4,
1079
1077
  };
@@ -1087,7 +1085,7 @@ function showNetworkMap(devices, map) {
1087
1085
  };
1088
1086
 
1089
1087
  if (map.lqis) {
1090
- map.lqis.forEach((mapEntry) => {
1088
+ map.lqis.forEach((mapEntry)=>{
1091
1089
  const dev = getDevice(mapEntry.ieeeAddr);
1092
1090
  if (!dev) {
1093
1091
  //console.log("No dev with ieee "+mapEntry.ieeeAddr);
@@ -1100,7 +1098,8 @@ function showNetworkMap(devices, map) {
1100
1098
  if (node) {
1101
1099
  nodes[mapEntry.ieeeAddr] = node;
1102
1100
  }
1103
- } else {
1101
+ }
1102
+ else {
1104
1103
  node = nodes[mapEntry.ieeeAddr];
1105
1104
  }
1106
1105
  if (node) {
@@ -1118,7 +1117,7 @@ function showNetworkMap(devices, map) {
1118
1117
 
1119
1118
  if (mapEntry.relationship === 0 || mapEntry.relationship === 1) { // 0 - parent, 1 - child
1120
1119
  // // parent/child
1121
- if (mapEntry.status !== 'online') {
1120
+ if (mapEntry.status !== 'online' ) {
1122
1121
  label = label + ' (off)';
1123
1122
  linkColor = '#ff0000';
1124
1123
  }
@@ -1135,8 +1134,8 @@ function showNetworkMap(devices, map) {
1135
1134
  if (reverse) {
1136
1135
  // update reverse edge
1137
1136
  edge = reverse;
1138
- edge.label += '\n' + label;
1139
- edge.arrows.from = {enabled: false, scaleFactor: 0.5}; // start hidden if node is not selected
1137
+ edge.label += '\n'+label;
1138
+ edge.arrows.from = { enabled: false, scaleFactor: 0.5 }; // start hidden if node is not selected
1140
1139
  if (mapEntry.relationship == 1) { //
1141
1140
  edge.color.color = linkColor;
1142
1141
  edge.color.highlight = linkColor;
@@ -1151,7 +1150,7 @@ function showNetworkMap(devices, map) {
1151
1150
  size: 0, // start hidden
1152
1151
  color: linkColor
1153
1152
  },
1154
- arrows: {to: {enabled: false, scaleFactor: 0.5}},
1153
+ arrows: { to: { enabled: false, scaleFactor: 0.5 }},
1155
1154
  //arrowStrikethrough: false,
1156
1155
  color: {
1157
1156
  color: linkColor,
@@ -1165,7 +1164,7 @@ function showNetworkMap(devices, map) {
1165
1164
  values.fromArrow = values.fromArrowScale != 1 ? true : false; // simplified, arrow existing if scale is not default value
1166
1165
  },
1167
1166
  label: () => {
1168
- // see onMapSelect workaround
1167
+ // see onMapSelect workaround
1169
1168
  // values.size = 10;
1170
1169
  }
1171
1170
  },
@@ -1231,16 +1230,15 @@ function showNetworkMap(devices, map) {
1231
1230
  const nodesArray = Object.values(nodes);
1232
1231
  // add devices without network links to map
1233
1232
  devices.forEach((dev) => {
1234
- const node = nodesArray.find((node) => {
1235
- return node.id == dev._id;
1236
- });
1233
+ const node = nodesArray.find((node) => { return node.id == dev._id; });
1237
1234
  if (!node) {
1238
1235
  const node = createNode(dev);
1239
1236
 
1240
- if (node) {
1241
- node.font = {color: '#ff0000'};
1237
+ if (node)
1238
+ {
1239
+ node.font = {color:'#ff0000'};
1242
1240
  if (dev.info && dev.info.device._type == 'Coordinator') {
1243
- node.font = {color: '#000000'};
1241
+ node.font = {color:'#000000'};
1244
1242
  }
1245
1243
  nodesArray.push(node);
1246
1244
  }
@@ -1262,7 +1260,7 @@ function showNetworkMap(devices, map) {
1262
1260
  shape: 'box'
1263
1261
  },
1264
1262
  layout: {
1265
- improvedLayout: true,
1263
+ improvedLayout:true,
1266
1264
  }
1267
1265
  };
1268
1266
 
@@ -1312,26 +1310,25 @@ function showNetworkMap(devices, map) {
1312
1310
  if (networkEvents.length > 0) {
1313
1311
  network.redraw();
1314
1312
  const toDelete = [];
1315
- networkEvents.forEach((event, index) => {
1313
+ networkEvents.forEach((event, index)=>{
1316
1314
  if (event.radius >= 1) {
1317
1315
  toDelete.push(index);
1318
1316
  } else {
1319
1317
  event.radius += 0.08;
1320
1318
  }
1321
1319
  });
1322
- toDelete.forEach((index) => {
1320
+ toDelete.forEach((index)=>{
1323
1321
  networkEvents.splice(index, 1);
1324
1322
  });
1325
1323
  }
1326
1324
  }
1327
-
1328
- network.on('beforeDrawing', function (ctx) {
1325
+ network.on('beforeDrawing', function(ctx) {
1329
1326
  if (networkEvents.length > 0) {
1330
- networkEvents.forEach((event) => {
1327
+ networkEvents.forEach((event)=>{
1331
1328
  const inode = event.node;
1332
1329
  const nodePosition = network.getPositions();
1333
1330
  event.radius = (event.radius > 1) ? 1 : event.radius;
1334
- const cap = Math.cos(event.radius * Math.PI / 2);
1331
+ const cap = Math.cos(event.radius*Math.PI/2);
1335
1332
  const colorCircle = `rgba(0, 255, 255, ${cap.toFixed(2)})`;
1336
1333
  const colorBorder = `rgba(0, 255, 255, ${cap.toFixed(2)})`;
1337
1334
  ctx.strokeStyle = colorCircle;
@@ -1348,11 +1345,11 @@ function showNetworkMap(devices, map) {
1348
1345
  function redrawMap() {
1349
1346
  if (network != undefined && devices.length > 0) {
1350
1347
  const width = ($('.adapter-body').width() || $('#main').width()) - 20,
1351
- height = ($('.adapter-body').height() || ($('#main').height())) - 120;
1348
+ height = ($('.adapter-body').height() || ($('#main').height())) -120;
1352
1349
  network.setSize(width, height);
1353
1350
  network.redraw();
1354
1351
  network.fit();
1355
- network.moveTo({offset: {x: 0.5 * width, y: 0.5 * height}});
1352
+ network.moveTo({offset:{x:0.5 * width, y:0.5 * height}});
1356
1353
  }
1357
1354
  }
1358
1355
 
@@ -1366,9 +1363,9 @@ function updateMapFilter() {
1366
1363
  const invisColor = $('#filterMesh').is(':checked') ? 0.2 : 0;
1367
1364
  mapEdges.forEach((edge) => {
1368
1365
  if (((edge.relationship === 0 || edge.relationship === 1) && showParent)
1369
- || (edge.relationship === 2 && showSibl)
1370
- || (edge.relationship === 3 && showParent) // ignore relationship "unknown"
1371
- || (edge.relationship === 4 && showPrvChild)) {
1366
+ || (edge.relationship === 2 && showSibl)
1367
+ || (edge.relationship === 3 && showParent) // ignore relationship "unknown"
1368
+ || (edge.relationship === 4 && showPrvChild)) {
1372
1369
  edge.color.opacity = 1.0;
1373
1370
  } else {
1374
1371
  edge.color.opacity = invisColor;
@@ -1392,15 +1389,13 @@ function getComPorts(onChange) {
1392
1389
  // }, 1000);
1393
1390
  // return;
1394
1391
  // }
1395
- if (!list) {
1396
- return;
1397
- }
1392
+ if (!list) return;
1398
1393
  const element = $('#ports');
1399
1394
  for (let j = 0; j < list.length; j++) {
1400
- element.append('<li><a href="#!" data-value="' + list[j].comName + '">' + list[j].comName + (list[j].label ? (' [' + list[j].label + ']') : '') + '</a></li>');
1395
+ element.append('<li><a href="#!">' + list[j].comName +'</a></li>');
1401
1396
  }
1402
- $('#ports a').click(function () {
1403
- $('#port').val($(this).data('value'));
1397
+ $('#ports a').click(function() {
1398
+ $('#port').val($(this).text());
1404
1399
  Materialize.updateTextFields();
1405
1400
  onChange();
1406
1401
  });
@@ -1410,33 +1405,29 @@ function getComPorts(onChange) {
1410
1405
  function loadDeveloperTab() {
1411
1406
  // fill device selector
1412
1407
  updateSelect('#dev', devices,
1413
- function (key, device) {
1408
+ function(key, device) {
1414
1409
  if (device.hasOwnProperty('info')) {
1415
- if (device.info.device._type === 'Coordinator') {
1410
+ if (device.info.device._type == 'Coordinator') {
1416
1411
  return null;
1417
1412
  }
1418
1413
  return `${device.common.name} (${device.info.name})`;
1419
1414
  } else { // fallback if device in list but not paired
1420
- return device.common.name + ' ' + device.native.id;
1415
+ device.common.name + ' ' +device.native.id;
1421
1416
  }
1422
1417
  },
1423
- function (key, device) {
1418
+ function(key, device) {
1424
1419
  return device._id;
1425
1420
  });
1426
1421
  // add groups to device selector
1427
1422
  const groupList = [];
1428
1423
  for (const key in groups) {
1429
- groupList.push({
1430
- _id: namespace + '.' + key.toString(16).padStart(16, '0'),
1431
- groupId: key,
1432
- groupName: groups[key]
1433
- });
1424
+ groupList.push({'_id': namespace+'.'+key.toString(16).padStart(16, '0'), 'groupId': key, 'groupName': groups[key]});
1434
1425
  }
1435
1426
  updateSelect('#dev', groupList,
1436
- function (key, device) {
1437
- return 'Group ' + device.groupId + ': ' + device.groupName;
1427
+ function(key, device) {
1428
+ return 'Group '+device.groupId+': '+device.groupName;
1438
1429
  },
1439
- function (key, device) {
1430
+ function(key, device) {
1440
1431
  return device._id;
1441
1432
  }, true);
1442
1433
 
@@ -1446,10 +1437,10 @@ function loadDeveloperTab() {
1446
1437
  populateSelector('#type', 'typeList', this.value);
1447
1438
 
1448
1439
  if (responseCodes == false) {
1449
- const getValue = function () { // convert to number if needed
1440
+ const getValue = function() { // convert to number if needed
1450
1441
  let attrData = $('#value-input').val();
1451
1442
  if (attrData.startsWith('"') && attrData.endsWith('"')) {
1452
- attrData = attrData.substr(1, attrData.length - 2);
1443
+ attrData = attrData.substr(1, attrData.length -2);
1453
1444
  } else {
1454
1445
  const numValue = Number(attrData);
1455
1446
  attrData = !isNaN(numValue) ? numValue : attrData;
@@ -1474,38 +1465,35 @@ function loadDeveloperTab() {
1474
1465
  return data;
1475
1466
  };
1476
1467
 
1477
- const prepareExpertData = function () {
1468
+ const prepareExpertData = function() {
1478
1469
  try {
1479
1470
  return JSON.parse($('#expert-json').val());
1480
1471
  } catch (exception) {
1481
1472
  showDevRunInfo('JSON error', exception, 'yellow');
1482
1473
  }
1483
1474
  };
1484
- const setExpertData = function (prop, value, removeIfEmpty = true) {
1475
+ const setExpertData = function(prop, value, removeIfEmpty = true) {
1485
1476
  if (!$('#expert-mode').is(':checked')) {
1486
1477
  return;
1487
1478
  }
1488
- if (!removeIfEmpty && value == null) {
1489
- value = '';
1490
- }
1479
+ if (!removeIfEmpty && value == null) { value = ''; }
1491
1480
  let data;
1492
1481
  if (prop) {
1493
1482
  data = prepareExpertData();
1494
1483
  // https://stackoverflow.com/a/6394168/6937282
1495
- const assignVal = function index(obj, is, value) {
1484
+ const assignVal = function index(obj,is, value) {
1496
1485
  if (typeof is == 'string') {
1497
- return index(obj, is.split('.'), value);
1498
- } else if (is.length === 1 && value !== undefined) {
1486
+ return index(obj,is.split('.'), value);
1487
+ } else if (is.length==1 && value!==undefined) {
1499
1488
  if (value == null) {
1500
1489
  return delete obj[is[0]];
1501
1490
  } else {
1502
1491
  return obj[is[0]] = value;
1503
1492
  }
1504
- } else if (!is.length) {
1493
+ } else if (is.length==0) {
1505
1494
  return obj;
1506
- } else {
1507
- return index(obj[is[0]], is.slice(1), value);
1508
- }
1495
+ } else
1496
+ return index(obj[is[0]],is.slice(1), value);
1509
1497
  };
1510
1498
  assignVal(data, prop, value);
1511
1499
  } else {
@@ -1515,7 +1503,7 @@ function loadDeveloperTab() {
1515
1503
  };
1516
1504
 
1517
1505
  // init event listener only at first load
1518
- $('#dev-selector').change(function () {
1506
+ $('#dev-selector').change(function() {
1519
1507
  if (this.selectedIndex <= 0) {
1520
1508
  return;
1521
1509
  }
@@ -1526,48 +1514,48 @@ function loadDeveloperTab() {
1526
1514
 
1527
1515
  const epList = device ? device.info.device._endpoints : null;
1528
1516
  updateSelect('#ep', epList,
1529
- function (key, ep) {
1517
+ function(key, ep) {
1530
1518
  return ep.ID;
1531
1519
  },
1532
- function (key, ep) {
1520
+ function(key, ep) {
1533
1521
  return ep.ID;
1534
1522
  });
1535
1523
  setExpertData('devId', this.value);
1536
1524
  setExpertData('ep', $('#ep-selector').val(), false);
1537
1525
  });
1538
1526
 
1539
- $('#ep-selector').change(function () {
1527
+ $('#ep-selector').change(function() {
1540
1528
  setExpertData('ep', this.value);
1541
1529
  });
1542
1530
 
1543
- $('#cid-selector').change(function () {
1531
+ $('#cid-selector').change(function() {
1544
1532
  populateSelector('#attrid', 'attrIdList', this.value);
1545
- if ($('#cmd-type-selector').val() === 'functional') {
1533
+ if ($('#cmd-type-selector').val() == 'functional') {
1546
1534
  const cid = $('#cid-selector option:selected').val();
1547
1535
  populateSelector('#cmd', 'cmdListFunctional', cid);
1548
1536
  }
1549
1537
  setExpertData('cid', this.value);
1550
1538
  });
1551
1539
 
1552
- $('#cmd-type-selector').change(function () {
1553
- if (this.value === 'foundation') {
1540
+ $('#cmd-type-selector').change(function() {
1541
+ if (this.value == 'foundation') {
1554
1542
  populateSelector('#cmd', 'cmdListFoundation');
1555
- } else if (this.value === 'functional') {
1543
+ } else if (this.value == 'functional') {
1556
1544
  const cid = $('#cid-selector option:selected').val();
1557
1545
  populateSelector('#cmd', 'cmdListFunctional', cid);
1558
1546
  }
1559
1547
  setExpertData('cmdType', this.value);
1560
1548
  });
1561
1549
 
1562
- $('#cmd-selector').change(function () {
1550
+ $('#cmd-selector').change(function() {
1563
1551
  setExpertData('cmd', this.value);
1564
1552
  });
1565
- $('#attrid-selector').change(function () {
1566
- setExpertData('zclData', {[this.value]: {}});
1553
+ $('#attrid-selector').change(function() {
1554
+ setExpertData('zclData', {[this.value]:{}});
1567
1555
  });
1568
1556
 
1569
1557
  // value selector checkbox
1570
- $('#value-needed').change(function () {
1558
+ $('#value-needed').change(function() {
1571
1559
  const attr = $('#attrid-selector').val();
1572
1560
  let attrData = null;
1573
1561
  if (this.checked === true) {
@@ -1576,17 +1564,17 @@ function loadDeveloperTab() {
1576
1564
  } else {
1577
1565
  $('#value-input').attr('disabled', 'disabled');
1578
1566
  }
1579
- setExpertData('zclData.' + attr, attrData);
1567
+ setExpertData('zclData.'+attr, attrData);
1580
1568
  $('#type-selector').select();
1581
1569
  Materialize.updateTextFields();
1582
1570
  });
1583
1571
 
1584
- $('#value-input').keyup(function () {
1572
+ $('#value-input').keyup(function() {
1585
1573
  const attr = $('#attrid-selector').val();
1586
- setExpertData('zclData.' + attr, getValue());
1574
+ setExpertData('zclData.'+attr, getValue());
1587
1575
  });
1588
1576
 
1589
- $('#expert-mode').change(function () {
1577
+ $('#expert-mode').change(function() {
1590
1578
  if (this.checked === true) {
1591
1579
  setExpertData();
1592
1580
  $('#expert-json-box').css('display', 'inline-block');
@@ -1597,7 +1585,7 @@ function loadDeveloperTab() {
1597
1585
  Materialize.updateTextFields();
1598
1586
  });
1599
1587
 
1600
- $('#dev-send-btn').click(function () {
1588
+ $('#dev-send-btn').click(function() {
1601
1589
  let data;
1602
1590
  if ($('#expert-mode').is(':checked')) {
1603
1591
  data = prepareExpertData();
@@ -1605,7 +1593,7 @@ function loadDeveloperTab() {
1605
1593
  data = prepareData();
1606
1594
  }
1607
1595
  sendToZigbee(data.devId, data.ep, data.cid, data.cmd, data.cmdType, data.zclData, data.cfg, function (reply) {
1608
- console.log('Reply from zigbee: ' + JSON.stringify(reply));
1596
+ console.log('Reply from zigbee: '+ JSON.stringify(reply));
1609
1597
  if (reply.hasOwnProperty('localErr')) {
1610
1598
  showDevRunInfo(reply.localErr, reply.errMsg, 'yellow');
1611
1599
  } else if (reply.hasOwnProperty('localStatus')) {
@@ -1646,34 +1634,25 @@ function loadDeveloperTab() {
1646
1634
  */
1647
1635
  function sendToZigbee(id, ep, cid, cmd, cmdType, zclData, cfg, callback) {
1648
1636
  if (!id) {
1649
- if (callback) {
1650
- callback({localErr: 'Incomplete', errMsg: 'Please select Device and Endpoint!'});
1651
- }
1637
+ if (callback) {callback({localErr: 'Incomplete', errMsg: 'Please select Device and Endpoint!'});}
1652
1638
  return;
1653
1639
  }
1654
1640
  if (!cid || !cmd || !cmdType) {
1655
- if (callback) {
1656
- callback({
1657
- localErr: 'Incomplete',
1658
- errMsg: 'Please choose ClusterId, Command, CommandType and AttributeId!'
1659
- });
1660
- }
1641
+ if (callback) {callback({localErr: 'Incomplete', errMsg: 'Please choose ClusterId, Command, CommandType and AttributeId!'});}
1661
1642
  return;
1662
1643
  }
1663
1644
  const data = {id: id, ep: ep, cid: cid, cmd: cmd, cmdType: cmdType, zclData: zclData, cfg: cfg};
1664
- if (callback) {
1665
- callback({localStatus: 'Send', errMsg: 'Waiting for reply...'});
1666
- }
1645
+ if (callback) {callback({localStatus: 'Send', errMsg: 'Waiting for reply...'});}
1667
1646
 
1668
- const sendTimeout = setTimeout(function () {
1647
+ const sendTimeout = setTimeout(function() {
1669
1648
  if (callback) {
1670
1649
  callback({localErr: 'Timeout', errMsg: 'We did not receive any response.'});
1671
1650
  }
1672
1651
  }, 15000);
1673
1652
 
1674
- console.log('Send to zigbee, id ' + id + ',ep ' + ep + ', cid ' + cid + ', cmd ' + cmd + ', cmdType ' + cmdType + ', zclData ' + JSON.stringify(zclData));
1653
+ console.log('Send to zigbee, id '+id+ ',ep '+ep+', cid '+cid+', cmd '+cmd+', cmdType '+cmdType+', zclData '+JSON.stringify(zclData));
1675
1654
 
1676
- sendTo(namespace, 'sendToZigbee', data, function (reply) {
1655
+ sendTo(namespace, 'sendToZigbee', data, function(reply) {
1677
1656
  clearTimeout(sendTimeout);
1678
1657
  if (callback) {
1679
1658
  callback(reply);
@@ -1686,10 +1665,11 @@ function sendToZigbee(id, ep, cid, cmd, cmdType, zclData, cfg, callback) {
1686
1665
  */
1687
1666
  function showDevRunInfo(result, text, level) {
1688
1667
  const card = $('#devActResult');
1689
- if (level === 'yellow') {
1690
- card.removeClass('white-text').addClass('yellow-text');
1691
- } else {
1692
- card.removeClass('yellow-text').addClass('white-text');
1668
+ if (level == 'yellow') {
1669
+ card.removeClass( 'white-text' ).addClass( 'yellow-text' );
1670
+ }
1671
+ else {
1672
+ card.removeClass( 'yellow-text' ).addClass( 'white-text' );
1693
1673
  }
1694
1674
  $('#devActResult').text(result);
1695
1675
  $('#devInfoMsg').text(text);
@@ -1697,13 +1677,13 @@ function showDevRunInfo(result, text, level) {
1697
1677
 
1698
1678
  function addDevLog(reply) {
1699
1679
  const statusCode = reply.statusCode;
1700
- let logHtml = '<span>' + JSON.stringify(reply.msg) + '</span><br>';
1680
+ let logHtml = '<span>'+JSON.stringify(reply.msg)+'</span><br>';
1701
1681
  if (responseCodes != undefined) {
1702
1682
  const status = Object.keys(responseCodes).find(key => responseCodes[key] === statusCode);
1703
1683
  if (statusCode == 0) {
1704
- logHtml = '<span class="green-text">' + status + '</span> ' + logHtml;
1684
+ logHtml = '<span class="green-text">'+status+'</span> '+logHtml;
1705
1685
  } else {
1706
- logHtml = '<span class="yellow-text">' + status + '</span> ' + logHtml;
1686
+ logHtml = '<span class="yellow-text">'+status+'</span> '+logHtml;
1707
1687
  }
1708
1688
  }
1709
1689
  const logView = $('#dev_result_log');
@@ -1715,7 +1695,7 @@ function addDevLog(reply) {
1715
1695
  * Query adapter and update select with result
1716
1696
  */
1717
1697
  function populateSelector(selectId, key, cid) {
1718
- $(selectId + '>option:enabled').remove(); // remove existing elements
1698
+ $(selectId+'>option:enabled').remove(); // remove existing elements
1719
1699
  $(selectId).select();
1720
1700
  if (cid == '-2') {
1721
1701
  updateSelect(selectId, null);
@@ -1726,7 +1706,7 @@ function populateSelector(selectId, key, cid) {
1726
1706
  if (key === 'attrIdList') {
1727
1707
  updateSelect(selectId, list,
1728
1708
  (attrName, attr) => {
1729
- return attrName + ' (' + attr.ID + ', type ' + attr.type + ')';
1709
+ return attrName + ' ('+attr.ID +', type '+attr.type+')';
1730
1710
  },
1731
1711
  (attrName) => {
1732
1712
  return attrName;
@@ -1734,7 +1714,7 @@ function populateSelector(selectId, key, cid) {
1734
1714
  } else if (key === 'typeList') {
1735
1715
  updateSelect(selectId, list,
1736
1716
  (name, val) => {
1737
- return name + ' (' + val + ')';
1717
+ return name +' ('+val+')';
1738
1718
  },
1739
1719
  (name, val) => {
1740
1720
  return val;
@@ -1742,7 +1722,7 @@ function populateSelector(selectId, key, cid) {
1742
1722
  } else {
1743
1723
  updateSelect(selectId, list,
1744
1724
  (propName, propInfo) => {
1745
- return propName + ' (' + propInfo.ID + ')';
1725
+ return propName +' ('+propInfo.ID+')';
1746
1726
  },
1747
1727
  (propName) => {
1748
1728
  return propName;
@@ -1752,31 +1732,32 @@ function populateSelector(selectId, key, cid) {
1752
1732
  }
1753
1733
 
1754
1734
  function updateSelect(id, list, getText, getId, append = false) {
1755
- const selectId = id + '-selector';
1735
+ const selectId = id+'-selector';
1756
1736
  const mySelect = $(selectId);
1757
1737
  if (!append) {
1758
- $(selectId + '>:not(:first[disabled])').remove(); // remove existing elements, except first if disabled, (is 'Select...' info)
1738
+ $(selectId+'>:not(:first[disabled])').remove(); // remove existing elements, except first if disabled, (is 'Select...' info)
1759
1739
  mySelect.select();
1760
1740
  }
1761
1741
  if (list == null && !append) {
1762
1742
  const infoOption = new Option('Nothing available');
1763
1743
  infoOption.disabled = true;
1764
- mySelect.append(infoOption);
1765
- } else {
1744
+ mySelect.append( infoOption);
1745
+ }
1746
+ else {
1766
1747
  const keys = Object.keys(list); // is index in case of array
1767
- for (let i = 0; i < keys.length; i++) {
1748
+ for (let i=0; i<keys.length; i++) {
1768
1749
  const key = keys[i];
1769
1750
  const item = list[key];
1770
1751
  const optionText = getText(key, item);
1771
1752
  if (optionText == null) {
1772
1753
  continue;
1773
1754
  }
1774
- mySelect.append(new Option(optionText, getId(key, item)));
1755
+ mySelect.append( new Option(optionText, getId(key, item)));
1775
1756
  }
1776
1757
  }
1777
1758
 
1778
- if ($(id + '-c-input').length > 0) {
1779
- mySelect.append(new Option('CUSTOM', -2));
1759
+ if ($(id+'-c-input').length > 0) {
1760
+ mySelect.append( new Option('CUSTOM', -2));
1780
1761
  }
1781
1762
  // update select element (Materialize)
1782
1763
  mySelect.select();
@@ -1805,17 +1786,17 @@ function showGroups() {
1805
1786
  const element = $('#groups_table');
1806
1787
  for (const j in groups) {
1807
1788
  if (groups.hasOwnProperty(j)) {
1808
- element.append(`<tr id="group_${j}" class="group"><td>${j}</td><td><div>${groups[j]}<span class="right">` +
1809
- `<a id="${j}" name="groupedit" class="waves-effect green btn-floating"><i class="material-icons">edit</i></a>` +
1810
- `<a id="${j}" name="groupdelete" class="waves-effect red btn-floating"><i class="material-icons">delete</i></a></span></div></td></tr>`);
1789
+ element.append(`<tr id="group_${j}" class="group"><td>${j}</td><td><div>${groups[j]}<span class="right">`+
1790
+ `<a id="${j}" name="groupedit" class="waves-effect green btn-floating"><i class="material-icons">edit</i></a>`+
1791
+ `<a id="${j}" name="groupdelete" class="waves-effect red btn-floating"><i class="material-icons">delete</i></a></span></div></td></tr>`);
1811
1792
  }
1812
1793
  }
1813
- $('a.btn-floating[name=\'groupedit\']').click(function () {
1794
+ $("a.btn-floating[name='groupedit']").click(function() {
1814
1795
  const index = $(this).attr('id'),
1815
1796
  name = groups[index];
1816
1797
  editGroupName(index, name, false);
1817
1798
  });
1818
- $('a.btn-floating[name=\'groupdelete\']').click(function () {
1799
+ $("a.btn-floating[name='groupdelete']").click(function() {
1819
1800
  const index = $(this).attr('id'),
1820
1801
  name = groups[index];
1821
1802
  deleteGroupConfirmation(index, name);
@@ -1827,51 +1808,53 @@ function editGroupName(id, name, isnew) {
1827
1808
  //console.log('devices: '+ JSON.stringify(devices));
1828
1809
  const groupables = [];
1829
1810
  for (const d of devices) {
1830
- if (d && d.info && d.info.endpoints) {
1831
- for (const ep of d.info.endpoints) {
1832
- if (ep.inputClusters.includes(4)) {
1833
- groupables.push(ep);
1834
- }
1835
- }
1836
- }
1837
- //console.log('device ' + JSON.stringify(d));
1811
+ if (d && d.info && d.info.endpoints) {
1812
+ for (const ep of d.info.endpoints) {
1813
+ if (ep.inputClusters.includes(4))
1814
+ {
1815
+ groupables.push(ep);
1816
+ }
1817
+ }
1818
+ }
1819
+ //console.log('device ' + JSON.stringify(d));
1838
1820
  }
1839
1821
 
1840
1822
  //var text = 'Enter new name for "'+name+'" ('+id+')?';
1841
1823
  if (isnew) {
1842
- $('#groupedit').find('.editgroup').addClass('hide');
1843
- $('#groupedit').find('.addgroup').removeClass('hide');
1844
- $('#groupedit').find('.input-field.members').addClass('hide');
1845
- $('#groupedit').find('.input-field.groupid').removeClass('hide');
1846
- } else {
1847
- $('#groupedit').find('.editgroup').removeClass('hide');
1848
- $('#groupedit').find('.addgroup').addClass('hide');
1849
- $('#groupedit').find('.input-field.members').removeClass('hide');
1850
- $('#groupedit').find('.input-field.groupid').addClass('hide');
1824
+ $('#groupedit').find('.editgroup').addClass('hide');
1825
+ $('#groupedit').find('.addgroup').removeClass('hide');
1826
+ $('#groupedit').find('.input-field.members').addClass('hide');
1827
+ $('#groupedit').find('.input-field.groupid').removeClass('hide');
1851
1828
  }
1852
- $('#groupedit').find('input[id=\'g_index\']').val(id);
1853
- $('#groupedit').find('input[id=\'g_name\']').val(name);
1854
- $('#groupedit a.btn[name=\'save\']').unbind('click');
1855
- $('#groupedit a.btn[name=\'save\']').click(() => {
1856
- const newId = $('#groupedit').find('input[id=\'g_index\']').val(),
1857
- newName = $('#groupedit').find('input[id=\'g_name\']').val();
1829
+ else {
1830
+ $('#groupedit').find('.editgroup').removeClass('hide');
1831
+ $('#groupedit').find('.addgroup').addClass('hide');
1832
+ $('#groupedit').find('.input-field.members').removeClass('hide');
1833
+ $('#groupedit').find('.input-field.groupid').addClass('hide');
1834
+ }
1835
+ $('#groupedit').find("input[id='g_index']").val(id);
1836
+ $('#groupedit').find("input[id='g_name']").val(name);
1837
+ $("#groupedit a.btn[name='save']").unbind('click');
1838
+ $("#groupedit a.btn[name='save']").click(() => {
1839
+ const newId = $('#groupedit').find("input[id='g_index']").val(),
1840
+ newName = $('#groupedit').find("input[id='g_name']").val();
1858
1841
  updateGroup(id, newId, newName);
1859
- // showGroups();
1860
- // getDevices();
1842
+ // showGroups();
1843
+ // getDevices();
1861
1844
  });
1862
1845
  $('#groupedit').modal('open');
1863
1846
  Materialize.updateTextFields();
1864
1847
  }
1865
1848
 
1866
1849
  function deleteGroupConfirmation(id, name) {
1867
- const text = translateWord('Do you really whant to delete group') + ' "' + name + '" (' + id + ')?';
1850
+ const text = translateWord('Do you really whant to delete group') + ' "'+name+'" ('+id+')?';
1868
1851
  $('#modaldelete').find('p').text(text);
1869
1852
  $('#forcediv').addClass('hide');
1870
- $('#modaldelete a.btn[name=\'yes\']').unbind('click');
1871
- $('#modaldelete a.btn[name=\'yes\']').click(() => {
1853
+ $("#modaldelete a.btn[name='yes']").unbind('click');
1854
+ $("#modaldelete a.btn[name='yes']").click(() => {
1872
1855
  deleteGroup(id);
1873
- // showGroups();
1874
- // getDevices();
1856
+ // showGroups();
1857
+ // getDevices();
1875
1858
  });
1876
1859
  $('#modaldelete').modal('open');
1877
1860
  }
@@ -1879,8 +1862,8 @@ function deleteGroupConfirmation(id, name) {
1879
1862
  function updateGroup(id, newId, newName) {
1880
1863
  delete groups[id];
1881
1864
  groups[newId] = newName;
1882
- sendTo(namespace, 'renameGroup', {id: newId, name: newName}, function (msg) {
1883
- if (msg && msg.error) {
1865
+ sendTo(namespace, 'renameGroup', { id: newId, name: newName}, function(msg) {
1866
+ if (msg && ms.error) {
1884
1867
  showMessage(msg.error, _('Error'));
1885
1868
  }
1886
1869
  getDevices();
@@ -1889,8 +1872,8 @@ function updateGroup(id, newId, newName) {
1889
1872
 
1890
1873
  function deleteGroup(id) {
1891
1874
  delete groups[id];
1892
- sendTo(namespace, 'deleteGroup', id, function (msg) {
1893
- if (msg && msg.error) {
1875
+ sendTo(namespace, 'deleteGroup', id , function(msg) {
1876
+ if (msg && ms.error) {
1894
1877
  showMessage(msg.error, _('Error'));
1895
1878
  }
1896
1879
  getDevices();
@@ -1898,55 +1881,58 @@ function deleteGroup(id) {
1898
1881
  }
1899
1882
 
1900
1883
  function updateDev(id, newName, newGroups) {
1901
- const dev = devices.find((d) => d._id === id);
1902
- if (dev && dev.common.name !== newName) {
1884
+ const dev = devices.find((d) => d._id == id);
1885
+ if (dev && dev.common.name != newName) {
1903
1886
  renameDevice(id, newName);
1904
1887
  }
1905
- const keys = Object.keys(newGroups);
1906
- if (keys && keys.length) {
1907
- sendTo(namespace, 'updateGroupMembership', {id: id, groups: newGroups}, function (msg) {
1908
- closeWaitingDialog();
1909
- if (msg && msg.error) {
1910
- showMessage(msg.error, _('Error'));
1911
- } else {
1912
- // save dev-groups on success
1913
- dev.groups = newGroups;
1914
- }
1915
- showDevices();
1916
- });
1917
- showWaitingDialog('Updating group memberships', 10);
1888
+ const keys = Object.keys(newGroups)
1889
+ if (keys && keys.length)
1890
+ {
1891
+ sendTo(namespace, 'updateGroupMembership', { id: id, groups: newGroups }, function (msg) {
1892
+ closeWaitingDialog();
1893
+ if (msg && msg.error) {
1894
+ showMessage(msg.error, _('Error'));
1895
+ }
1896
+ else {
1897
+ // save dev-groups on success
1898
+ dev.groups = newGroups;
1899
+ }
1900
+ showDevices();
1901
+ });
1902
+ showWaitingDialog('Updating group memberships', 10);
1918
1903
 
1919
1904
  }
1920
- /*
1921
- if (dev.info.device._type == 'Router') {
1922
- const oldGroups = devGroups[id] || [];
1923
- if (oldGroups.toString() != newGroups.toString()) {
1924
- devGroups[id] = newGroups;
1925
- sendTo(namespace, 'updateGroupMembership', { id: id, groups: newGroups }, function (msg) {
1926
- if (msg && msg.error) {
1927
- showMessage(msg.error, _('Error'));
1928
- }
1929
- else {
1930
- // save dev-groups on success
1931
- dev.groups = newGroups;
1932
- }
1933
- showDevices();
1934
- });
1935
- }
1905
+ /*
1906
+ if (dev.info.device._type == 'Router') {
1907
+ const oldGroups = devGroups[id] || [];
1908
+ if (oldGroups.toString() != newGroups.toString()) {
1909
+ devGroups[id] = newGroups;
1910
+ sendTo(namespace, 'updateGroupMembership', { id: id, groups: newGroups }, function (msg) {
1911
+ if (msg && msg.error) {
1912
+ showMessage(msg.error, _('Error'));
1913
+ }
1914
+ else {
1915
+ // save dev-groups on success
1916
+ dev.groups = newGroups;
1917
+ }
1918
+ showDevices();
1919
+ });
1936
1920
  }
1937
- */
1921
+ }
1922
+ */
1938
1923
  }
1939
1924
 
1940
1925
  function resetConfirmation() {
1941
1926
  $('#modalreset').modal('open');
1942
1927
  const btn = $('#modalreset .modal-content a.btn');
1943
1928
  btn.unbind('click');
1944
- btn.click(function (e) {
1929
+ btn.click(function(e) {
1945
1930
  sendTo(namespace, 'reset', {mode: e.target.id}, function (err) {
1946
1931
  if (err) {
1947
1932
  console.log(err);
1948
- } else {
1949
- console.log('Reset done');
1933
+ }
1934
+ else {
1935
+ console.log('Reseted');
1950
1936
  }
1951
1937
  });
1952
1938
  });
@@ -1956,7 +1942,7 @@ function showViewConfig() {
1956
1942
  $('#modalviewconfig').modal('open');
1957
1943
  }
1958
1944
 
1959
- function prepareBindingDialog(bindObj) {
1945
+ function prepareBindingDialog(bindObj){
1960
1946
  const binddevices = devices.slice();
1961
1947
  binddevices.unshift('');
1962
1948
  const bind_source = (bindObj) ? [bindObj.bind_source] : [''];
@@ -1967,12 +1953,12 @@ function prepareBindingDialog(bindObj) {
1967
1953
  const allowClustersName = {5: 'genScenes', 6: 'genOnOff', 8: 'genLevelCtrl', 768: 'lightingColorCtrl'};
1968
1954
  // fill device selector
1969
1955
  list2select('#bind_source', binddevices, bind_source,
1970
- function (key, device) {
1956
+ function(key, device) {
1971
1957
  if (device == '') {
1972
1958
  return 'Select source device';
1973
1959
  }
1974
1960
  if (device.hasOwnProperty('info')) {
1975
- if (device.info.device._type === 'Coordinator') {
1961
+ if (device.info.device._type == 'Coordinator') {
1976
1962
  return null;
1977
1963
  }
1978
1964
  // check for output clusters
@@ -1992,18 +1978,19 @@ function prepareBindingDialog(bindObj) {
1992
1978
  return null;
1993
1979
  }
1994
1980
  return device.common.name;
1995
- } else { // fallback if device in list but not paired
1996
- return device.common.name + ' ' + device.native.id;
1981
+ }
1982
+ else { // fallback if device in list but not paired
1983
+ device.common.name + ' ' +device.native.id;
1997
1984
  }
1998
1985
  },
1999
- function (key, device) {
1986
+ function(key, device) {
2000
1987
  if (device == '') {
2001
1988
  return '';
2002
1989
  } else {
2003
1990
  return device._id;
2004
1991
  }
2005
1992
  },
2006
- function (key, device) {
1993
+ function(key, device) {
2007
1994
  if (device == '') {
2008
1995
  return 'disabled';
2009
1996
  } else if (device.icon) {
@@ -2018,12 +2005,12 @@ function prepareBindingDialog(bindObj) {
2018
2005
  bindtargets.push({'_id': key, 'groupId': key, 'groupName': groups[key]});
2019
2006
  }
2020
2007
  list2select('#bind_target', bindtargets, bind_target,
2021
- function (key, device) {
2008
+ function(key, device) {
2022
2009
  if (device == '') {
2023
2010
  return 'Select target device';
2024
2011
  }
2025
2012
  if (device.hasOwnProperty('info')) {
2026
- if (device.info.device._type === 'Coordinator') {
2013
+ if (device.info.device._type == 'Coordinator') {
2027
2014
  return null;
2028
2015
  }
2029
2016
  // check for input clusters
@@ -2049,14 +2036,14 @@ function prepareBindingDialog(bindObj) {
2049
2036
  }
2050
2037
  }
2051
2038
  },
2052
- function (key, device) {
2039
+ function(key, device) {
2053
2040
  if (device == '') {
2054
2041
  return '';
2055
2042
  } else {
2056
2043
  return device._id;
2057
2044
  }
2058
2045
  },
2059
- function (key, device) {
2046
+ function(key, device) {
2060
2047
  if (device == '') {
2061
2048
  return 'disabled';
2062
2049
  } else if (device.icon) {
@@ -2075,17 +2062,13 @@ function prepareBindingDialog(bindObj) {
2075
2062
  const epList = device ? device.info.endpoints : [];
2076
2063
  const sClusterList = epList.map((ep) => {
2077
2064
  const clusters = ep.outputClusters.map((cl) => {
2078
- return allowClusters.includes(cl) ? {ID: ep.ID + '_' + cl, name: allowClustersName[cl]} : null;
2079
- }).filter((i) => {
2080
- return i != null;
2081
- });
2082
- return clusters.length == 0 ? null : [{ID: ep.ID, name: 'all'}, clusters];
2083
- }).flat(2).filter((i) => {
2084
- return i != null;
2085
- });
2065
+ return allowClusters.includes(cl) ? {ID: ep.ID+'_'+cl, name: allowClustersName[cl]} : null;
2066
+ }).filter((i) => {return i != null;});
2067
+ return clusters.length == 0 ? null: [{ID: ep.ID, name: 'all'}, clusters];
2068
+ }).flat(2).filter((i) => {return i != null;});
2086
2069
  list2select('#bind_source_ep', sClusterList, (selected) ? [selected] : [],
2087
2070
  (key, ep) => {
2088
- return ep.ID + ' ' + ep.name;
2071
+ return ep.ID+' '+ep.name;
2089
2072
  },
2090
2073
  (key, ep) => {
2091
2074
  return ep.ID;
@@ -2101,20 +2084,13 @@ function prepareBindingDialog(bindObj) {
2101
2084
  const epList = device ? device.info.endpoints : [];
2102
2085
  const tClusterList = epList.map((ep) => {
2103
2086
  const clusters = ep.inputClusters.map((cl) => {
2104
- return (allowClusters.includes(cl) && (!sourceCl || sourceCl == cl)) ? {
2105
- ID: ep.ID + '_' + cl,
2106
- name: allowClustersName[cl]
2107
- } : null;
2108
- }).filter((i) => {
2109
- return i != null;
2110
- });
2111
- return !clusters.length ? null : [{ID: ep.ID, name: 'all'}, clusters];
2112
- }).flat(2).filter(i => {
2113
- return i != null;
2114
- });
2087
+ return (allowClusters.includes(cl) && (!sourceCl || sourceCl == cl)) ? {ID: ep.ID+'_'+cl, name: allowClustersName[cl]} : null;
2088
+ }).filter((i) => {return i != null;});
2089
+ return clusters.length == 0 ? null: [{ID: ep.ID, name: 'all'}, clusters];
2090
+ }).flat(2).filter((i) => {return i != null;});
2115
2091
  list2select('#bind_target_ep', tClusterList, (selected) ? [selected] : [],
2116
2092
  (key, ep) => {
2117
- return ep.ID + ' ' + ep.name;
2093
+ return ep.ID+' '+ep.name;
2118
2094
  },
2119
2095
  (key, ep) => {
2120
2096
  return ep.ID;
@@ -2122,7 +2098,7 @@ function prepareBindingDialog(bindObj) {
2122
2098
  );
2123
2099
  };
2124
2100
 
2125
- $('#bind_source').change(function () {
2101
+ $('#bind_source').change(function() {
2126
2102
  if (this.selectedIndex <= 0) {
2127
2103
  return;
2128
2104
  }
@@ -2134,7 +2110,7 @@ function prepareBindingDialog(bindObj) {
2134
2110
  configureSourceEp();
2135
2111
  }
2136
2112
 
2137
- $('#bind_target').change(function () {
2113
+ $('#bind_target').change(function() {
2138
2114
  if (this.selectedIndex <= 0) {
2139
2115
  return;
2140
2116
  }
@@ -2147,7 +2123,7 @@ function prepareBindingDialog(bindObj) {
2147
2123
  configureTargetEp();
2148
2124
  }
2149
2125
 
2150
- $('#bind_source_ep').change(function () {
2126
+ $('#bind_source_ep').change(function() {
2151
2127
  $('#bind_target').trigger('change');
2152
2128
  });
2153
2129
 
@@ -2156,8 +2132,8 @@ function prepareBindingDialog(bindObj) {
2156
2132
  }
2157
2133
 
2158
2134
  function addBindingDialog() {
2159
- $('#bindingmodaledit a.btn[name=\'save\']').unbind('click');
2160
- $('#bindingmodaledit a.btn[name=\'save\']').click(() => {
2135
+ $("#bindingmodaledit a.btn[name='save']").unbind('click');
2136
+ $("#bindingmodaledit a.btn[name='save']").click(() => {
2161
2137
  const //bind_id = $('#bindingmodaledit').find("input[id='bind_id']").val(),
2162
2138
  bind_source = $('#bindingmodaledit').find('#bind_source option:selected').val(),
2163
2139
  bind_source_ep = $('#bindingmodaledit').find('#bind_source_ep option:selected').val(),
@@ -2181,8 +2157,10 @@ function addBinding(bind_source, bind_source_ep, bind_target, bind_target_ep, un
2181
2157
  unbind_from_coordinator
2182
2158
  }, function (msg) {
2183
2159
  closeWaitingDialog();
2184
- if (msg && msg.error) {
2185
- showMessage(msg.error, _('Error'));
2160
+ if (msg) {
2161
+ if (msg.error) {
2162
+ showMessage(msg.error, _('Error'));
2163
+ }
2186
2164
  }
2187
2165
  getBinding();
2188
2166
  });
@@ -2199,8 +2177,10 @@ function editBinding(bind_id, bind_source, bind_source_ep, bind_target, bind_tar
2199
2177
  unbind_from_coordinator
2200
2178
  }, function (msg) {
2201
2179
  closeWaitingDialog();
2202
- if (msg && msg.error) {
2203
- showMessage(msg.error, _('Error'));
2180
+ if (msg) {
2181
+ if (msg.error) {
2182
+ showMessage(msg.error, _('Error'));
2183
+ }
2204
2184
  }
2205
2185
  getBinding();
2206
2186
  });
@@ -2208,8 +2188,8 @@ function editBinding(bind_id, bind_source, bind_source_ep, bind_target, bind_tar
2208
2188
  }
2209
2189
 
2210
2190
  function editBindingDialog(bindObj) {
2211
- $('#bindingmodaledit a.btn[name=\'save\']').unbind('click');
2212
- $('#bindingmodaledit a.btn[name=\'save\']').click(() => {
2191
+ $("#bindingmodaledit a.btn[name='save']").unbind('click');
2192
+ $("#bindingmodaledit a.btn[name='save']").click(() => {
2213
2193
  const //bind_id = $('#bindingmodaledit').find("input[id='bind_id']").val(),
2214
2194
  bind_source = $('#bindingmodaledit').find('#bind_source option:selected').val(),
2215
2195
  bind_source_ep = $('#bindingmodaledit').find('#bind_source_ep option:selected').val(),
@@ -2245,9 +2225,9 @@ function showBinding() {
2245
2225
  <i class="right">${target_icon}</i>
2246
2226
  <div style="min-height:72px; font-size: 0.8em" class="truncate">
2247
2227
  <ul>
2248
- <li><span class="label">source:</span><span>0x${bind_source.replace(namespace + '.', '')}</span></li>
2228
+ <li><span class="label">source:</span><span>0x${bind_source.replace(namespace+'.', '')}</span></li>
2249
2229
  <li><span class="label">endpoint:</span><span>${bind_source_ep}</span></li>
2250
- <li><span class="label">target:</span><span>0x${bind_target.replace(namespace + '.', '')}</span></li>
2230
+ <li><span class="label">target:</span><span>0x${bind_target.replace(namespace+'.', '')}</span></li>
2251
2231
  <li><span class="label">endpoint:</span><span>${bind_target_ep}</span></li>
2252
2232
  </ul>
2253
2233
  </div>
@@ -2269,11 +2249,11 @@ function showBinding() {
2269
2249
  element.append(card);
2270
2250
  });
2271
2251
 
2272
- $('#binding button[name=\'delete\']').click(function () {
2252
+ $("#binding button[name='delete']").click(function() {
2273
2253
  const bind_id = $(this).parents('.binding')[0].id;
2274
2254
  deleteBindingConfirmation(bind_id);
2275
2255
  });
2276
- $('#binding button[name=\'edit\']').click(function () {
2256
+ $("#binding button[name='edit']").click(function() {
2277
2257
  const bind_id = $(this).parents('.binding')[0].id;
2278
2258
  const bindObj = binding.find((b) => b.id == bind_id);
2279
2259
  if (bindObj) {
@@ -2300,8 +2280,8 @@ function deleteBindingConfirmation(id) {
2300
2280
  $('#modaldelete').find('p').text(text);
2301
2281
  //$('#forcediv').removeClass('hide');
2302
2282
  $('#forcediv').addClass('hide');
2303
- $('#modaldelete a.btn[name=\'yes\']').unbind('click');
2304
- $('#modaldelete a.btn[name=\'yes\']').click(() => {
2283
+ $("#modaldelete a.btn[name='yes']").unbind('click');
2284
+ $("#modaldelete a.btn[name='yes']").click(() => {
2305
2285
  deleteBinding(id);
2306
2286
  });
2307
2287
  $('#modaldelete').modal('open');
@@ -2334,14 +2314,14 @@ function genDevInfo(device) {
2334
2314
  const dev = (device && device.info) ? device.info.device : undefined;
2335
2315
  const mapped = (device && device.info) ? device.info.mapped : undefined;
2336
2316
  if (!dev) return `<div class="truncate">No info</div>`;
2337
- const genRow = function (name, value, refresh) {
2317
+ const genRow = function(name, value, refresh) {
2338
2318
  if (value === undefined) {
2339
2319
  return '';
2340
2320
  } else {
2341
2321
  return `<li><span class="label">${name}:</span><span>${value}</span></li>`;
2342
2322
  }
2343
2323
  };
2344
- const genRowValues = function (name, value) {
2324
+ const genRowValues = function(name, value) {
2345
2325
  if (value === undefined) {
2346
2326
  return '';
2347
2327
  } else {
@@ -2376,7 +2356,7 @@ function genDevInfo(device) {
2376
2356
  </div>`;
2377
2357
  }
2378
2358
  const imgSrc = device.icon || device.common.icon;
2379
- const imgInfo = (imgSrc) ? `<img src=${imgSrc} width='150px' onerror="this.onerror=null;this.src='img/unavailable.png';"><div class="divider"></div>` : '';
2359
+ const imgInfo = (imgSrc) ? `<img src=${imgSrc} width='150px' onerror="this.onerror=null;this.src='img/unavailable.png';"><div class="divider"></div>`: '';
2380
2360
  const info =
2381
2361
  `<div class="col s12 m6 l6 xl6">
2382
2362
  ${imgInfo}
@@ -2408,28 +2388,29 @@ function genDevInfo(device) {
2408
2388
  return info;
2409
2389
  }
2410
2390
 
2411
- function showDevInfo(id) {
2391
+ function showDevInfo(id){
2412
2392
  const info = genDevInfo(getDeviceByID(id));
2413
2393
  $('#devinfo').html(info);
2414
2394
  $('#modaldevinfo').modal('open');
2415
2395
  }
2416
2396
 
2417
- function showGroupList(show) {
2397
+ function showGroupList(show){
2418
2398
  const htmlsections = [];
2419
2399
  for (const groupid in devGroups) {
2420
- const dev = devGroups[groupid];
2421
- const grpname = (dev.common && dev.common.name ? dev.common.name : 'Group ' + groupid);
2422
- const selectables = [];
2423
- const members = [];
2424
- if (dev && dev.memberinfo) {
2425
- selectables.push(`<select id="members_${groupid}" multiple>`);
2426
- for (let m = 0; m < dev.memberinfo.length; m++) {
2427
- members.push(`${dev.memberinfo[m].device}.${dev.memberinfo[m].epid} (${dev.memberinfo[m].ieee})`);
2428
- selectables.push(`<option value="${m}">${dev.memberinfo[m].device}.${dev.memberinfo[m].epid} (...${dev.memberinfo[m].ieee.slice(-4)})</option>`);
2429
- }
2430
- selectables.push('</select>');
2431
- }
2432
- htmlsections.push(`
2400
+ const dev = devGroups[groupid];
2401
+ const grpname = (dev.common && dev.common.name?dev.common.name:'Group '+groupid);
2402
+ const selectables = [];
2403
+ const members = [];
2404
+ if (dev && dev.memberinfo)
2405
+ {
2406
+ selectables.push(`<select id="members_${groupid}" multiple>`)
2407
+ for (let m=0;m<dev.memberinfo.length; m++) {
2408
+ members.push(`${dev.memberinfo[m].device}.${dev.memberinfo[m].epid} (${dev.memberinfo[m].ieee})`)
2409
+ selectables.push(`<option value="${m}">${dev.memberinfo[m].device}.${dev.memberinfo[m].epid} (...${dev.memberinfo[m].ieee.slice(-4)})</option>`);
2410
+ }
2411
+ selectables.push('</select>');
2412
+ }
2413
+ htmlsections.push(`
2433
2414
  <div class="row">
2434
2415
  <div class="col s4 m4 l4">
2435
2416
  <h5>${grpname}<h5>
@@ -2442,38 +2423,38 @@ function showGroupList(show) {
2442
2423
  }
2443
2424
 
2444
2425
  $('#grouplist').html(htmlsections.join(''));
2445
- $('#add').click(function () {
2446
- const maxind = parseInt(Object.getOwnPropertyNames(groups).reduce((a, b) => a > b ? a : b, 0));
2447
- editGroupName(maxind + 1, 'Group ' + maxind + 1, true);
2426
+ $('#add').click(function() {
2427
+ const maxind = parseInt(Object.getOwnPropertyNames(groups).reduce((a,b) => a>b ? a : b, 0));
2428
+ editGroupName(maxind+1, 'Group ' + maxind+1, true);
2448
2429
  showGroupList(false);
2449
2430
  });
2450
2431
 
2451
- $('#modalgrouplist a.btn[name=\'save\']').unbind('click');
2452
- $('#modalgrouplist a.btn[name=\'save\']').click(() => {
2432
+ $("#modalgrouplist a.btn[name='save']").unbind('click');
2433
+ $("#modalgrouplist a.btn[name='save']").click(() => {
2453
2434
  });
2454
2435
  if (show) $('#modalgrouplist').modal('open');
2455
2436
  }
2456
2437
 
2457
2438
  let waitingTimeout, waitingInt;
2458
2439
 
2459
- function showWaitingDialog(text, timeout) {
2440
+ function showWaitingDialog(text, timeout){
2460
2441
  let countDown = timeout;
2461
- waitingInt = setInterval(function () {
2442
+ waitingInt = setInterval(function() {
2462
2443
  countDown -= 1;
2463
- const percent = 100 - 100 * countDown / timeout;
2444
+ const percent = 100-100*countDown/timeout;
2464
2445
  $('#waiting_progress_line').css('width', `${percent}%`);
2465
2446
  }, 1000);
2466
- waitingTimeout = setTimeout(function () {
2447
+ waitingTimeout = setTimeout(function() {
2467
2448
  $('#waiting_progress_line').css('width', `0%`);
2468
2449
  clearTimeout(waitingInt);
2469
2450
  clearTimeout(waitingTimeout);
2470
2451
  $('#modalWaiting').modal('close');
2471
- }, timeout * 1000);
2452
+ }, timeout*1000);
2472
2453
  $('#waiting_message').text(text);
2473
2454
  $('#modalWaiting').modal('open');
2474
2455
  }
2475
2456
 
2476
- function closeWaitingDialog() {
2457
+ function closeWaitingDialog(){
2477
2458
  if (waitingInt) clearTimeout(waitingInt);
2478
2459
  if (waitingTimeout) clearTimeout(waitingTimeout);
2479
2460
  $('#modalWaiting').modal('close');
@@ -2490,7 +2471,7 @@ function showChannels() {
2490
2471
  $('#modalchannels').modal('open');
2491
2472
  let info = '';
2492
2473
  for (let ch = 11; ch < 27; ch++) {
2493
- const value = msg.energyvalues[ch - 11];
2474
+ const value = msg.energyvalues[ch-11];
2494
2475
  info +=
2495
2476
  `<div style="padding-top: 10px">
2496
2477
  <span class="">№ ${ch}: ${value}%</span>
@@ -2548,7 +2529,7 @@ function prepareExcludeDialog(excludeObj) {
2548
2529
 
2549
2530
  list2select('#exclude_target', onlyOneTargets, exclude_target,
2550
2531
 
2551
- function (key, device) {
2532
+ function(key, device) {
2552
2533
  if (device == '') {
2553
2534
  return 'Select model';
2554
2535
  }
@@ -2561,14 +2542,14 @@ function prepareExcludeDialog(excludeObj) {
2561
2542
  return device.common.type;
2562
2543
  }
2563
2544
  },
2564
- function (key, device) {
2545
+ function(key, device) {
2565
2546
  if (device == '') {
2566
2547
  return '';
2567
2548
  } else {
2568
2549
  return device._id;
2569
2550
  }
2570
2551
  },
2571
- function (key, device) {
2552
+ function(key, device) {
2572
2553
  if (device == '') {
2573
2554
  return 'disabled';
2574
2555
  } else if (device.icon) {
@@ -2582,8 +2563,8 @@ function prepareExcludeDialog(excludeObj) {
2582
2563
  }
2583
2564
 
2584
2565
  function addExcludeDialog() {
2585
- $('#excludemodaledit a.btn[name=\'save\']').unbind('click');
2586
- $('#excludemodaledit a.btn[name=\'save\']').click(() => {
2566
+ $("#excludemodaledit a.btn[name='save']").unbind('click');
2567
+ $("#excludemodaledit a.btn[name='save']").click(() => {
2587
2568
  const exclude_id = $('#excludemodaledit').find('#exclude_target option:selected').val();
2588
2569
 
2589
2570
  const ids = devices.map(el => el._id);
@@ -2666,7 +2647,7 @@ function showExclude() {
2666
2647
  element.append(card);
2667
2648
  });
2668
2649
 
2669
- $('#exclude button[name=\'delete\']').click(function () {
2650
+ $("#exclude button[name='delete']").click(function() {
2670
2651
  const exclude_id = $(this).parents('.exclude')[0].id;
2671
2652
  deleteExcludeConfirmation(exclude_id);
2672
2653
  deleteExclude(exclude_id);
@@ -2674,13 +2655,14 @@ function showExclude() {
2674
2655
  }
2675
2656
 
2676
2657
 
2658
+
2677
2659
  function deleteExcludeConfirmation(id) {
2678
2660
  const text = translateWord('Do you really want to delete exclude?');
2679
2661
  $('#modaldelete').find('p').text(text);
2680
2662
  //$('#forcediv').removeClass('hide');
2681
2663
  $('#forcediv').addClass('hide');
2682
- $('#modaldelete a.btn[name=\'yes\']').unbind('click');
2683
- $('#modaldelete a.btn[name=\'yes\']').click(() => {
2664
+ $("#modaldelete a.btn[name='yes']").unbind('click');
2665
+ $("#modaldelete a.btn[name='yes']").click(() => {
2684
2666
  deleteExclude(id);
2685
2667
  });
2686
2668
  $('#modaldelete').modal('open');
@@ -2721,7 +2703,7 @@ function doFilter(inputText) {
2721
2703
  } else {
2722
2704
  return room;
2723
2705
  }
2724
- }).filter((item) => item != undefined).map((item) => item.toLowerCase().trim());
2706
+ }).filter((item)=>item != undefined).map((item)=>item.toLowerCase().trim());
2725
2707
  valid = rooms.includes(roomFilter);
2726
2708
  } else {
2727
2709
  valid = false;
@@ -2738,9 +2720,9 @@ function doFilter(inputText) {
2738
2720
  function doSort() {
2739
2721
  if (shuffleInstance) {
2740
2722
  const sortOrder = $('#device-order-btn').text().toLowerCase();
2741
- if (sortOrder === 'default') {
2723
+ if (sortOrder == 'default') {
2742
2724
  shuffleInstance.sort({});
2743
- } else if (sortOrder === 'a-z') {
2725
+ } else if (sortOrder == 'a-z') {
2744
2726
  shuffleInstance.sort({
2745
2727
  by: sortByTitle
2746
2728
  });
@@ -2754,12 +2736,12 @@ function sortByTitle(element) {
2754
2736
 
2755
2737
  function getDashCard(dev, groupImage) {
2756
2738
  const title = dev.common.name,
2757
- id = dev._id,
2758
- type = dev.common.type,
2759
- img_src = (groupImage ? groupImage : dev.icon || dev.common.icon),
2760
- isActive = !dev.common.deactivated,
2761
- rooms = [],
2762
- lang = systemLang || 'en';
2739
+ id = dev._id,
2740
+ type = dev.common.type,
2741
+ img_src = (groupImage ? groupImage: dev.icon || dev.common.icon),
2742
+ isActive = (dev.common.deactivated ? false : true),
2743
+ rooms = [],
2744
+ lang = systemLang || 'en';
2763
2745
  const paired = (dev.paired) ? '' : '<i class="material-icons right">leak_remove</i>';
2764
2746
  const rid = id.split('.').join('_');
2765
2747
  const modelUrl = (!type) ? '' : `<a href="https://www.zigbee2mqtt.io/devices/${type}.html" target="_blank" rel="noopener noreferrer">${type}</a>`;
@@ -2768,46 +2750,46 @@ function getDashCard(dev, groupImage) {
2768
2750
  battery_cls = getBatteryCls(dev.battery),
2769
2751
  lqi_cls = getLQICls(dev.link_quality),
2770
2752
  battery = (dev.battery && isActive) ? `<div class="col tool"><i id="${rid}_battery_icon" class="material-icons ${battery_cls}">battery_std</i><div id="${rid}_battery" class="center" style="font-size:0.7em">${dev.battery}</div></div>` : '',
2771
- lq = (dev.link_quality > 0 && isActive) ? `<div class="col tool"><i id="${rid}_link_quality_icon" class="material-icons ${lqi_cls}">network_check</i><div id="${rid}_link_quality" class="center" style="font-size:0.7em">${dev.link_quality}</div></div>` : (isActive ? '<div class="col tool"><i class="material-icons icon-green">check_circle</i></div>' : ''),
2772
- status = (dev.link_quality > 0 && isActive) ? `<div class="col tool"><i class="material-icons icon-green">check_circle</i></div>` : (groupImage || !isActive ? '' : `<div class="col tool"><i class="material-icons icon-black">leak_remove</i></div>`),
2773
- permitJoinBtn = (isActive && dev.info && dev.info.device._type === 'Router') ? '<button name="join" class="btn-floating btn-small waves-effect waves-light right hoverable green"><i class="material-icons tiny">leak_add</i></button>' : '',
2753
+ lq = (dev.link_quality > 0 && isActive) ? `<div class="col tool"><i id="${rid}_link_quality_icon" class="material-icons ${lqi_cls}">network_check</i><div id="${rid}_link_quality" class="center" style="font-size:0.7em">${dev.link_quality}</div></div>` : (isActive ? '<div class="col tool"><i class="material-icons icon-green">check_circle</i></div>':''),
2754
+ status = (dev.link_quality > 0 && isActive) ? `<div class="col tool"><i class="material-icons icon-green">check_circle</i></div>` : (groupImage || !isActive ? '': `<div class="col tool"><i class="material-icons icon-black">leak_remove</i></div>`),
2755
+ permitJoinBtn = (isActive && dev.info && dev.info.device._type == 'Router') ? '<button name="join" class="btn-floating btn-small waves-effect waves-light right hoverable green"><i class="material-icons tiny">leak_add</i></button>' : '',
2774
2756
  infoBtn = (nwk) ? `<button name="info" class="left btn-flat btn-small"><i class="material-icons icon-blue">info</i></button>` : '',
2775
2757
  idleTime = (dev.link_quality_lc > 0 && isActive) ? `<div class="col tool"><i id="${rid}_link_quality_lc_icon" class="material-icons idletime">access_time</i><div id="${rid}_link_quality_lc" class="center" style="font-size:0.7em">${getIdleTime(dev.link_quality_lc)}</div></div>` : '';
2776
- const info = (dev.statesDef) ? dev.statesDef.map((stateDef) => {
2758
+ const info = (dev.statesDef) ? dev.statesDef.map((stateDef)=>{
2777
2759
  const id = stateDef.id;
2778
2760
  const sid = id.split('.').join('_');
2779
2761
  let val = stateDef.val || '';
2780
- if (stateDef.role === 'switch' && stateDef.write) {
2781
- val = `<span class="switch"><label><input type="checkbox" ${(val) ? 'checked' : ''}><span class="lever"></span></label></span>`;
2782
- } else if (stateDef.role === 'level.dimmer' && stateDef.write) {
2783
- val = `<span class="range-field dash"><input type="range" min="0" max="100" ${(val != undefined) ? `value="${val}"` : ''} /></span>`;
2784
- } else if (stateDef.role === 'level.color.temperature' && stateDef.write) {
2785
- val = `<span class="range-field dash"><input type="range" min="150" max="500" ${(val != undefined) ? `value="${val}"` : ''} /></span>`;
2786
- } else if (stateDef.type === 'boolean') {
2762
+ if (stateDef.role == 'switch' && stateDef.write) {
2763
+ val = `<span class="switch"><label><input type="checkbox" ${(val) ? "checked" : ""}><span class="lever"></span></label></span>`;
2764
+ } else if (stateDef.role == 'level.dimmer' && stateDef.write) {
2765
+ val = `<span class="range-field dash"><input type="range" min="0" max="100" ${(val != undefined) ? `value="${val}"` : ""} /></span>`;
2766
+ } else if (stateDef.role == 'level.color.temperature' && stateDef.write) {
2767
+ val = `<span class="range-field dash"><input type="range" min="150" max="500" ${(val != undefined) ? `value="${val}"` : ""} /></span>`;
2768
+ } else if (stateDef.type == 'boolean') {
2787
2769
  const disabled = (stateDef.write) ? '' : 'disabled="disabled"';
2788
- val = `<label class="dash"><input type="checkbox" ${(val == true) ? 'checked=\'checked\'' : ''} ${disabled}/><span></span></label>`;
2770
+ val = `<label class="dash"><input type="checkbox" ${(val == true) ? "checked='checked'" : ""} ${disabled}/><span></span></label>`;
2789
2771
  } else if (stateDef.states && stateDef.write) {
2790
2772
  let options;
2791
- if (typeof stateDef.states == 'string') {
2773
+ if(typeof stateDef.states == "string") {
2792
2774
  const sts = stateDef.states.split(';');
2793
2775
  options = sts.map((item) => {
2794
2776
  const v = item.split(':');
2795
- return `<option value="${v[0]}" ${(val == v[0]) ? 'selected' : ''}>${v[1]}</option>`;
2777
+ return `<option value="${v[0]}" ${(val == v[0]) ? "selected" : ""}>${v[1]}</option>`;
2796
2778
  });
2797
2779
  } else {
2798
2780
  options = [];
2799
- for (const [key, value] of Object.entries(stateDef.states)) {
2800
- options.push(`<option value="${key}" ${(val == key) ? 'selected' : ''}>${value}</option>`);
2781
+ for(const [key, value] of Object.entries(stateDef.states)) {
2782
+ options.push(`<option value="${key}" ${(val == key) ? "selected" : ""}>${value}</option>`);
2801
2783
  }
2802
2784
  }
2803
- val = `<select class="browser-default enum" style="height: 16px; padding: 0; width: auto; display: inline-block">${options.join('')}</select>`;
2785
+ val = `<select class="browser-default enum" style="height: 16px; padding: 0px; width: auto; display: inline-block">${options.join('')}</select>`;
2804
2786
  } else {
2805
2787
  val = `<span class="dash value">${val} ${(stateDef.unit) ? stateDef.unit : ''}</span>`;
2806
2788
  }
2807
2789
  return `<li><span class="label dash truncate">${stateDef.name}</span><span id=${sid} oid=${id} class="state">${val}</span></li>`;
2808
2790
  }).join('') : '';
2809
2791
  const dashCard = `
2810
- <div class="card-content zcard ${isActive ? '' : 'bg_red'}">
2792
+ <div class="card-content zcard ${isActive?'':'bg_red'}">
2811
2793
  <div class="flip" style="cursor: pointer">
2812
2794
  <span class="top right small" style="border-radius: 50%">
2813
2795
  ${idleTime}
@@ -2820,7 +2802,7 @@ function getDashCard(dev, groupImage) {
2820
2802
  <i class="left">${image}</i>
2821
2803
  <div style="min-height:88px; font-size: 0.8em; height: 130px; overflow-y: auto" class="truncate">
2822
2804
  <ul>
2823
- ${(isActive ? info : 'Device deactivated')}
2805
+ ${(isActive?info:'Device deactivated')}
2824
2806
  </ul>
2825
2807
  </div>
2826
2808
  <div class="footer right-align"></div>
@@ -2833,19 +2815,19 @@ function setDashStates(id, state) {
2833
2815
  const devId = getDevId(id);
2834
2816
  const dev = getDeviceByID(devId);
2835
2817
  if (dev) {
2836
- const stateDef = dev.statesDef.find((stateDef) => stateDef.id == id);
2818
+ const stateDef = dev.statesDef.find((stateDef)=> stateDef.id == id);
2837
2819
  if (stateDef) {
2838
2820
  const sid = id.split('.').join('_');
2839
- if (stateDef.role === 'switch' && stateDef.write) {
2840
- $(`#${sid}`).find('input[type=\'checkbox\']').prop('checked', state.val);
2841
- } else if (stateDef.role === 'level.dimmer' && stateDef.write) {
2842
- $(`#${sid}`).find('input[type=\'range\']').prop('value', state.val);
2843
- } else if (stateDef.role === 'level.color.temperature' && stateDef.write) {
2844
- $(`#${sid}`).find('input[type=\'range\']').prop('value', state.val);
2821
+ if (stateDef.role == 'switch' && stateDef.write) {
2822
+ $(`#${sid}`).find("input[type='checkbox']").prop('checked', state.val);
2823
+ } else if (stateDef.role == 'level.dimmer' && stateDef.write) {
2824
+ $(`#${sid}`).find("input[type='range']").prop('value', state.val);
2825
+ } else if (stateDef.role == 'level.color.temperature' && stateDef.write) {
2826
+ $(`#${sid}`).find("input[type='range']").prop('value', state.val);
2845
2827
  } else if (stateDef.states && stateDef.write) {
2846
2828
  $(`#${sid}`).find(`select option[value=${state.val}]`).prop('selected', true);
2847
- } else if (stateDef.type === 'boolean') {
2848
- $(`#${sid}`).find('input[type=\'checkbox\']').prop('checked', state.val);
2829
+ } else if (stateDef.type == 'boolean') {
2830
+ $(`#${sid}`).find("input[type='checkbox']").prop('checked', state.val);
2849
2831
  } else {
2850
2832
  $(`#${sid}`).find('.value').text(`${state.val} ${(stateDef.unit) ? stateDef.unit : ''}`);
2851
2833
  }
@@ -2854,23 +2836,23 @@ function setDashStates(id, state) {
2854
2836
  }
2855
2837
 
2856
2838
  function hookControls() {
2857
- $('input[type=\'checkbox\']').change(function (event) {
2839
+ $("input[type='checkbox']").change(function (event) {
2858
2840
  const val = $(this).is(':checked');
2859
- const id = $(this).parents('.state').attr('oid');
2841
+ const id = $(this).parents(".state").attr('oid');
2860
2842
  sendTo(namespace, 'setState', {id: id, val: val}, function (data) {
2861
2843
  //console.log(data);
2862
2844
  });
2863
2845
  });
2864
- $('input[type=\'range\']').change(function (event) {
2846
+ $("input[type='range']").change(function (event) {
2865
2847
  const val = $(this).val();
2866
- const id = $(this).parents('.state').attr('oid');
2848
+ const id = $(this).parents(".state").attr('oid');
2867
2849
  sendTo(namespace, 'setState', {id: id, val: val}, function (data) {
2868
2850
  //console.log(data);
2869
2851
  });
2870
2852
  });
2871
- $('.state select').on('change', function () {
2853
+ $(".state select").on( "change", function () {
2872
2854
  const val = $(this).val();
2873
- const id = $(this).parents('.state').attr('oid');
2855
+ const id = $(this).parents(".state").attr('oid');
2874
2856
  sendTo(namespace, 'setState', {id: id, val: val}, function (data) {
2875
2857
  //console.log(data);
2876
2858
  });
@@ -2878,12 +2860,12 @@ function hookControls() {
2878
2860
  }
2879
2861
 
2880
2862
  function getIdleTime(value) {
2881
- return (value) ? moment(new Date(value)).fromNow(true) : '';
2863
+ return (value) ? moment(new Date(value)).fromNow(true) : "";
2882
2864
  }
2883
2865
 
2884
2866
  function updateCardTimer() {
2885
2867
  if (devices) {
2886
- devices.forEach((dev) => {
2868
+ devices.forEach((dev)=>{
2887
2869
  const id = dev._id;
2888
2870
  if (id) {
2889
2871
  const rid = id.split('.').join('_');
@@ -2900,7 +2882,9 @@ function updateDevice(id) {
2900
2882
  showMessage(devs.error, _('Error'));
2901
2883
  } else {
2902
2884
  removeDevice(id);
2903
- devs.forEach(dev => devices.push(dev));
2885
+ devs.forEach((dev)=>{
2886
+ devices.push(dev);
2887
+ })
2904
2888
  showDevices();
2905
2889
  }
2906
2890
  }
@@ -2918,21 +2902,21 @@ function removeDevice(id) {
2918
2902
  }
2919
2903
 
2920
2904
  function swapActive(id) {
2921
- const dev = getDeviceByID(id);
2922
- if (dev && dev.common) {
2923
- dev.common.deactivated = !(dev.common.deactivated);
2924
- sendTo(namespace, 'setDeviceActivated', {id: id, deactivated: dev.common.deactivated}, function () {
2925
- showDevices();
2926
- });
2927
- }
2905
+ const dev = getDeviceByID(id);
2906
+ if (dev && dev.common) {
2907
+ dev.common.deactivated = !(dev.common.deactivated)
2908
+ sendTo(namespace, 'setDeviceActivated', {id: id, deactivated: dev.common.deactivated}, function () {
2909
+ showDevices();
2910
+ });
2911
+ }
2928
2912
  }
2929
2913
 
2930
2914
  function reconfigureDlg(id) {
2931
2915
  const text = translateWord(`Do you really want to reconfigure device?`);
2932
2916
  $('#modalreconfigure').find('p').text(text);
2933
- $('#modalreconfigure a.btn[name=\'yes\']').unbind('click');
2934
- $('#modalreconfigure a.btn[name=\'yes\']').click(() => {
2935
- reconfigureDevice(id/*, force*/);
2917
+ $("#modalreconfigure a.btn[name='yes']").unbind('click');
2918
+ $("#modalreconfigure a.btn[name='yes']").click(() => {
2919
+ reconfigureDevice(id, force);
2936
2920
  });
2937
2921
  $('#modalreconfigure').modal('open');
2938
2922
  Materialize.updateTextFields();