iobroker.zigbee 1.8.3 → 1.8.7

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 +6 -0
  2. package/admin/adapter-settings.js +244 -0
  3. package/admin/admin.js +520 -494
  4. package/admin/index_m.html +1171 -1001
  5. package/admin/tab_m.html +44 -2
  6. package/docs/de/img/CC2531.png +0 -0
  7. package/docs/de/img/CC2538_CC2592_PA.PNG +0 -0
  8. package/docs/de/img/CC2591.png +0 -0
  9. package/docs/de/img/boards.jpg +0 -0
  10. package/docs/de/img/cc26x2r.PNG +0 -0
  11. package/docs/de/img/results.jpg +0 -0
  12. package/docs/de/img/sku_429478_2.png +0 -0
  13. package/docs/de/img/sku_429601_2.png +0 -0
  14. package/docs/de/readme.md +27 -0
  15. package/docs/en/img/CC2531.png +0 -0
  16. package/docs/en/img/CC2591.png +0 -0
  17. package/docs/en/img/deconz.png +0 -0
  18. package/docs/en/img/sku_429478_2.png +0 -0
  19. package/docs/en/img/sku_429601_2.png +0 -0
  20. package/docs/en/readme.md +30 -0
  21. package/docs/flashing_via_arduino_(en).md +110 -0
  22. package/docs/ru/img/CC2531.png +0 -0
  23. package/docs/ru/img/CC2591.png +0 -0
  24. package/docs/ru/img/sku_429478_2.png +0 -0
  25. package/docs/ru/img/sku_429601_2.png +0 -0
  26. package/docs/ru/readme.md +28 -0
  27. package/docs/tutorial/CC2530_20190425.zip +0 -0
  28. package/docs/tutorial/CC2530_CC2591_20190515.zip +0 -0
  29. package/docs/tutorial/CC2530_CC2592_20190515.zip +0 -0
  30. package/docs/tutorial/CC2531_20190425.zip +0 -0
  31. package/docs/tutorial/adm5_1.PNG +0 -0
  32. package/docs/tutorial/adm5_2.PNG +0 -0
  33. package/docs/tutorial/cat.PNG +0 -0
  34. package/docs/tutorial/groups-1.png +0 -0
  35. package/docs/tutorial/groups-2.png +0 -0
  36. package/docs/tutorial/inst.PNG +0 -0
  37. package/docs/tutorial/reflash-finish.PNG +0 -0
  38. package/docs/tutorial/reflash-step0.png +0 -0
  39. package/docs/tutorial/reflash-step1.PNG +0 -0
  40. package/docs/tutorial/reflash-step2.PNG +0 -0
  41. package/docs/tutorial/settings.png +0 -0
  42. package/docs/tutorial/tab-dev-1.png +0 -0
  43. package/docs/tutorial/zigbee.png +0 -0
  44. package/docs/tutorial/zigbee15.png +0 -0
  45. package/io-package.json +34 -33
  46. package/lib/backup.js +2 -2
  47. package/lib/binding.js +32 -37
  48. package/lib/colors.js +163 -158
  49. package/lib/commands.js +100 -91
  50. package/lib/developer.js +9 -12
  51. package/lib/devices.js +168 -178
  52. package/lib/exclude.js +30 -36
  53. package/lib/exposes.js +168 -143
  54. package/lib/groups.js +81 -83
  55. package/lib/json.js +5 -6
  56. package/lib/networkmap.js +2 -3
  57. package/lib/ota.js +34 -18
  58. package/lib/rgb.js +114 -72
  59. package/lib/seriallist.js +25 -20
  60. package/lib/statescontroller.js +206 -183
  61. package/lib/utils.js +29 -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 +28 -14
  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,32 @@ 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
+ if (settings.panID === undefined) {
771
+ settings.panID = 6754;
772
+ }
773
+ if (settings.extPanID === undefined) {
774
+ settings.extPanID = 'DDDDDDDDDDDDDDDD';
775
+ }
779
776
  // 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;
784
-
777
+ if (settings.extPanID === 'DDDDDDDDDDDDDDD') {
778
+ settings.extPanID = 'DDDDDDDDDDDDDDDD';
779
+ }
780
+
781
+ if (settings.precfgkey === undefined) {
782
+ settings.precfgkey = '01030507090B0D0F00020406080A0C0D';
783
+ }
784
+ if (settings.channel === undefined) {
785
+ settings.channel = 11;
786
+ }
787
+ if (settings.disablePing === undefined) {
788
+ settings.disablePing = false;
789
+ }
790
+
785
791
  // example: select elements with id=key and class=value and insert value
