iobroker.zigbee 1.8.3 → 1.8.5

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