786
792
  for (const key in settings) {
787
- if (savedSettings.indexOf(key) === -1) continue;
793
+ if (savedSettings.indexOf(key) === -1) {
794
+ continue;
795
+ }
788
796
  // example: select elements with id=key and class=value and insert value
789
797
  const value = $('#' + key + '.value');
790
798
  if (value.attr('type') === 'checkbox') {
@@ -810,35 +818,36 @@ function load(settings, onChange) {
810
818
  // Signal to admin, that no changes yet
811
819
  onChange(false);
812
820
 
813
- $('#state_cleanup_btn').click(function() {
821
+ $('#state_cleanup_btn').click(function () {
814
822
  cleanConfirmation();
815
823
  });
816
- $('#fw_check_btn').click(function() {
824
+ $('#fw_check_btn').click(function () {
817
825
  checkFwUpdate();
818
826
  });
819
- $('#touchlink_btn').click(function() {
827
+ $('#touchlink_btn').click(function () {
820
828
  touchlinkReset();
821
829
  showPairingProcess();
822
830
  });
823
- $('#pairing').click(function() {
824
- if (!$('#pairing').hasClass('pulse'))
831
+ $('#pairing').click(function () {
832
+ if (!$('#pairing').hasClass('pulse')) {
825
833
  letsPairing();
834
+ }
826
835
  showPairingProcess();
827
836
  });
828
837
 
829
- $('#refresh').click(function() {
838
+ $('#refresh').click(function () {
830
839
  getMap();
831
840
  });
832
841
 
833
- $('#reset-btn').click(function() {
842
+ $('#reset-btn').click(function () {
834
843
  resetConfirmation();
835
844
  });
836
845
 
837
- $('#viewconfig').click(function() {
846
+ $('#viewconfig').click(function () {
838
847
  showViewConfig();
839
848
  });
840
849
 
841
- $('#scan').click(function() {
850
+ $('#scan').click(function () {
842
851
  showChannels();
843
852
  });
844
853
 
@@ -847,18 +856,18 @@ function load(settings, onChange) {
847
856
  showGroups();
848
857
  });
849
858
 
850
- $('#add_group').click(function() {
859
+ $('#add_group').click(function () {
851
860
  // 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);
861
+ const maxind = parseInt(Object.getOwnPropertyNames(groups).reduce((a, b) => a > b ? a : b, 0));
862
+ editGroupName(maxind + 1, 'Group ' + maxind + 1, true);
854
863
  });
855
- $('#add_grp_btn').click(function() {
864
+ $('#add_grp_btn').click(function () {
856
865
  // 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);
866
+ const maxind = parseInt(Object.getOwnPropertyNames(groups).reduce((a, b) => a > b ? a : b, 0));
867
+ editGroupName(maxind + 1, 'Group ' + maxind + 1, true);
859
868
  });
860
869
 
861
- $(document).ready(function() {
870
+ $(document).ready(function () {
862
871
  $('.modal').modal({
863
872
  startingTop: '30%',
864
873
  endingTop: '10%',
@@ -882,7 +891,7 @@ function load(settings, onChange) {
882
891
  $('#pairing').attr('data-tooltip', transText);
883
892
  }
884
893
 
885
- $('ul.tabs').on('click', 'a', function(e) {
894
+ $('ul.tabs').on('click', 'a', function (e) {
886
895
  if ($(e.target).attr('id') == 'tabmap') {
887
896
  redrawMap();
888
897
  }
@@ -891,11 +900,11 @@ function load(settings, onChange) {
891
900
  }
892
901
  });
893
902
 
894
- $('#add_exclude').click(function() {
903
+ $('#add_exclude').click(function () {
895
904
  addExcludeDialog();
896
905
  });
897
906
 
898
- $('#add_binding').click(function() {
907
+ $('#add_binding').click(function () {
899
908
  addBindingDialog();
900
909
  });
901
910
 
@@ -934,7 +943,7 @@ function save(callback) {
934
943
  const $this = $(this);
935
944
  if (savedSettings.indexOf($this.attr('id')) === -1) return;
936
945
  if ($this.hasClass('validate') && $this.hasClass('invalid')) {
937
- showMessage('Invalid input for ' +$this.attr('id'), _('Error'));
946
+ showMessage('Invalid input for ' + $this.attr('id'), _('Error'));
938
947
  return;
939
948
  }
940
949
  if ($this.attr('type') === 'checkbox') {
@@ -948,7 +957,7 @@ function save(callback) {
948
957
 
949
958
 
950
959
  function getDevId(adapterDevId) {
951
- return adapterDevId.split('.').slice(0,3).join('.');
960
+ return adapterDevId.split('.').slice(0, 3).join('.');
952
961
  }
953
962
 
954
963
  // subscribe to changes
@@ -975,7 +984,7 @@ socket.on('stateChange', function (id, state) {
975
984
  } else {
976
985
  $('#pairing').addClass('pulse');
977
986
  $('#pairing').html(state.val);
978
- const percent = 100-100*state.val/($('#countDown').val() || 60);
987
+ const percent = 100 - 100 * state.val / ($('#countDown').val() || 60);
979
988
  $('#progress_line').css('width', `${percent}%`);
980
989
  }
981
990
  } else if (id.match(/\.info\.pairingMessage$/)) {
@@ -1021,6 +1030,7 @@ socket.on('objectChange', function (id, obj) {
1021
1030
  }
1022
1031
  }
1023
1032
  });
1033
+
1024
1034
  /*
1025
1035
  socket.emit('getObject', 'system.config', function (err, res) {
1026
1036
  if (!err && res && res.common) {
@@ -1033,45 +1043,47 @@ socket.emit('getObject', 'system.config', function (err, res) {
1033
1043
  function putEventToNode(devId) {
1034
1044
  if (network) {
1035
1045
  const nodesArray = Object.values(network.body.data.nodes._data);
1036
- const node = nodesArray.find((node) => { return node.id == devId; });
1046
+ const node = nodesArray.find((node) => {
1047
+ return node.id == devId;
1048
+ });
1037
1049
  if (node) {
1038
1050
  const exists = networkEvents.find((event) => {
1039
1051
  return event.node == node.id;
1040
1052
  });
1041
1053
  if (!exists) {
1042
1054
  networkEvents.push({node: node.id, radius: 0, forward: true});
1043
- // } else {
1044
- // exists.radius = 0;
1045
- // exists.forward = true;
1055
+ // } else {
1056
+ // exists.radius = 0;
1057
+ // exists.forward = true;
1046
1058
  }
1047
1059
  }
1048
1060
  }
1049
1061
  }
1050
1062
 
1051
- function showNetworkMap(devices, map){
1063
+ function showNetworkMap(devices, map) {
1052
1064
  // create an object with nodes
1053
1065
  const nodes = {};
1054
1066
  // create an array with edges
1055
1067
  const edges = [];
1056
1068
 
1057
1069
  if (map.lqis == undefined || map.lqis.length === 0) { // first init
1058
- $('#filterParent, #filterSibl, #filterPrvChild, #filterMesh').change(function() {
1070
+ $('#filterParent, #filterSibl, #filterPrvChild, #filterMesh').change(function () {
1059
1071
  updateMapFilter();
1060
1072
  });
1061
1073
  }
1062
1074
 
1063
- const createNode = function(dev, mapEntry) {
1075
+ const createNode = function (dev, mapEntry) {
1064
1076
  if (dev.common && dev.common.type == 'group') return undefined;
1065
1077
  const extInfo = (mapEntry && mapEntry.networkAddress) ? `\n (nwkAddr: 0x${mapEntry.networkAddress.toString(16)} | ${mapEntry.networkAddress})` : '';
1066
1078
  const node = {
1067
1079
  id: dev._id,
1068
- label: (dev.link_quality >0 ? dev.common.name:`${dev.common.name}\n(disconnected)`) ,
1069
- title: dev._id.replace(namespace+'.', '') + extInfo,
1080
+ label: (dev.link_quality > 0 ? dev.common.name : `${dev.common.name}\n(disconnected)`),
1081
+ title: dev._id.replace(namespace + '.', '') + extInfo,
1070
1082
  shape: 'circularImage',
1071
1083
  image: dev.icon,
1072
1084
  imagePadding: {top: 5, bottom: 5, left: 5, right: 5},
1073
1085
  color: {background: 'white', highlight: {background: 'white'}},
1074
- font: {color:'#007700'},
1086
+ font: {color: '#007700'},
1075
1087
  borderWidth: 1,
1076
1088
  borderWidthSelected: 4,
1077
1089
  };
@@ -1085,7 +1097,7 @@ function showNetworkMap(devices, map){
1085
1097
  };
1086
1098
 
1087
1099
  if (map.lqis) {
1088
- map.lqis.forEach((mapEntry)=>{
1100
+ map.lqis.forEach((mapEntry) => {
1089
1101
  const dev = getDevice(mapEntry.ieeeAddr);
1090
1102
  if (!dev) {
1091
1103
  //console.log("No dev with ieee "+mapEntry.ieeeAddr);
@@ -1098,8 +1110,7 @@ function showNetworkMap(devices, map){
1098
1110
  if (node) {
1099
1111
  nodes[mapEntry.ieeeAddr] = node;
1100
1112
  }
1101
- }
1102
- else {
1113
+ } else {
1103
1114
  node = nodes[mapEntry.ieeeAddr];
1104
1115
  }
1105
1116
  if (node) {
@@ -1117,7 +1128,7 @@ function showNetworkMap(devices, map){
1117
1128
 
1118
1129
  if (mapEntry.relationship === 0 || mapEntry.relationship === 1) { // 0 - parent, 1 - child
1119
1130
  // // parent/child
1120
- if (mapEntry.status !== 'online' ) {
1131
+ if (mapEntry.status !== 'online') {
1121
1132
  label = label + ' (off)';
1122
1133
  linkColor = '#ff0000';
1123
1134
  }
@@ -1134,8 +1145,8 @@ function showNetworkMap(devices, map){
1134
1145
  if (reverse) {
1135
1146
  // update reverse edge
1136
1147
  edge = reverse;
1137
- edge.label += '\n'+label;
1138
- edge.arrows.from = { enabled: false, scaleFactor: 0.5 }; // start hidden if node is not selected
1148
+ edge.label += '\n' + label;
1149
+ edge.arrows.from = {enabled: false, scaleFactor: 0.5}; // start hidden if node is not selected
1139
1150
  if (mapEntry.relationship == 1) { //
1140
1151
  edge.color.color = linkColor;
1141
1152
  edge.color.highlight = linkColor;
@@ -1150,7 +1161,7 @@ function showNetworkMap(devices, map){
1150
1161
  size: 0, // start hidden
1151
1162
  color: linkColor
1152
1163
  },
1153
- arrows: { to: { enabled: false, scaleFactor: 0.5 }},
1164
+ arrows: {to: {enabled: false, scaleFactor: 0.5}},
1154
1165
  //arrowStrikethrough: false,
1155
1166
  color: {
1156
1167
  color: linkColor,
@@ -1164,7 +1175,7 @@ function showNetworkMap(devices, map){
1164
1175
  values.fromArrow = values.fromArrowScale != 1 ? true : false; // simplified, arrow existing if scale is not default value
1165
1176
  },
1166
1177
  label: () => {
1167
- // see onMapSelect workaround
1178
+ // see onMapSelect workaround
1168
1179
  // values.size = 10;
1169
1180
  }
1170
1181
  },
@@ -1230,15 +1241,16 @@ function showNetworkMap(devices, map){
1230
1241
  const nodesArray = Object.values(nodes);
1231
1242
  // add devices without network links to map
1232
1243
  devices.forEach((dev) => {
1233
- const node = nodesArray.find((node) => { return node.id == dev._id; });
1244
+ const node = nodesArray.find((node) => {
1245
+ return node.id == dev._id;
1246
+ });
1234
1247
  if (!node) {
1235
1248
  const node = createNode(dev);
1236
1249
 
1237
- if (node)
1238
- {
1239
- node.font = {color:'#ff0000'};
1250
+ if (node) {
1251
+ node.font = {color: '#ff0000'};
1240
1252
  if (dev.info && dev.info.device._type == 'Coordinator') {
1241
- node.font = {color:'#000000'};
1253
+ node.font = {color: '#000000'};
1242
1254
  }
1243
1255
  nodesArray.push(node);
1244
1256
  }
@@ -1260,7 +1272,7 @@ function showNetworkMap(devices, map){
1260
1272
  shape: 'box'
1261
1273
  },
1262
1274
  layout: {
1263
- improvedLayout:true,
1275
+ improvedLayout: true,
1264
1276
  }
1265
1277
  };
1266
1278
 
@@ -1310,25 +1322,26 @@ function showNetworkMap(devices, map){
1310
1322
  if (networkEvents.length > 0) {
1311
1323
  network.redraw();
1312
1324
  const toDelete = [];
1313
- networkEvents.forEach((event, index)=>{
1325
+ networkEvents.forEach((event, index) => {
1314
1326
  if (event.radius >= 1) {
1315
1327
  toDelete.push(index);
1316
1328
  } else {
1317
1329
  event.radius += 0.08;
1318
1330
  }
1319
1331
  });
1320
- toDelete.forEach((index)=>{
1332
+ toDelete.forEach((index) => {
1321
1333
  networkEvents.splice(index, 1);
1322
1334
  });
1323
1335
  }
1324
1336
  }
1325
- network.on('beforeDrawing', function(ctx) {
1337
+
1338
+ network.on('beforeDrawing', function (ctx) {
1326
1339
  if (networkEvents.length > 0) {
1327
- networkEvents.forEach((event)=>{
1340
+ networkEvents.forEach((event) => {
1328
1341
  const inode = event.node;
1329
1342
  const nodePosition = network.getPositions();
1330
1343
  event.radius = (event.radius > 1) ? 1 : event.radius;
1331
- const cap = Math.cos(event.radius*Math.PI/2);
1344
+ const cap = Math.cos(event.radius * Math.PI / 2);
1332
1345
  const colorCircle = `rgba(0, 255, 255, ${cap.toFixed(2)})`;
1333
1346
  const colorBorder = `rgba(0, 255, 255, ${cap.toFixed(2)})`;
1334
1347
  ctx.strokeStyle = colorCircle;
@@ -1345,11 +1358,11 @@ function showNetworkMap(devices, map){
1345
1358
  function redrawMap() {
1346
1359
  if (network != undefined && devices.length > 0) {
1347
1360
  const width = ($('.adapter-body').width() || $('#main').width()) - 20,
1348
- height = ($('.adapter-body').height() || ($('#main').height())) -120;
1361
+ height = ($('.adapter-body').height() || ($('#main').height())) - 120;
1349
1362
  network.setSize(width, height);
1350
1363
  network.redraw();
1351
1364
  network.fit();
1352
- network.moveTo({offset:{x:0.5 * width, y:0.5 * height}});
1365
+ network.moveTo({offset: {x: 0.5 * width, y: 0.5 * height}});
1353
1366
  }
1354
1367
  }
1355
1368
 
@@ -1363,9 +1376,9 @@ function updateMapFilter() {
1363
1376
  const invisColor = $('#filterMesh').is(':checked') ? 0.2 : 0;
1364
1377
  mapEdges.forEach((edge) => {
1365
1378
  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)) {
1379
+ || (edge.relationship === 2 && showSibl)
1380
+ || (edge.relationship === 3 && showParent) // ignore relationship "unknown"
1381
+ || (edge.relationship === 4 && showPrvChild)) {
1369
1382
  edge.color.opacity = 1.0;
1370
1383
  } else {
1371
1384
  edge.color.opacity = invisColor;
@@ -1389,13 +1402,15 @@ function getComPorts(onChange) {
1389
1402
  // }, 1000);
1390
1403
  // return;
1391
1404
  // }
1392
- if (!list) return;
1405
+ if (!list) {
1406
+ return;
1407
+ }
1393
1408
  const element = $('#ports');
1394
1409
  for (let j = 0; j < list.length; j++) {
1395
- element.append('<li><a href="#!">' + list[j].comName +'</a></li>');
1410
+ element.append('<li><a href="#!" data-value="' + list[j].comName + '">' + list[j].comName + (list[j].label ? (' [' + list[j].label + ']') : '') + '</a></li>');
1396
1411
  }
1397
- $('#ports a').click(function() {
1398
- $('#port').val($(this).text());
1412
+ $('#ports a').click(function () {
1413
+ $('#port').val($(this).data('value'));
1399
1414
  Materialize.updateTextFields();
1400
1415
  onChange();
1401
1416
  });
@@ -1405,29 +1420,33 @@ function getComPorts(onChange) {
1405
1420
  function loadDeveloperTab() {
1406
1421
  // fill device selector
1407
1422
  updateSelect('#dev', devices,
1408
- function(key, device) {
1423
+ function (key, device) {
1409
1424
  if (device.hasOwnProperty('info')) {
1410
- if (device.info.device._type == 'Coordinator') {
1425
+ if (device.info.device._type === 'Coordinator') {
1411
1426
  return null;
1412
1427
  }
1413
1428
  return `${device.common.name} (${device.info.name})`;
1414
1429
  } else { // fallback if device in list but not paired
1415
- device.common.name + ' ' +device.native.id;
1430
+ return device.common.name + ' ' + device.native.id;
1416
1431
  }
1417
1432
  },
1418
- function(key, device) {
1433
+ function (key, device) {
1419
1434
  return device._id;
1420
1435
  });
1421
1436
  // add groups to device selector
1422
1437
  const groupList = [];
1423
1438
  for (const key in groups) {
1424
- groupList.push({'_id': namespace+'.'+key.toString(16).padStart(16, '0'), 'groupId': key, 'groupName': groups[key]});
1439
+ groupList.push({
1440
+ _id: namespace + '.' + key.toString(16).padStart(16, '0'),
1441
+ groupId: key,
1442
+ groupName: groups[key]
1443
+ });
1425
1444
  }
1426
1445
  updateSelect('#dev', groupList,
1427
- function(key, device) {
1428
- return 'Group '+device.groupId+': '+device.groupName;
1446
+ function (key, device) {
1447
+ return 'Group ' + device.groupId + ': ' + device.groupName;
1429
1448
  },
1430
- function(key, device) {
1449
+ function (key, device) {
1431
1450
  return device._id;
1432
1451
  }, true);
1433
1452
 
@@ -1437,10 +1456,10 @@ function loadDeveloperTab() {
1437
1456
  populateSelector('#type', 'typeList', this.value);
1438
1457
 
1439
1458
  if (responseCodes == false) {
1440
- const getValue = function() { // convert to number if needed
1459
+ const getValue = function () { // convert to number if needed
1441
1460
  let attrData = $('#value-input').val();
1442
1461
  if (attrData.startsWith('"') && attrData.endsWith('"')) {
1443
- attrData = attrData.substr(1, attrData.length -2);
1462
+ attrData = attrData.substr(1, attrData.length - 2);
1444
1463
  } else {
1445
1464
  const numValue = Number(attrData);
1446
1465
  attrData = !isNaN(numValue) ? numValue : attrData;
@@ -1465,35 +1484,38 @@ function loadDeveloperTab() {
1465
1484
  return data;
1466
1485
  };
1467
1486
 
1468
- const prepareExpertData = function() {
1487
+ const prepareExpertData = function () {
1469
1488
  try {
1470
1489
  return JSON.parse($('#expert-json').val());
1471
1490
  } catch (exception) {
1472
1491
  showDevRunInfo('JSON error', exception, 'yellow');
1473
1492
  }
1474
1493
  };
1475
- const setExpertData = function(prop, value, removeIfEmpty = true) {
1494
+ const setExpertData = function (prop, value, removeIfEmpty = true) {
1476
1495
  if (!$('#expert-mode').is(':checked')) {
1477
1496
  return;
1478
1497
  }
1479
- if (!removeIfEmpty && value == null) { value = ''; }
1498
+ if (!removeIfEmpty && value == null) {
1499
+ value = '';
1500
+ }
1480
1501
  let data;
1481
1502
  if (prop) {
1482
1503
  data = prepareExpertData();
1483
1504
  // https://stackoverflow.com/a/6394168/6937282
1484
- const assignVal = function index(obj,is, value) {
1505
+ const assignVal = function index(obj, is, value) {
1485
1506
  if (typeof is == 'string') {
1486
- return index(obj,is.split('.'), value);
1487
- } else if (is.length==1 && value!==undefined) {
1507
+ return index(obj, is.split('.'), value);
1508
+ } else if (is.length === 1 && value !== undefined) {
1488
1509
  if (value == null) {
1489
1510
  return delete obj[is[0]];
1490
1511
  } else {
1491
1512
  return obj[is[0]] = value;
1492
1513
  }
1493
- } else if (is.length==0) {
1514
+ } else if (!is.length) {
1494
1515
  return obj;
1495
- } else
1496
- return index(obj[is[0]],is.slice(1), value);
1516
+ } else {
1517
+ return index(obj[is[0]], is.slice(1), value);
1518
+ }
1497
1519
  };
1498
1520
  assignVal(data, prop, value);
1499
1521
  } else {
@@ -1503,7 +1525,7 @@ function loadDeveloperTab() {
1503
1525
  };
1504
1526
 
1505
1527
  // init event listener only at first load
1506
- $('#dev-selector').change(function() {
1528
+ $('#dev-selector').change(function () {
1507
1529
  if (this.selectedIndex <= 0) {
1508
1530
  return;
1509
1531
  }
@@ -1514,48 +1536,48 @@ function loadDeveloperTab() {
1514
1536
 
1515
1537
  const epList = device ? device.info.device._endpoints : null;
1516
1538
  updateSelect('#ep', epList,
1517
- function(key, ep) {
1539
+ function (key, ep) {
1518
1540
  return ep.ID;
1519
1541
  },
1520
- function(key, ep) {
1542
+ function (key, ep) {
1521
1543
  return ep.ID;
1522
1544
  });
1523
1545
  setExpertData('devId', this.value);
1524
1546
  setExpertData('ep', $('#ep-selector').val(), false);
1525
1547
  });
1526
1548
 
1527
- $('#ep-selector').change(function() {
1549
+ $('#ep-selector').change(function () {
1528
1550
  setExpertData('ep', this.value);
1529
1551
  });
1530
1552
 
1531
- $('#cid-selector').change(function() {
1553
+ $('#cid-selector').change(function () {
1532
1554
  populateSelector('#attrid', 'attrIdList', this.value);
1533
- if ($('#cmd-type-selector').val() == 'functional') {
1555
+ if ($('#cmd-type-selector').val() === 'functional') {
1534
1556
  const cid = $('#cid-selector option:selected').val();
1535
1557
  populateSelector('#cmd', 'cmdListFunctional', cid);
1536
1558
  }
1537
1559
  setExpertData('cid', this.value);
1538
1560
  });
1539
1561
 
1540
- $('#cmd-type-selector').change(function() {
1541
- if (this.value == 'foundation') {
1562
+ $('#cmd-type-selector').change(function () {
1563
+ if (this.value === 'foundation') {
1542
1564
  populateSelector('#cmd', 'cmdListFoundation');
1543
- } else if (this.value == 'functional') {
1565
+ } else if (this.value === 'functional') {
1544
1566
  const cid = $('#cid-selector option:selected').val();
1545
1567
  populateSelector('#cmd', 'cmdListFunctional', cid);
1546
1568
  }
1547
1569
  setExpertData('cmdType', this.value);
1548
1570
  });
1549
1571
 
1550
- $('#cmd-selector').change(function() {
1572
+ $('#cmd-selector').change(function () {
1551
1573
  setExpertData('cmd', this.value);
1552
1574
  });
1553
- $('#attrid-selector').change(function() {
1554
- setExpertData('zclData', {[this.value]:{}});
1575
+ $('#attrid-selector').change(function () {
1576
+ setExpertData('zclData', {[this.value]: {}});
1555
1577
  });
1556
1578
 
1557
1579
  // value selector checkbox
1558
- $('#value-needed').change(function() {
1580
+ $('#value-needed').change(function () {
1559
1581
  const attr = $('#attrid-selector').val();
1560
1582
  let attrData = null;
1561
1583
  if (this.checked === true) {
@@ -1564,17 +1586,17 @@ function loadDeveloperTab() {
1564
1586
  } else {
1565
1587
  $('#value-input').attr('disabled', 'disabled');
1566
1588
  }
1567
- setExpertData('zclData.'+attr, attrData);
1589
+ setExpertData('zclData.' + attr, attrData);
1568
1590
  $('#type-selector').select();
1569
1591
  Materialize.updateTextFields();
1570
1592
  });
1571
1593
 
1572
- $('#value-input').keyup(function() {
1594
+ $('#value-input').keyup(function () {
1573
1595
  const attr = $('#attrid-selector').val();
1574
- setExpertData('zclData.'+attr, getValue());
1596
+ setExpertData('zclData.' + attr, getValue());
1575
1597
  });
1576
1598
 
1577
- $('#expert-mode').change(function() {
1599
+ $('#expert-mode').change(function () {
1578
1600
  if (this.checked === true) {
1579
1601
  setExpertData();
1580
1602
  $('#expert-json-box').css('display', 'inline-block');
@@ -1585,7 +1607,7 @@ function loadDeveloperTab() {
1585
1607
  Materialize.updateTextFields();
1586
1608
  });
1587
1609
 
1588
- $('#dev-send-btn').click(function() {
1610
+ $('#dev-send-btn').click(function () {
1589
1611
  let data;
1590
1612
  if ($('#expert-mode').is(':checked')) {
1591
1613
  data = prepareExpertData();
@@ -1593,7 +1615,7 @@ function loadDeveloperTab() {
1593
1615
  data = prepareData();
1594
1616
  }
1595
1617
  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));
1618
+ console.log('Reply from zigbee: ' + JSON.stringify(reply));
1597
1619
  if (reply.hasOwnProperty('localErr')) {
1598
1620
  showDevRunInfo(reply.localErr, reply.errMsg, 'yellow');
1599
1621
  } else if (reply.hasOwnProperty('localStatus')) {
@@ -1634,25 +1656,34 @@ function loadDeveloperTab() {
1634
1656
  */
1635
1657
  function sendToZigbee(id, ep, cid, cmd, cmdType, zclData, cfg, callback) {
1636
1658
  if (!id) {
1637
- if (callback) {callback({localErr: 'Incomplete', errMsg: 'Please select Device and Endpoint!'});}
1659
+ if (callback) {
1660
+ callback({localErr: 'Incomplete', errMsg: 'Please select Device and Endpoint!'});
1661
+ }
1638
1662
  return;
1639
1663
  }
1640
1664
  if (!cid || !cmd || !cmdType) {
1641
- if (callback) {callback({localErr: 'Incomplete', errMsg: 'Please choose ClusterId, Command, CommandType and AttributeId!'});}
1665
+ if (callback) {
1666
+ callback({
1667
+ localErr: 'Incomplete',
1668
+ errMsg: 'Please choose ClusterId, Command, CommandType and AttributeId!'
1669
+ });
1670
+ }
1642
1671
  return;
1643
1672
  }
1644
1673
  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...'});}
1674
+ if (callback) {
1675
+ callback({localStatus: 'Send', errMsg: 'Waiting for reply...'});
1676
+ }
1646
1677
 
1647
- const sendTimeout = setTimeout(function() {
1678
+ const sendTimeout = setTimeout(function () {
1648
1679
  if (callback) {
1649
1680
  callback({localErr: 'Timeout', errMsg: 'We did not receive any response.'});
1650
1681
  }
1651
1682
  }, 15000);
1652
1683
 
1653
- console.log('Send to zigbee, id '+id+ ',ep '+ep+', cid '+cid+', cmd '+cmd+', cmdType '+cmdType+', zclData '+JSON.stringify(zclData));
1684
+ console.log('Send to zigbee, id ' + id + ',ep ' + ep + ', cid ' + cid + ', cmd ' + cmd + ', cmdType ' + cmdType + ', zclData ' + JSON.stringify(zclData));
1654
1685
 
1655
- sendTo(namespace, 'sendToZigbee', data, function(reply) {
1686
+ sendTo(namespace, 'sendToZigbee', data, function (reply) {
1656
1687
  clearTimeout(sendTimeout);
1657
1688
  if (callback) {
1658
1689
  callback(reply);
@@ -1665,11 +1696,10 @@ function sendToZigbee(id, ep, cid, cmd, cmdType, zclData, cfg, callback) {
1665
1696
  */
1666
1697
  function showDevRunInfo(result, text, level) {
1667
1698
  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' );
1699
+ if (level === 'yellow') {
1700
+ card.removeClass('white-text').addClass('yellow-text');
1701
+ } else {
1702
+ card.removeClass('yellow-text').addClass('white-text');
1673
1703
  }
1674
1704
  $('#devActResult').text(result);
1675
1705
  $('#devInfoMsg').text(text);
@@ -1677,13 +1707,13 @@ function showDevRunInfo(result, text, level) {
1677
1707
 
1678
1708
  function addDevLog(reply) {
1679
1709
  const statusCode = reply.statusCode;
1680
- let logHtml = '<span>'+JSON.stringify(reply.msg)+'</span><br>';
1710
+ let logHtml = '<span>' + JSON.stringify(reply.msg) + '</span><br>';
1681
1711
  if (responseCodes != undefined) {
1682
1712
  const status = Object.keys(responseCodes).find(key => responseCodes[key] === statusCode);
1683
1713
  if (statusCode == 0) {
1684
- logHtml = '<span class="green-text">'+status+'</span> '+logHtml;
1714
+ logHtml = '<span class="green-text">' + status + '</span> ' + logHtml;
1685
1715
  } else {
1686
- logHtml = '<span class="yellow-text">'+status+'</span> '+logHtml;
1716
+ logHtml = '<span class="yellow-text">' + status + '</span> ' + logHtml;
1687
1717
  }
1688
1718
  }
1689
1719
  const logView = $('#dev_result_log');
@@ -1695,7 +1725,7 @@ function addDevLog(reply) {
1695
1725
  * Query adapter and update select with result
1696
1726
  */
1697
1727
  function populateSelector(selectId, key, cid) {
1698
- $(selectId+'>option:enabled').remove(); // remove existing elements
1728
+ $(selectId + '>option:enabled').remove(); // remove existing elements
1699
1729
  $(selectId).select();
1700
1730
  if (cid == '-2') {
1701
1731
  updateSelect(selectId, null);
@@ -1706,7 +1736,7 @@ function populateSelector(selectId, key, cid) {
1706
1736
  if (key === 'attrIdList') {
1707
1737
  updateSelect(selectId, list,
1708
1738
  (attrName, attr) => {
1709
- return attrName + ' ('+attr.ID +', type '+attr.type+')';
1739
+ return attrName + ' (' + attr.ID + ', type ' + attr.type + ')';
1710
1740
  },
1711
1741
  (attrName) => {
1712
1742
  return attrName;
@@ -1714,7 +1744,7 @@ function populateSelector(selectId, key, cid) {
1714
1744
  } else if (key === 'typeList') {
1715
1745
  updateSelect(selectId, list,
1716
1746
  (name, val) => {
1717
- return name +' ('+val+')';
1747
+ return name + ' (' + val + ')';
1718
1748
  },
1719
1749
  (name, val) => {
1720
1750
  return val;
@@ -1722,7 +1752,7 @@ function populateSelector(selectId, key, cid) {
1722
1752
  } else {
1723
1753
  updateSelect(selectId, list,
1724
1754
  (propName, propInfo) => {
1725
- return propName +' ('+propInfo.ID+')';
1755
+ return propName + ' (' + propInfo.ID + ')';
1726
1756
  },
1727
1757
  (propName) => {
1728
1758
  return propName;
@@ -1732,32 +1762,31 @@ function populateSelector(selectId, key, cid) {
1732
1762
  }
1733
1763
 
1734
1764
  function updateSelect(id, list, getText, getId, append = false) {
1735
- const selectId = id+'-selector';
1765
+ const selectId = id + '-selector';
1736
1766
  const mySelect = $(selectId);
1737
1767
  if (!append) {
1738
- $(selectId+'>:not(:first[disabled])').remove(); // remove existing elements, except first if disabled, (is 'Select...' info)
1768
+ $(selectId + '>:not(:first[disabled])').remove(); // remove existing elements, except first if disabled, (is 'Select...' info)
1739
1769
  mySelect.select();
1740
1770
  }
1741
1771
  if (list == null && !append) {
1742
1772
  const infoOption = new Option('Nothing available');
1743
1773
  infoOption.disabled = true;
1744
- mySelect.append( infoOption);
1745
- }
1746
- else {
1774
+ mySelect.append(infoOption);
1775
+ } else {
1747
1776
  const keys = Object.keys(list); // is index in case of array
1748
- for (let i=0; i<keys.length; i++) {
1777
+ for (let i = 0; i < keys.length; i++) {
1749
1778
  const key = keys[i];
1750
1779
  const item = list[key];
1751
1780
  const optionText = getText(key, item);
1752
1781
  if (optionText == null) {
1753
1782
  continue;
1754
1783
  }
1755
- mySelect.append( new Option(optionText, getId(key, item)));
1784
+ mySelect.append(new Option(optionText, getId(key, item)));
1756
1785
  }
1757
1786
  }
1758
1787
 
1759
- if ($(id+'-c-input').length > 0) {
1760
- mySelect.append( new Option('CUSTOM', -2));
1788
+ if ($(id + '-c-input').length > 0) {
1789
+ mySelect.append(new Option('CUSTOM', -2));
1761
1790
  }
1762
1791
  // update select element (Materialize)
1763
1792
  mySelect.select();
@@ -1786,17 +1815,17 @@ function showGroups() {
1786
1815
  const element = $('#groups_table');
1787
1816
  for (const j in groups) {
1788
1817
  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>`);
1818
+ element.append(`<tr id="group_${j}" class="group"><td>${j}</td><td><div>${groups[j]}<span class="right">` +
1819
+ `<a id="${j}" name="groupedit" class="waves-effect green btn-floating"><i class="material-icons">edit</i></a>` +
1820
+ `<a id="${j}" name="groupdelete" class="waves-effect red btn-floating"><i class="material-icons">delete</i></a></span></div></td></tr>`);
1792
1821
  }
1793
1822
  }
1794
- $("a.btn-floating[name='groupedit']").click(function() {
1823
+ $('a.btn-floating[name=\'groupedit\']').click(function () {
1795
1824
  const index = $(this).attr('id'),
1796
1825
  name = groups[index];
1797
1826
  editGroupName(index, name, false);
1798
1827
  });
1799
- $("a.btn-floating[name='groupdelete']").click(function() {
1828
+ $('a.btn-floating[name=\'groupdelete\']').click(function () {
1800
1829
  const index = $(this).attr('id'),
1801
1830
  name = groups[index];
1802
1831
  deleteGroupConfirmation(index, name);
@@ -1808,53 +1837,51 @@ function editGroupName(id, name, isnew) {
1808
1837
  //console.log('devices: '+ JSON.stringify(devices));
1809
1838
  const groupables = [];
1810
1839
  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
- }
1840
+ if (d && d.info && d.info.endpoints) {
1841
+ for (const ep of d.info.endpoints) {
1842
+ if (ep.inputClusters.includes(4)) {
1843
+ groupables.push(ep);
1844
+ }
1845
+ }
1817
1846
  }
1818
- }
1819
- //console.log('device ' + JSON.stringify(d));
1847
+ //console.log('device ' + JSON.stringify(d));
1820
1848
  }
1821
1849
 
1822
1850
  //var text = 'Enter new name for "'+name+'" ('+id+')?';
1823
1851
  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();
1852
+ $('#groupedit').find('.editgroup').addClass('hide');
1853
+ $('#groupedit').find('.addgroup').removeClass('hide');
1854
+ $('#groupedit').find('.input-field.members').addClass('hide');
1855
+ $('#groupedit').find('.input-field.groupid').removeClass('hide');
1856
+ } else {
1857
+ $('#groupedit').find('.editgroup').removeClass('hide');
1858
+ $('#groupedit').find('.addgroup').addClass('hide');
1859
+ $('#groupedit').find('.input-field.members').removeClass('hide');
1860
+ $('#groupedit').find('.input-field.groupid').addClass('hide');
1861
+ }
1862
+ $('#groupedit').find('input[id=\'g_index\']').val(id);
1863
+ $('#groupedit').find('input[id=\'g_name\']').val(name);
1864
+ $('#groupedit a.btn[name=\'save\']').unbind('click');
1865
+ $('#groupedit a.btn[name=\'save\']').click(() => {
1866
+ const newId = $('#groupedit').find('input[id=\'g_index\']').val(),
1867
+ newName = $('#groupedit').find('input[id=\'g_name\']').val();
1841
1868
  updateGroup(id, newId, newName);
1842
- // showGroups();
1843
- // getDevices();
1869
+ // showGroups();
1870
+ // getDevices();
1844
1871
  });
1845
1872
  $('#groupedit').modal('open');
1846
1873
  Materialize.updateTextFields();
1847
1874
  }
1848
1875
 
1849
1876
  function deleteGroupConfirmation(id, name) {
1850
- const text = translateWord('Do you really whant to delete group') + ' "'+name+'" ('+id+')?';
1877
+ const text = translateWord('Do you really whant to delete group') + ' "' + name + '" (' + id + ')?';
1851
1878
  $('#modaldelete').find('p').text(text);
1852
1879
  $('#forcediv').addClass('hide');
1853
- $("#modaldelete a.btn[name='yes']").unbind('click');
1854
- $("#modaldelete a.btn[name='yes']").click(() => {
1880
+ $('#modaldelete a.btn[name=\'yes\']').unbind('click');
1881
+ $('#modaldelete a.btn[name=\'yes\']').click(() => {
1855
1882
  deleteGroup(id);
1856
- // showGroups();
1857
- // getDevices();
1883
+ // showGroups();
1884
+ // getDevices();
1858
1885
  });
1859
1886
  $('#modaldelete').modal('open');
1860
1887
  }
@@ -1862,8 +1889,8 @@ function deleteGroupConfirmation(id, name) {
1862
1889
  function updateGroup(id, newId, newName) {
1863
1890
  delete groups[id];
1864
1891
  groups[newId] = newName;
1865
- sendTo(namespace, 'renameGroup', { id: newId, name: newName}, function(msg) {
1866
- if (msg && ms.error) {
1892
+ sendTo(namespace, 'renameGroup', {id: newId, name: newName}, function (msg) {
1893
+ if (msg && msg.error) {
1867
1894
  showMessage(msg.error, _('Error'));
1868
1895
  }
1869
1896
  getDevices();
@@ -1872,8 +1899,8 @@ function updateGroup(id, newId, newName) {
1872
1899
 
1873
1900
  function deleteGroup(id) {
1874
1901
  delete groups[id];
1875
- sendTo(namespace, 'deleteGroup', id , function(msg) {
1876
- if (msg && ms.error) {
1902
+ sendTo(namespace, 'deleteGroup', id, function (msg) {
1903
+ if (msg && msg.error) {
1877
1904
  showMessage(msg.error, _('Error'));
1878
1905
  }
1879
1906
  getDevices();
@@ -1881,58 +1908,55 @@ function deleteGroup(id) {
1881
1908
  }
1882
1909
 
1883
1910
  function updateDev(id, newName, newGroups) {
1884
- const dev = devices.find((d) => d._id == id);
1885
- if (dev && dev.common.name != newName) {
1911
+ const dev = devices.find((d) => d._id === id);
1912
+ if (dev && dev.common.name !== newName) {
1886
1913
  renameDevice(id, newName);
1887
1914
  }
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);
1915
+ const keys = Object.keys(newGroups);
1916
+ if (keys && keys.length) {
1917
+ sendTo(namespace, 'updateGroupMembership', {id: id, groups: newGroups}, function (msg) {
1918
+ closeWaitingDialog();
1919
+ if (msg && msg.error) {
1920
+ showMessage(msg.error, _('Error'));
1921
+ } else {
1922
+ // save dev-groups on success
1923
+ dev.groups = newGroups;
1924
+ }
1925
+ showDevices();
1926
+ });
1927
+ showWaitingDialog('Updating group memberships', 10);
1903
1928
 
1904
1929
  }
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
- });
1930
+ /*
1931
+ if (dev.info.device._type == 'Router') {
1932
+ const oldGroups = devGroups[id] || [];
1933
+ if (oldGroups.toString() != newGroups.toString()) {
1934
+ devGroups[id] = newGroups;
1935
+ sendTo(namespace, 'updateGroupMembership', { id: id, groups: newGroups }, function (msg) {
1936
+ if (msg && msg.error) {
1937
+ showMessage(msg.error, _('Error'));
1938
+ }
1939
+ else {
1940
+ // save dev-groups on success
1941
+ dev.groups = newGroups;
1942
+ }
1943
+ showDevices();
1944
+ });
1945
+ }
1920
1946
  }
1921
- }
1922
- */
1947
+ */
1923
1948
  }
1924
1949
 
1925
1950
  function resetConfirmation() {
1926
1951
  $('#modalreset').modal('open');
1927
1952
  const btn = $('#modalreset .modal-content a.btn');
1928
1953
  btn.unbind('click');
1929
- btn.click(function(e) {
1954
+ btn.click(function (e) {
1930
1955
  sendTo(namespace, 'reset', {mode: e.target.id}, function (err) {
1931
1956
  if (err) {
1932
1957
  console.log(err);
1933
- }
1934
- else {
1935
- console.log('Reseted');
1958
+ } else {
1959
+ console.log('Reset done');
1936
1960
  }
1937
1961
  });
1938
1962
  });
@@ -1942,7 +1966,7 @@ function showViewConfig() {
1942
1966
  $('#modalviewconfig').modal('open');
1943
1967
  }
1944
1968
 
1945
- function prepareBindingDialog(bindObj){
1969
+ function prepareBindingDialog(bindObj) {
1946
1970
  const binddevices = devices.slice();
1947
1971
  binddevices.unshift('');
1948
1972
  const bind_source = (bindObj) ? [bindObj.bind_source] : [''];
@@ -1953,12 +1977,12 @@ function prepareBindingDialog(bindObj){
1953
1977
  const allowClustersName = {5: 'genScenes', 6: 'genOnOff', 8: 'genLevelCtrl', 768: 'lightingColorCtrl'};
1954
1978
  // fill device selector
1955
1979
  list2select('#bind_source', binddevices, bind_source,
1956
- function(key, device) {
1980
+ function (key, device) {
1957
1981
  if (device == '') {
1958
1982
  return 'Select source device';
1959
1983
  }
1960
1984
  if (device.hasOwnProperty('info')) {
1961
- if (device.info.device._type == 'Coordinator') {
1985
+ if (device.info.device._type === 'Coordinator') {
1962
1986
  return null;
1963
1987
  }
1964
1988
  // check for output clusters
@@ -1978,19 +2002,18 @@ function prepareBindingDialog(bindObj){
1978
2002
  return null;
1979
2003
  }
1980
2004
  return device.common.name;
1981
- }
1982
- else { // fallback if device in list but not paired
1983
- device.common.name + ' ' +device.native.id;
2005
+ } else { // fallback if device in list but not paired
2006
+ return device.common.name + ' ' + device.native.id;
1984
2007
  }
1985
2008
  },
1986
- function(key, device) {
2009
+ function (key, device) {
1987
2010
  if (device == '') {
1988
2011
  return '';
1989
2012
  } else {
1990
2013
  return device._id;
1991
2014
  }
1992
2015
  },
1993
- function(key, device) {
2016
+ function (key, device) {
1994
2017
  if (device == '') {
1995
2018
  return 'disabled';
1996
2019
  } else if (device.icon) {
@@ -2005,12 +2028,12 @@ function prepareBindingDialog(bindObj){
2005
2028
  bindtargets.push({'_id': key, 'groupId': key, 'groupName': groups[key]});
2006
2029
  }
2007
2030
  list2select('#bind_target', bindtargets, bind_target,
2008
- function(key, device) {
2031
+ function (key, device) {
2009
2032
  if (device == '') {
2010
2033
  return 'Select target device';
2011
2034
  }
2012
2035
  if (device.hasOwnProperty('info')) {
2013
- if (device.info.device._type == 'Coordinator') {
2036
+ if (device.info.device._type === 'Coordinator') {
2014
2037
  return null;
2015
2038
  }
2016
2039
  // check for input clusters
@@ -2036,14 +2059,14 @@ function prepareBindingDialog(bindObj){
2036
2059
  }
2037
2060
  }
2038
2061
  },
2039
- function(key, device) {
2062
+ function (key, device) {
2040
2063
  if (device == '') {
2041
2064
  return '';
2042
2065
  } else {
2043
2066
  return device._id;
2044
2067
  }
2045
2068
  },
2046
- function(key, device) {
2069
+ function (key, device) {
2047
2070
  if (device == '') {
2048
2071
  return 'disabled';
2049
2072
  } else if (device.icon) {
@@ -2062,13 +2085,17 @@ function prepareBindingDialog(bindObj){
2062
2085
  const epList = device ? device.info.endpoints : [];
2063
2086
  const sClusterList = epList.map((ep) => {
2064
2087
  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;});
2088
+ return allowClusters.includes(cl) ? {ID: ep.ID + '_' + cl, name: allowClustersName[cl]} : null;
2089
+ }).filter((i) => {
2090
+ return i != null;
2091
+ });
2092
+ return clusters.length == 0 ? null : [{ID: ep.ID, name: 'all'}, clusters];
2093
+ }).flat(2).filter((i) => {
2094
+ return i != null;
2095
+ });
2069
2096
  list2select('#bind_source_ep', sClusterList, (selected) ? [selected] : [],
2070
2097
  (key, ep) => {
2071
- return ep.ID+' '+ep.name;
2098
+ return ep.ID + ' ' + ep.name;
2072
2099
  },
2073
2100
  (key, ep) => {
2074
2101
  return ep.ID;
@@ -2084,13 +2111,20 @@ function prepareBindingDialog(bindObj){
2084
2111
  const epList = device ? device.info.endpoints : [];
2085
2112
  const tClusterList = epList.map((ep) => {
2086
2113
  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;});
2114
+ return (allowClusters.includes(cl) && (!sourceCl || sourceCl == cl)) ? {
2115
+ ID: ep.ID + '_' + cl,
2116
+ name: allowClustersName[cl]
2117
+ } : null;
2118
+ }).filter((i) => {
2119
+ return i != null;
2120
+ });
2121
+ return !clusters.length ? null : [{ID: ep.ID, name: 'all'}, clusters];
2122
+ }).flat(2).filter(i => {
2123
+ return i != null;
2124
+ });
2091
2125
  list2select('#bind_target_ep', tClusterList, (selected) ? [selected] : [],
2092
2126
  (key, ep) => {
2093
- return ep.ID+' '+ep.name;
2127
+ return ep.ID + ' ' + ep.name;
2094
2128
  },
2095
2129
  (key, ep) => {
2096
2130
  return ep.ID;
@@ -2098,7 +2132,7 @@ function prepareBindingDialog(bindObj){
2098
2132
  );
2099
2133
  };
2100
2134
 
2101
- $('#bind_source').change(function() {
2135
+ $('#bind_source').change(function () {
2102
2136
  if (this.selectedIndex <= 0) {
2103
2137
  return;
2104
2138
  }
@@ -2110,7 +2144,7 @@ function prepareBindingDialog(bindObj){
2110
2144
  configureSourceEp();
2111
2145
  }
2112
2146
 
2113
- $('#bind_target').change(function() {
2147
+ $('#bind_target').change(function () {
2114
2148
  if (this.selectedIndex <= 0) {
2115
2149
  return;
2116
2150
  }
@@ -2123,7 +2157,7 @@ function prepareBindingDialog(bindObj){
2123
2157
  configureTargetEp();
2124
2158
  }
2125
2159
 
2126
- $('#bind_source_ep').change(function() {
2160
+ $('#bind_source_ep').change(function () {
2127
2161
  $('#bind_target').trigger('change');
2128
2162
  });
2129
2163
 
@@ -2132,8 +2166,8 @@ function prepareBindingDialog(bindObj){
2132
2166
  }
2133
2167
 
2134
2168
  function addBindingDialog() {
2135
- $("#bindingmodaledit a.btn[name='save']").unbind('click');
2136
- $("#bindingmodaledit a.btn[name='save']").click(() => {
2169
+ $('#bindingmodaledit a.btn[name=\'save\']').unbind('click');
2170
+ $('#bindingmodaledit a.btn[name=\'save\']').click(() => {
2137
2171
  const //bind_id = $('#bindingmodaledit').find("input[id='bind_id']").val(),
2138
2172
  bind_source = $('#bindingmodaledit').find('#bind_source option:selected').val(),
2139
2173
  bind_source_ep = $('#bindingmodaledit').find('#bind_source_ep option:selected').val(),
@@ -2157,10 +2191,8 @@ function addBinding(bind_source, bind_source_ep, bind_target, bind_target_ep, un
2157
2191
  unbind_from_coordinator
2158
2192
  }, function (msg) {
2159
2193
  closeWaitingDialog();
2160
- if (msg) {
2161
- if (msg.error) {
2162
- showMessage(msg.error, _('Error'));
2163
- }
2194
+ if (msg && msg.error) {
2195
+ showMessage(msg.error, _('Error'));
2164
2196
  }
2165
2197
  getBinding();
2166
2198
  });
@@ -2177,10 +2209,8 @@ function editBinding(bind_id, bind_source, bind_source_ep, bind_target, bind_tar
2177
2209
  unbind_from_coordinator
2178
2210
  }, function (msg) {
2179
2211
  closeWaitingDialog();
2180
- if (msg) {
2181
- if (msg.error) {
2182
- showMessage(msg.error, _('Error'));
2183
- }
2212
+ if (msg && msg.error) {
2213
+ showMessage(msg.error, _('Error'));
2184
2214
  }
2185
2215
  getBinding();
2186
2216
  });
@@ -2188,8 +2218,8 @@ function editBinding(bind_id, bind_source, bind_source_ep, bind_target, bind_tar
2188
2218
  }
2189
2219
 
2190
2220
  function editBindingDialog(bindObj) {
2191
- $("#bindingmodaledit a.btn[name='save']").unbind('click');
2192
- $("#bindingmodaledit a.btn[name='save']").click(() => {
2221
+ $('#bindingmodaledit a.btn[name=\'save\']').unbind('click');
2222
+ $('#bindingmodaledit a.btn[name=\'save\']').click(() => {
2193
2223
  const //bind_id = $('#bindingmodaledit').find("input[id='bind_id']").val(),
2194
2224
  bind_source = $('#bindingmodaledit').find('#bind_source option:selected').val(),
2195
2225
  bind_source_ep = $('#bindingmodaledit').find('#bind_source_ep option:selected').val(),
@@ -2225,9 +2255,9 @@ function showBinding() {
2225
2255
  <i class="right">${target_icon}</i>
2226
2256
  <div style="min-height:72px; font-size: 0.8em" class="truncate">
2227
2257
  <ul>
2228
- <li><span class="label">source:</span><span>0x${bind_source.replace(namespace+'.', '')}</span></li>
2258
+ <li><span class="label">source:</span><span>0x${bind_source.replace(namespace + '.', '')}</span></li>
2229
2259
  <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>
2260
+ <li><span class="label">target:</span><span>0x${bind_target.replace(namespace + '.', '')}</span></li>
2231
2261
  <li><span class="label">endpoint:</span><span>${bind_target_ep}</span></li>
2232
2262
  </ul>
2233
2263
  </div>
@@ -2249,11 +2279,11 @@ function showBinding() {
2249
2279
  element.append(card);
2250
2280
  });
2251
2281
 
2252
- $("#binding button[name='delete']").click(function() {
2282
+ $('#binding button[name=\'delete\']').click(function () {
2253
2283
  const bind_id = $(this).parents('.binding')[0].id;
2254
2284
  deleteBindingConfirmation(bind_id);
2255
2285
  });
2256
- $("#binding button[name='edit']").click(function() {
2286
+ $('#binding button[name=\'edit\']').click(function () {
2257
2287
  const bind_id = $(this).parents('.binding')[0].id;
2258
2288
  const bindObj = binding.find((b) => b.id == bind_id);
2259
2289
  if (bindObj) {
@@ -2280,8 +2310,8 @@ function deleteBindingConfirmation(id) {
2280
2310
  $('#modaldelete').find('p').text(text);
2281
2311
  //$('#forcediv').removeClass('hide');
2282
2312
  $('#forcediv').addClass('hide');
2283
- $("#modaldelete a.btn[name='yes']").unbind('click');
2284
- $("#modaldelete a.btn[name='yes']").click(() => {
2313
+ $('#modaldelete a.btn[name=\'yes\']').unbind('click');
2314
+ $('#modaldelete a.btn[name=\'yes\']').click(() => {
2285
2315
  deleteBinding(id);
2286
2316
  });
2287
2317
  $('#modaldelete').modal('open');
@@ -2314,14 +2344,14 @@ function genDevInfo(device) {
2314
2344
  const dev = (device && device.info) ? device.info.device : undefined;
2315
2345
  const mapped = (device && device.info) ? device.info.mapped : undefined;
2316
2346
  if (!dev) return `<div class="truncate">No info</div>`;
2317
- const genRow = function(name, value, refresh) {
2347
+ const genRow = function (name, value, refresh) {
2318
2348
  if (value === undefined) {
2319
2349
  return '';
2320
2350
  } else {
2321
2351
  return `<li><span class="label">${name}:</span><span>${value}</span></li>`;
2322
2352
  }
2323
2353
  };
2324
- const genRowValues = function(name, value) {
2354
+ const genRowValues = function (name, value) {
2325
2355
  if (value === undefined) {
2326
2356
  return '';
2327
2357
  } else {
@@ -2356,7 +2386,7 @@ function genDevInfo(device) {
2356
2386
  </div>`;
2357
2387
  }
2358
2388
  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>`: '';
2389
+ const imgInfo = (imgSrc) ? `<img src=${imgSrc} width='150px' onerror="this.onerror=null;this.src='img/unavailable.png';"><div class="divider"></div>` : '';
2360
2390
  const info =
2361
2391
  `<div class="col s12 m6 l6 xl6">
2362
2392
  ${imgInfo}
@@ -2388,29 +2418,28 @@ function genDevInfo(device) {
2388
2418
  return info;
2389
2419
  }
2390
2420
 
2391
- function showDevInfo(id){
2421
+ function showDevInfo(id) {
2392
2422
  const info = genDevInfo(getDeviceByID(id));
2393
2423
  $('#devinfo').html(info);
2394
2424
  $('#modaldevinfo').modal('open');
2395
2425
  }
2396
2426
 
2397
- function showGroupList(show){
2427
+ function showGroupList(show) {
2398
2428
  const htmlsections = [];
2399
2429
  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(`
2430
+ const dev = devGroups[groupid];
2431
+ const grpname = (dev.common && dev.common.name ? dev.common.name : 'Group ' + groupid);
2432
+ const selectables = [];
2433
+ const members = [];
2434
+ if (dev && dev.memberinfo) {
2435
+ selectables.push(`<select id="members_${groupid}" multiple>`);
2436
+ for (let m = 0; m < dev.memberinfo.length; m++) {
2437
+ members.push(`${dev.memberinfo[m].device}.${dev.memberinfo[m].epid} (${dev.memberinfo[m].ieee})`);
2438
+ selectables.push(`<option value="${m}">${dev.memberinfo[m].device}.${dev.memberinfo[m].epid} (...${dev.memberinfo[m].ieee.slice(-4)})</option>`);
2439
+ }
2440
+ selectables.push('</select>');
2441
+ }
2442
+ htmlsections.push(`
2414
2443
  <div class="row">
2415
2444
  <div class="col s4 m4 l4">
2416
2445
  <h5>${grpname}<h5>
@@ -2423,38 +2452,38 @@ function showGroupList(show){
2423
2452
  }
2424
2453
 
2425
2454
  $('#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);
2455
+ $('#add').click(function () {
2456
+ const maxind = parseInt(Object.getOwnPropertyNames(groups).reduce((a, b) => a > b ? a : b, 0));
2457
+ editGroupName(maxind + 1, 'Group ' + maxind + 1, true);
2429
2458
  showGroupList(false);
2430
2459
  });
2431
2460
 
2432
- $("#modalgrouplist a.btn[name='save']").unbind('click');
2433
- $("#modalgrouplist a.btn[name='save']").click(() => {
2461
+ $('#modalgrouplist a.btn[name=\'save\']').unbind('click');
2462
+ $('#modalgrouplist a.btn[name=\'save\']').click(() => {
2434
2463
  });
2435
2464
  if (show) $('#modalgrouplist').modal('open');
2436
2465
  }
2437
2466
 
2438
2467
  let waitingTimeout, waitingInt;
2439
2468
 
2440
- function showWaitingDialog(text, timeout){
2469
+ function showWaitingDialog(text, timeout) {
2441
2470
  let countDown = timeout;
2442
- waitingInt = setInterval(function() {
2471
+ waitingInt = setInterval(function () {
2443
2472
  countDown -= 1;
2444
- const percent = 100-100*countDown/timeout;
2473
+ const percent = 100 - 100 * countDown / timeout;
2445
2474
  $('#waiting_progress_line').css('width', `${percent}%`);
2446
2475
  }, 1000);
2447
- waitingTimeout = setTimeout(function() {
2476
+ waitingTimeout = setTimeout(function () {
2448
2477
  $('#waiting_progress_line').css('width', `0%`);
2449
2478
  clearTimeout(waitingInt);
2450
2479
  clearTimeout(waitingTimeout);
2451
2480
  $('#modalWaiting').modal('close');
2452
- }, timeout*1000);
2481
+ }, timeout * 1000);
2453
2482
  $('#waiting_message').text(text);
2454
2483
  $('#modalWaiting').modal('open');
2455
2484
  }
2456
2485
 
2457
- function closeWaitingDialog(){
2486
+ function closeWaitingDialog() {
2458
2487
  if (waitingInt) clearTimeout(waitingInt);
2459
2488
  if (waitingTimeout) clearTimeout(waitingTimeout);
2460
2489
  $('#modalWaiting').modal('close');
@@ -2471,7 +2500,7 @@ function showChannels() {
2471
2500
  $('#modalchannels').modal('open');
2472
2501
  let info = '';
2473
2502
  for (let ch = 11; ch < 27; ch++) {
2474
- const value = msg.energyvalues[ch-11];
2503
+ const value = msg.energyvalues[ch - 11];
2475
2504
  info +=
2476
2505
  `<div style="padding-top: 10px">
2477
2506
  <span class="">№ ${ch}: ${value}%</span>
@@ -2529,7 +2558,7 @@ function prepareExcludeDialog(excludeObj) {
2529
2558
 
2530
2559
  list2select('#exclude_target', onlyOneTargets, exclude_target,
2531
2560
 
2532
- function(key, device) {
2561
+ function (key, device) {
2533
2562
  if (device == '') {
2534
2563
  return 'Select model';
2535
2564
  }
@@ -2542,14 +2571,14 @@ function prepareExcludeDialog(excludeObj) {
2542
2571
  return device.common.type;
2543
2572
  }
2544
2573
  },
2545
- function(key, device) {
2574
+ function (key, device) {
2546
2575
  if (device == '') {
2547
2576
  return '';
2548
2577
  } else {
2549
2578
  return device._id;
2550
2579
  }
2551
2580
  },
2552
- function(key, device) {
2581
+ function (key, device) {
2553
2582
  if (device == '') {
2554
2583
  return 'disabled';
2555
2584
  } else if (device.icon) {
@@ -2563,8 +2592,8 @@ function prepareExcludeDialog(excludeObj) {
2563
2592
  }
2564
2593
 
2565
2594
  function addExcludeDialog() {
2566
- $("#excludemodaledit a.btn[name='save']").unbind('click');
2567
- $("#excludemodaledit a.btn[name='save']").click(() => {
2595
+ $('#excludemodaledit a.btn[name=\'save\']').unbind('click');
2596
+ $('#excludemodaledit a.btn[name=\'save\']').click(() => {
2568
2597
  const exclude_id = $('#excludemodaledit').find('#exclude_target option:selected').val();
2569
2598
 
2570
2599
  const ids = devices.map(el => el._id);
@@ -2647,7 +2676,7 @@ function showExclude() {
2647
2676
  element.append(card);
2648
2677
  });
2649
2678
 
2650
- $("#exclude button[name='delete']").click(function() {
2679
+ $('#exclude button[name=\'delete\']').click(function () {
2651
2680
  const exclude_id = $(this).parents('.exclude')[0].id;
2652
2681
  deleteExcludeConfirmation(exclude_id);
2653
2682
  deleteExclude(exclude_id);
@@ -2655,14 +2684,13 @@ function showExclude() {
2655
2684
  }
2656
2685
 
2657
2686
 
2658
-
2659
2687
  function deleteExcludeConfirmation(id) {
2660
2688
  const text = translateWord('Do you really want to delete exclude?');
2661
2689
  $('#modaldelete').find('p').text(text);
2662
2690
  //$('#forcediv').removeClass('hide');
2663
2691
  $('#forcediv').addClass('hide');
2664
- $("#modaldelete a.btn[name='yes']").unbind('click');
2665
- $("#modaldelete a.btn[name='yes']").click(() => {
2692
+ $('#modaldelete a.btn[name=\'yes\']').unbind('click');
2693
+ $('#modaldelete a.btn[name=\'yes\']').click(() => {
2666
2694
  deleteExclude(id);
2667
2695
  });
2668
2696
  $('#modaldelete').modal('open');
@@ -2703,7 +2731,7 @@ function doFilter(inputText) {
2703
2731
  } else {
2704
2732
  return room;
2705
2733
  }
2706
- }).filter((item)=>item != undefined).map((item)=>item.toLowerCase().trim());
2734
+ }).filter((item) => item != undefined).map((item) => item.toLowerCase().trim());
2707
2735
  valid = rooms.includes(roomFilter);
2708
2736
  } else {
2709
2737
  valid = false;
@@ -2720,9 +2748,9 @@ function doFilter(inputText) {
2720
2748
  function doSort() {
2721
2749
  if (shuffleInstance) {
2722
2750
  const sortOrder = $('#device-order-btn').text().toLowerCase();
2723
- if (sortOrder == 'default') {
2751
+ if (sortOrder === 'default') {
2724
2752
  shuffleInstance.sort({});
2725
- } else if (sortOrder == 'a-z') {
2753
+ } else if (sortOrder === 'a-z') {
2726
2754
  shuffleInstance.sort({
2727
2755
  by: sortByTitle
2728
2756
  });
@@ -2736,12 +2764,12 @@ function sortByTitle(element) {
2736
2764
 
2737
2765
  function getDashCard(dev, groupImage) {
2738
2766
  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';
2767
+ id = dev._id,
2768
+ type = dev.common.type,
2769
+ img_src = (groupImage ? groupImage : dev.icon || dev.common.icon),
2770
+ isActive = !dev.common.deactivated,
2771
+ rooms = [],
2772
+ lang = systemLang || 'en';
2745
2773
  const paired = (dev.paired) ? '' : '<i class="material-icons right">leak_remove</i>';
2746
2774
  const rid = id.split('.').join('_');
2747
2775
  const modelUrl = (!type) ? '' : `<a href="https://www.zigbee2mqtt.io/devices/${type}.html" target="_blank" rel="noopener noreferrer">${type}</a>`;
@@ -2750,46 +2778,46 @@ function getDashCard(dev, groupImage) {
2750
2778
  battery_cls = getBatteryCls(dev.battery),
2751
2779
  lqi_cls = getLQICls(dev.link_quality),
2752
2780
  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>' : '',
2781
+ 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>' : ''),
2782
+ 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>`),
2783
+ 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
2784
  infoBtn = (nwk) ? `<button name="info" class="left btn-flat btn-small"><i class="material-icons icon-blue">info</i></button>` : '',
2757
2785
  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)=>{
2786
+ const info = (dev.statesDef) ? dev.statesDef.map((stateDef) => {
2759
2787
  const id = stateDef.id;
2760
2788
  const sid = id.split('.').join('_');
2761
2789
  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') {
2790
+ if (stateDef.role === 'switch' && stateDef.write) {
2791
+ val = `<span class="switch"><label><input type="checkbox" ${(val) ? 'checked' : ''}><span class="lever"></span></label></span>`;
2792
+ } else if (stateDef.role === 'level.dimmer' && stateDef.write) {
2793
+ val = `<span class="range-field dash"><input type="range" min="0" max="100" ${(val != undefined) ? `value="${val}"` : ''} /></span>`;
2794
+ } else if (stateDef.role === 'level.color.temperature' && stateDef.write) {
2795
+ val = `<span class="range-field dash"><input type="range" min="150" max="500" ${(val != undefined) ? `value="${val}"` : ''} /></span>`;
2796
+ } else if (stateDef.type === 'boolean') {
2769
2797
  const disabled = (stateDef.write) ? '' : 'disabled="disabled"';
2770
- val = `<label class="dash"><input type="checkbox" ${(val == true) ? "checked='checked'" : ""} ${disabled}/><span></span></label>`;
2798
+ val = `<label class="dash"><input type="checkbox" ${(val == true) ? 'checked=\'checked\'' : ''} ${disabled}/><span></span></label>`;
2771
2799
  } else if (stateDef.states && stateDef.write) {
2772
2800
  let options;
2773
- if(typeof stateDef.states == "string") {
2801
+ if (typeof stateDef.states == 'string') {
2774
2802
  const sts = stateDef.states.split(';');
2775
2803
  options = sts.map((item) => {
2776
2804
  const v = item.split(':');
2777
- return `<option value="${v[0]}" ${(val == v[0]) ? "selected" : ""}>${v[1]}</option>`;
2805
+ return `<option value="${v[0]}" ${(val == v[0]) ? 'selected' : ''}>${v[1]}</option>`;
2778
2806
  });
2779
2807
  } else {
2780
2808
  options = [];
2781
- for(const [key, value] of Object.entries(stateDef.states)) {
2782
- options.push(`<option value="${key}" ${(val == key) ? "selected" : ""}>${value}</option>`);
2809
+ for (const [key, value] of Object.entries(stateDef.states)) {
2810
+ options.push(`<option value="${key}" ${(val == key) ? 'selected' : ''}>${value}</option>`);
2783
2811
  }
2784
2812
  }
2785
- val = `<select class="browser-default enum" style="height: 16px; padding: 0px; width: auto; display: inline-block">${options.join('')}</select>`;
2813
+ val = `<select class="browser-default enum" style="color : white; background-color: grey; height: 16px; padding: 0; width: auto; display: inline-block">${options.join('')}</select>`;
2786
2814
  } else {
2787
2815
  val = `<span class="dash value">${val} ${(stateDef.unit) ? stateDef.unit : ''}</span>`;
2788
2816
  }
2789
2817
  return `<li><span class="label dash truncate">${stateDef.name}</span><span id=${sid} oid=${id} class="state">${val}</span></li>`;
2790
2818
  }).join('') : '';
2791
2819
  const dashCard = `
2792
- <div class="card-content zcard ${isActive?'':'bg_red'}">
2820
+ <div class="card-content zcard ${isActive ? '' : 'bg_red'}">
2793
2821
  <div class="flip" style="cursor: pointer">
2794
2822
  <span class="top right small" style="border-radius: 50%">
2795
2823
  ${idleTime}
@@ -2802,7 +2830,7 @@ function getDashCard(dev, groupImage) {
2802
2830
  <i class="left">${image}</i>
2803
2831
  <div style="min-height:88px; font-size: 0.8em; height: 130px; overflow-y: auto" class="truncate">
2804
2832
  <ul>
2805
- ${(isActive?info:'Device deactivated')}
2833
+ ${(isActive ? info : 'Device deactivated')}
2806
2834
  </ul>
2807
2835
  </div>
2808
2836
  <div class="footer right-align"></div>
@@ -2815,19 +2843,19 @@ function setDashStates(id, state) {
2815
2843
  const devId = getDevId(id);
2816
2844
  const dev = getDeviceByID(devId);
2817
2845
  if (dev) {
2818
- const stateDef = dev.statesDef.find((stateDef)=> stateDef.id == id);
2846
+ const stateDef = dev.statesDef.find((stateDef) => stateDef.id == id);
2819
2847
  if (stateDef) {
2820
2848
  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);
2849
+ if (stateDef.role === 'switch' && stateDef.write) {
2850
+ $(`#${sid}`).find('input[type=\'checkbox\']').prop('checked', state.val);
2851
+ } else if (stateDef.role === 'level.dimmer' && stateDef.write) {
2852
+ $(`#${sid}`).find('input[type=\'range\']').prop('value', state.val);
2853
+ } else if (stateDef.role === 'level.color.temperature' && stateDef.write) {
2854
+ $(`#${sid}`).find('input[type=\'range\']').prop('value', state.val);
2827
2855
  } else if (stateDef.states && stateDef.write) {
2828
2856
  $(`#${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);
2857
+ } else if (stateDef.type === 'boolean') {
2858
+ $(`#${sid}`).find('input[type=\'checkbox\']').prop('checked', state.val);
2831
2859
  } else {
2832
2860
  $(`#${sid}`).find('.value').text(`${state.val} ${(stateDef.unit) ? stateDef.unit : ''}`);
2833
2861
  }
@@ -2836,23 +2864,23 @@ function setDashStates(id, state) {
2836
2864
  }
2837
2865
 
2838
2866
  function hookControls() {
2839
- $("input[type='checkbox']").change(function (event) {
2867
+ $('input[type=\'checkbox\']').change(function (event) {
2840
2868
  const val = $(this).is(':checked');
2841
- const id = $(this).parents(".state").attr('oid');
2869
+ const id = $(this).parents('.state').attr('oid');
2842
2870
  sendTo(namespace, 'setState', {id: id, val: val}, function (data) {
2843
2871
  //console.log(data);
2844
2872
  });
2845
2873
  });
2846
- $("input[type='range']").change(function (event) {
2874
+ $('input[type=\'range\']').change(function (event) {
2847
2875
  const val = $(this).val();
2848
- const id = $(this).parents(".state").attr('oid');
2876
+ const id = $(this).parents('.state').attr('oid');
2849
2877
  sendTo(namespace, 'setState', {id: id, val: val}, function (data) {
2850
2878
  //console.log(data);
2851
2879
  });
2852
2880
  });
2853
- $(".state select").on( "change", function () {
2881
+ $('.state select').on('change', function () {
2854
2882
  const val = $(this).val();
2855
- const id = $(this).parents(".state").attr('oid');
2883
+ const id = $(this).parents('.state').attr('oid');
2856
2884
  sendTo(namespace, 'setState', {id: id, val: val}, function (data) {
2857
2885
  //console.log(data);
2858
2886
  });
@@ -2860,12 +2888,12 @@ function hookControls() {
2860
2888
  }
2861
2889
 
2862
2890
  function getIdleTime(value) {
2863
- return (value) ? moment(new Date(value)).fromNow(true) : "";
2891
+ return (value) ? moment(new Date(value)).fromNow(true) : '';
2864
2892
  }
2865
2893
 
2866
2894
  function updateCardTimer() {
2867
2895
  if (devices) {
2868
- devices.forEach((dev)=>{
2896
+ devices.forEach((dev) => {
2869
2897
  const id = dev._id;
2870
2898
  if (id) {
2871
2899
  const rid = id.split('.').join('_');
@@ -2882,9 +2910,7 @@ function updateDevice(id) {
2882
2910
  showMessage(devs.error, _('Error'));
2883
2911
  } else {
2884
2912
  removeDevice(id);
2885
- devs.forEach((dev)=>{
2886
- devices.push(dev);
2887
- })
2913
+ devs.forEach(dev => devices.push(dev));
2888
2914
  showDevices();
2889
2915
  }
2890
2916
  }
@@ -2902,21 +2928,21 @@ function removeDevice(id) {
2902
2928
  }
2903
2929
 
2904
2930
  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
- }
2931
+ const dev = getDeviceByID(id);
2932
+ if (dev && dev.common) {
2933
+ dev.common.deactivated = !(dev.common.deactivated);
2934
+ sendTo(namespace, 'setDeviceActivated', {id: id, deactivated: dev.common.deactivated}, function () {
2935
+ showDevices();
2936
+ });
2937
+ }
2912
2938
  }
2913
2939
 
2914
2940
  function reconfigureDlg(id) {
2915
2941
  const text = translateWord(`Do you really want to reconfigure device?`);
2916
2942
  $('#modalreconfigure').find('p').text(text);
2917
- $("#modalreconfigure a.btn[name='yes']").unbind('click');
2918
- $("#modalreconfigure a.btn[name='yes']").click(() => {
2919
- reconfigureDevice(id, force);
2943
+ $('#modalreconfigure a.btn[name=\'yes\']').unbind('click');
2944
+ $('#modalreconfigure a.btn[name=\'yes\']').click(() => {
2945
+ reconfigureDevice(id/*, force*/);
2920
2946
  });
2921
2947
  $('#modalreconfigure').modal('open');
2922
2948
  Materialize.updateTextFields();