iobroker.zigbee 3.2.1 → 3.2.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -154,6 +154,22 @@ You can thank the authors by these links:
154
154
 
155
155
  -----------------------------------------------------------------------------------------------------
156
156
  ## Changelog
157
+ ### 3.2.3 (2025-10-31)
158
+ * (asgothian) Improvements on debug UI
159
+ * (asgothian) Option 'resend_states' to publish state values to device on reconnect
160
+ * (asgothian) Improved group card
161
+ * (asgothian) Improved group info
162
+ * (asgothian) Modified coordinator card (2 sides)
163
+ * (asgothian) retry on error 25
164
+ * (asgothian) clear stashed error messages
165
+ * (asgothian) ZHC 25.50.0 or newer
166
+
167
+ ### 3.2.2 (2025-10-27)
168
+ * (asgothian) Bugfix on delete object.
169
+ * (asgothian) improved device query.
170
+ * (asgothain) fixed delete device with local overrides.
171
+ *
172
+
157
173
  ### 3.2.1 (2025-10-26)
158
174
  * (asgothian) fix bug #2640
159
175
  *
package/admin/admin.js CHANGED
@@ -36,7 +36,7 @@ let devices = [],
36
36
  },
37
37
  cidList,
38
38
  shuffleInstance,
39
- errorData = [],
39
+ errorData = { errors:{}, unknownModels: {}},
40
40
  debugMessages = {},
41
41
  debugInLog = true,
42
42
  nvRamBackup = {},
@@ -64,7 +64,7 @@ const networkOptions = {
64
64
  const savedSettings = [
65
65
  'port', 'panID', 'channel', 'disableLed', 'countDown', 'groups', 'extPanID', 'precfgkey', 'transmitPower','useNewCompositeStates',
66
66
  'adapterType', 'debugHerdsman', 'disableBackup', 'external', 'startWithInconsistent','pingTimeout','listDevicesAtStart',
67
- 'warnOnDeviceAnnouncement', 'baudRate', 'flowCTRL', 'autostart', 'readAtAnnounce', 'startReadDelay', 'readAllAtStart','pingCluster'
67
+ 'warnOnDeviceAnnouncement', 'baudRate', 'flowCTRL', 'autostart', 'readAtAnnounce', 'startReadDelay', 'readAllAtStart','pingCluster','availableUpdateTime'
68
68
  ];
69
69
  const lockout = {
70
70
  timeoutid:undefined,
@@ -466,7 +466,8 @@ function showLocalData() {
466
466
  const keys = item.replace('d_delall_', '').split('-');
467
467
  const model = models[keys[0]];
468
468
  const device = model.devices.find( (d) => d.native.id === keys[1]);
469
- deleteConfirmation(keys[1], device.common.name, keys[1], models[keys[0]].devices.count <=1 ? models[keys[0]]?.model.model : undefined);
469
+ console.warn(`setting delete confirmation with ${keys[1]} ${models[keys[0]].devices?.length} ${models[keys[0]]?.model.model} `);
470
+ deleteConfirmation(keys[1], device.common.name, keys[1], models[keys[0]]?.devices?.length <=1 ? models[keys[0]]?.model?.model : undefined);
470
471
  });
471
472
  }
472
473
  }
@@ -497,6 +498,7 @@ function getCard(dev) {
497
498
  rooms.push(dev.rooms[r]);
498
499
  }
499
500
  }
501
+
500
502
  const NoInterviewIcon = dev.info?.device?.interviewstate != 'SUCCESSFUL' ? `<div class="col tool"><i class="material-icons icon-red">perm_device_information</i></div>` : ``;
501
503
  const paired = (dev.paired) ? '' : '<i class="material-icons right">leak_remove</i>';
502
504
  const rid = id.split('.').join('_');
@@ -586,32 +588,59 @@ function getCoordinatorCard(dev) {
586
588
  lq = (dev && dev.link_quality) ? `<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>` : '',
587
589
  info = `<div style="min-height:88px; font-size: 0.8em" class="truncate">
588
590
  <ul>
589
- <li><span class="label">type:</span><span>${coordinatorinfo.type}</span></li>
590
- <li><span class="label">version:</span><span>${coordinatorinfo.version}</span></li>
591
- <li><span class="label">revision:</span><span>${coordinatorinfo.revision}</span></li>
592
- <li><span class="label">port:</span><span>${coordinatorinfo.port}</span></li>
593
- <li><span class="label">channel:</span><span>${coordinatorinfo.channel}</span></li>
594
- <li><span class="label">------------</span><span>Software versions </span></li>
595
- <li><span class="label">adapter:</span><span>${coordinatorinfo.installedVersion}</span></li>
596
- <li><span class="label">installed from:</span><span>${coordinatorinfo.installSource}</span></li>
597
- <li><span class="label">ZHC / ZH:</span><span>${coordinatorinfo.converters} / ${coordinatorinfo.herdsman}</span></li>
591
+ <li><span class="label coordinator">type:</span><span>${coordinatorinfo.type}</span></li>
592
+ <li><span class="label coordinator">version:</span><span>${coordinatorinfo.version}</span></li>
593
+ <li><span class="label coordinator">revision:</span><span>${coordinatorinfo.revision}</span></li>
594
+ <li><span class="label coordinator">port:</span><span>${coordinatorinfo.port}</span></li>
595
+ <li><span class="label coordinator">channel:</span><span>${coordinatorinfo.channel}</span></li>
598
596
  </ul>
599
597
  </div>`,
600
598
  permitJoinBtn = '<div class="col tool"><button name="joinCard" class="waves-effect btn-small btn-flat right hoverable green tooltipped" title="open network"><i class="material-icons icon-green">leak_add</i></button></div>',
601
599
  //permitJoinBtn = `<div class="col tool"><button name="join" class="btn-floating-sml waves-effect waves-light right hoverable green><i class="material-icons">leak_add</i></button></div>`,
602
600
  card = `<div id="${id}" class="device">
603
- <div class="card hoverable">
604
- <div class="card-content zcard">
605
- <span class="top right small" style="border-radius: 50%">
606
- ${lq}
607
- ${status}
608
- ${permitJoinBtn}
609
- </span>
610
- <!--/a--!>
611
- <span id="dName" class="card-title truncate">${title}</span><!--${paired}--!>
612
- <i class="left">${image}</i>
613
- ${info}
614
- <div class="footer right-align"></div>
601
+ <div class="card hoverable flipable">
602
+ <div class="front face">
603
+ <div class="card-content zcard">
604
+ <div class="flip" style="cursor: pointer">
605
+ <span class="top right small" style="border-radius: 50%">
606
+ ${lq}
607
+ ${status}
608
+ ${permitJoinBtn}
609
+ </span>
610
+ <!--/a--!>
611
+ <span id="dName" class="card-title truncate">${title}</span><!--${paired}--!>
612
+ </div>
613
+ <i class="left">${image}</i>
614
+ ${info}
615
+ <div class="footer right-align">
616
+ <div class="flip" style="cursor: pointer"><i class="material-icons">rotate_left</i></div>
617
+ </div>
618
+ </div>
619
+ </div>
620
+ <div class="back face">
621
+ <div class="card-content zcard">
622
+ <div class="flip" style="cursor: pointer">
623
+ <span class="top right small" style="border-radius: 50%">
624
+ ${lq}
625
+ ${status}
626
+ ${permitJoinBtn}
627
+ </span>
628
+ <!--/a--!>
629
+ <span id="dName" class="card-title truncate">${title}</span><!--${paired}--!>
630
+ </div>
631
+ <i class="left">${image}</i>
632
+ <div style="min-height:88px; font-size: 0.8em" class="truncate">
633
+ <ul>
634
+ <li><span class="label coordinator">Adapter:</span><span>${coordinatorinfo.installedVersion}</span></li>
635
+ <li><span class="label coordinator">Installed:</span><span>${coordinatorinfo.installSource}</span></li>
636
+ <li><span class="label coordinator">Herdsman:</span><span>${coordinatorinfo.herdsman}</span></li>
637
+ <li><span class="label coordinator">Converters:</span><span>${coordinatorinfo.converters}</span></li>
638
+ </ul>
639
+ </div>
640
+ <div class="footer right-align">
641
+ <div class="flip" style="cursor: pointer"><i class="material-icons">rotate_left</i></div>
642
+ </div>
643
+ </div>
615
644
  </div>
616
645
  </div>
617
646
  </div>`;
@@ -636,19 +665,18 @@ function getGroupCard(dev) {
636
665
  const roomInfo = rooms.length ? `<li><span class="labelinfo">rooms:</span><span>${rooms.join(',') || ''}</span></li>` : '';
637
666
  const room = rooms.join(',') || '&nbsp';
638
667
  let memberCount = 0;
639
- let info = `<div style="min-height:88px; font-size: 0.8em; overflow-y: auto" class="truncate">
640
- <ul>`;
641
- info = info.concat(`<li><span class="labelinfo">Group ${numid}</span></li>`);
668
+ const info = [`<div style="min-height:88px; font-size: 0.8em; height: 90px; width: 220px; overflow-y: auto" class="truncate"><ul>`];
669
+ info.push(`<li><span class="labelinfo">Group ${numid}</span></li>`);
642
670
  if (dev.memberinfo === undefined) {
643
- info = info.concat(`<li><span class="labelinfo">No devices in group</span></li>`);
671
+ info.push(`<li><span class="labelinfo">No devices in group</span></li>`);
644
672
  } else {
645
673
  for (let m = 0; m < dev.memberinfo.length; m++) {
646
- 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>`);
674
+ info.push(`<li><span align:"left">${dev.memberinfo[m].device}.${dev.memberinfo[m].epid}</span><span align:"right"> ...${dev.memberinfo[m].ieee.slice(-4)}</span></li>`);
647
675
  }
648
676
  memberCount = (dev.memberinfo.length < 8 ? dev.memberinfo.length : 7);
649
677
  }
650
678
  ;
651
- info = info.concat(` ${roomInfo}</ul>
679
+ info.push(` ${roomInfo}</ul>
652
680
  </div>`);
653
681
  const infoBtn = `<button name="info" class="left btn-flat btn-small"><i class="material-icons icon-blue">info</i></button>`;
654
682
  const image = `<img src="${dev.common.icon}" width="64px" onerror="this.onerror=null;this.src='img/unavailable.png';">`;
@@ -666,7 +694,7 @@ function getGroupCard(dev) {
666
694
  <span id="dName" class="card-title truncate">${title}</span><!----!>
667
695
  </div>
668
696
  <i class="left">${image}</i>
669
- ${info}
697
+ ${info.join('')}
670
698
 
671
699
  <div class="footer right-align"></div>
672
700
  </div>
@@ -1389,7 +1417,7 @@ function getCoordinatorInfo() {
1389
1417
  sendToWrapper(namespace, 'getCoordinatorInfo', {}, function (msg) {
1390
1418
  if (msg) {
1391
1419
  if (msg.error) {
1392
- errorData.push(msg.error);
1420
+ //errorData.push(msg.error);
1393
1421
  delete msg.error;
1394
1422
  isHerdsmanRunning = false;
1395
1423
  } else {
@@ -1568,13 +1596,7 @@ async function editDeviceOptions(id, isModel) {
1568
1596
  }
1569
1597
  else
1570
1598
  {
1571
- const val = $(`#option_value_${k}`).val();
1572
- try {
1573
- _do[k].value = JSON.parse(val);
1574
- }
1575
- catch {
1576
- _do[k].value = val;
1577
- }
1599
+ _do[k].value = $(`#option_value_${k}`).val();
1578
1600
  }
1579
1601
  if (_do[k].key.length > 0) {
1580
1602
  console.warn(`dok: ${_do[k].key} : ${_do[k].value}`);
@@ -1617,11 +1639,14 @@ async function editDeviceOptions(id, isModel) {
1617
1639
 
1618
1640
  const dialogData = {};
1619
1641
 
1642
+ const adapterDefinedOptions = ['resend_states']
1643
+
1620
1644
  if (isModel) {
1621
1645
  const model = id.model;
1622
1646
  dialogData.model = model;
1623
1647
  dialogData.availableOptions = model.options.slice() || [];
1624
1648
  dialogData.availableOptions.push('custom');
1649
+ dialogData.availableOptions.push(...adapterDefinedOptions)
1625
1650
  if (model.hasLegacyDef) dialogData.availableOptions.push('legacy');
1626
1651
  dialogData.setOptions = {};
1627
1652
  for (const k in Object.keys(id.setOptions))
@@ -1636,6 +1661,7 @@ async function editDeviceOptions(id, isModel) {
1636
1661
  const dev = devices.find((d) => d._id == id);
1637
1662
  dialogData.model = dev.info.mapped;
1638
1663
  dialogData.availableOptions = (dev.info.mapped ? dev.info.mapped.options.slice() || []:[]);
1664
+ dialogData.availableOptions.push(...adapterDefinedOptions)
1639
1665
  dialogData.name = dev.common.name;
1640
1666
  dialogData.icon = dev.common.icon || dev.icon;
1641
1667
  dialogData.default_icon = (dev.common.type === 'group' ? dev.common.modelIcon : `img/${dev.common.type.replace(/\//g, '-')}.png`);
@@ -1731,6 +1757,7 @@ function HtmlFromInDebugMessages(messages, devID, filter) {
1731
1757
  const filterSet = new Set();
1732
1758
  let isodd = true;
1733
1759
  const buttonList = [];
1760
+ const idRed = ' id="dbgred"'
1734
1761
  if (dbgMsghide.has('i_'+devID)) {
1735
1762
  console.warn('in all filtered out')
1736
1763
  Html.push('&nbsp;')
@@ -1742,7 +1769,7 @@ function HtmlFromInDebugMessages(messages, devID, filter) {
1742
1769
  let fs = '';
1743
1770
  for (const state of item.states) {
1744
1771
  fs = fs+state.id+'.'+fne(item);
1745
- const redText = (item.errors && item.errors.length > 0 ? ' id="dbgred"' : '');
1772
+ const redText = (item.errors && item.errors.length > 0 ? idRed : '');
1746
1773
  idx--;
1747
1774
  const LHtml = [(`<tr id="${isodd ? 'dbgrowodd' : 'dbgroweven'}">`)];
1748
1775
  if (idx==0) {
@@ -1750,7 +1777,7 @@ function HtmlFromInDebugMessages(messages, devID, filter) {
1750
1777
  buttonList.push(item.dataID)
1751
1778
  LHtml.push(`<td${rowspan}>${msgbutton}</td><td${rowspan}>${safestring(item.payload)}</td>`);
1752
1779
  }
1753
- LHtml.push(`<td></td><td${redText}>${safestring(state.payload)}</td><td${redText}>${state.id}</td><td${redText}>${state.value}</td><td${redText}>${fne(item)}</td></tr>`);
1780
+ LHtml.push(`<td></td><td${redText}>${safestring(state.payload)}</td><td${state.inError ? idRed: redText}>${state.id}</td><td${state.inError ? idRed : redText}>${state.value}</td><td${redText}>${fne(item)}</td></tr>`);
1754
1781
  IHtml.unshift(...LHtml)
1755
1782
  }
1756
1783
  if (filter)
@@ -1835,7 +1862,7 @@ function displayDebugMessages(msg) {
1835
1862
  const modelUrl = (type_url === 'unknown') ? 'unknown' : `<a href="https://www.zigbee2mqtt.io/devices/${type_url}.html" target="_blank" rel="noopener noreferrer">${image}</a>`;
1836
1863
  const devName = (dev && dev.common && dev.common.name) ? dev.common.name : 'unnamed';
1837
1864
  const button = `<a id="e_${devID}" class="btn-floating waves-effect waves-light green tooltipped center-align hoverable translateT" title="Update debug messages"><i class="material-icons large">sync_problem</i></a>`
1838
- const dbutton = `<a id="d_${devID}" class="btn-floating waves-effect waves-light red tooltipped center-align hoverable translateT" title="Update debug messages"><i class="material-icons icon-yellow large">delete_forever</i></a>`;
1865
+ const dbutton = `<a id="d_${devID}" class="btn-floating waves-effect waves-light red tooltipped center-align hoverable translateT" title="Delete debug messages"><i class="material-icons icon-yellow large">delete_forever</i></a>`;
1839
1866
  buttonNames.push(devID);
1840
1867
  Html.push(`<li><table><thead id="dbgtable"><tr><td colspan="4">${devName} (ID: ${devID} Model: ${dev && dev.common ? dev.common.name : 'unknown'})</td><td>${modelUrl}</td><td>${button}</td><td>${dbutton}</td></tr></thead><tbody>`);
1841
1868
  if (dbgData[devID].IN.length > 0) {
@@ -1988,7 +2015,7 @@ function getDevices() {
1988
2015
  sendToWrapper(namespace, 'getCoordinatorInfo', {}, function (msg) {
1989
2016
  if (msg) {
1990
2017
  if (msg.error) {
1991
- errorData.push(msg.error);
2018
+ //errorData.push(msg.error);
1992
2019
  delete msg.error;
1993
2020
  isHerdsmanRunning = false;
1994
2021
  } else {
@@ -2006,7 +2033,7 @@ function getDevices() {
2006
2033
  if (msg) {
2007
2034
  extractDevicesData(msg);
2008
2035
  if (msg.error) {
2009
- errorData.push(msg.error);
2036
+ //errorData.push(msg.error);
2010
2037
  isHerdsmanRunning = false;
2011
2038
  } else {
2012
2039
  isHerdsmanRunning = true;
@@ -2043,7 +2070,7 @@ function extractDevicesData(msg) {
2043
2070
  $('#state_cleanup_btn').removeClass('hide');
2044
2071
  else
2045
2072
  $('#state_cleanup_btn').addClass('hide');
2046
- if (msg.errors && msg.errors.length > 0) {
2073
+ if (msg.errors?.hasData) {
2047
2074
  $('#show_errors_btn').removeClass('hide');
2048
2075
  errorData = msg.errors;
2049
2076
  }
@@ -2084,14 +2111,14 @@ function getMap(rebuild) {
2084
2111
  $('#refresh').removeClass('disabled');
2085
2112
  if (msg) {
2086
2113
  if (msg.error) {
2087
- errorData.push(msg.error);
2114
+ //errorData.push(msg.error);
2088
2115
  isHerdsmanRunning = false;
2089
2116
  updateStartButton();
2090
2117
  } else {
2091
2118
  isHerdsmanRunning = true;
2092
2119
  updateStartButton();
2093
2120
  if (msg.errors.length > 0 && $('#errorCollectionOn').is(':checked')) {
2094
- showMessage(msg.errors.join('<p>'), 'Map generation messages');
2121
+ showMessage(msg.errors.join('<br>'), 'Map generation messages');
2095
2122
  }
2096
2123
  map = msg;
2097
2124
  showNetworkMap(devices, map);
@@ -2229,7 +2256,34 @@ function load(settings, onChange) {
2229
2256
  cleanConfirmation();
2230
2257
  });
2231
2258
  $('#show_errors_btn').click(function () {
2232
- showMessage(errorData.join('<br>'), 'Stashed error messages');
2259
+ const errMsgTable = [];
2260
+ console.warn(JSON.stringify(errorData));
2261
+ if (Object.keys(errorData.errors).length > 0) {
2262
+ errMsgTable.push(`<table><tr><th>Message</th><th>#</th><th>last seen</th></tr>`)
2263
+ for (const err of Object.values(errorData.errors))
2264
+ if (err) errMsgTable.push(`<tr><td>${err.message}</td><td>${err.ts.length}</td><td>${new Date(err.ts[err.ts.length-1]).toLocaleTimeString()}</td></tr>`)
2265
+ errMsgTable.push('</table>');
2266
+ }
2267
+ if (Object.keys(errorData.unknownModels).length > 0) {
2268
+ errMsgTable.push(`<table><tr><th>Unknown Models</th><th>#</th><th>last seen</th></tr>`)
2269
+ for (const err of Object.values(errorData.unknownModels))
2270
+ errMsgTable.push(`<tr><td>${err.message}</td><td>${err.ts.length}</td><td>${new Date(err.ts[err.ts.length-1]).toLocaleTimeString()}</td></tr>`)
2271
+ errMsgTable.push('</table>');
2272
+ }
2273
+ console.warn(JSON.stringify(errMsgTable));
2274
+ showMessage(errMsgTable.join(''), 'Stashed error messages', '<a id="delete_errors_btn" class="btn-floating waves-effect waves-light tooltipped center-align hoverable translateT" title="delete Errors"></i class="material-icons icon-black">delete_sweep</i></a>');
2275
+ $('#delete_errors_btn').unbind('click')
2276
+ $('#delete_errors_btn').click(function () {
2277
+ sendToWrapper(namespace, 'clearErrors', {}, function(msg) {
2278
+ if (msg) {
2279
+ console.warn('msg is ' + JSON.stringify(msg));
2280
+ errorData = msg;
2281
+ $('#show_errors_btn').addClass('hide');
2282
+ }
2283
+ $('#dialog-message').modal('close');
2284
+
2285
+ })
2286
+ })
2233
2287
  });
2234
2288
  $('#download_icons_btn').click(function () {
2235
2289
  showMessage(downloadIcons());
@@ -2617,6 +2671,26 @@ socket.on('stateChange', function (id, state) {
2617
2671
  getDevices();
2618
2672
  }
2619
2673
  }
2674
+ } else if (id.match(/\.info\.lasterror$/)) {
2675
+ try {
2676
+ console.warn(`lasterror is ${JSON.stringify(state)}`)
2677
+ const errobj = JSON.parse(state.val);
2678
+ let changed = false;
2679
+ if (errobj.error) {
2680
+ errorData.errors[errobj.error] = errobj.data;
2681
+ changed = true;
2682
+ }
2683
+ if (errobj.model) {
2684
+ errorData.unknownModels[errobj.model] = errobj.data;
2685
+ changed = true;
2686
+ }
2687
+ errorData.hasData |= changed;
2688
+ if (changed) {
2689
+ $('#show_errors_btn').removeClass('hide');
2690
+ }
2691
+ }
2692
+ catch { console.error('JSON didnt parse') }
2693
+
2620
2694
  } else {
2621
2695
  const devId = getDevId(id);
2622
2696
  putEventToNode(devId);
@@ -4020,7 +4094,7 @@ function genDevInfo(device) {
4020
4094
  `<div style="font-size: 0.9em">
4021
4095
  <ul>`);
4022
4096
  for (const item in mapped) {
4023
- if (item == 'model')
4097
+ if (item == 'model' && mapped.model != 'group')
4024
4098
  mappedInfo.push(genRow(item,modelUrl));
4025
4099
  else
4026
4100
  if (typeof mapped[item] != 'object') mappedInfo.push(genRow(item,mapped[item]));
@@ -4042,7 +4116,7 @@ function genDevInfo(device) {
4042
4116
  </ul>
4043
4117
  </div>`;
4044
4118
  }
4045
- const imgSrc = device.icon || device.common.icon;
4119
+ const imgSrc = mapped?.model == 'group' ? mapped.icon : device.icon || device.common.icon;
4046
4120
  const imgInfo = (imgSrc) ? `<img src=${imgSrc} width='150px' onerror="this.onerror=null;this.src='img/unavailable.png';"><div class="divider"></div>` : '';
4047
4121
  const info =[
4048
4122
  `<div class="col ${device.memberinfo != undefined ? 's12 m12 l12 xl12':'s12 m6 l6 xl6'}">
@@ -858,6 +858,10 @@
858
858
  <input id="useNewCompositeStates" type="checkbox" class="value"/>
859
859
  <label class="translate" for="useNewCompositeStates">use channel for complex exposes</label>
860
860
  </div>
861
+ <div class="input-field col s12 m6 l4">
862
+ <input id="availableUpdateTime" type="number" min="0" max="120" class="value"/>
863
+ <label class="translate" for="availableUpdateTime">Min. Available update timeout.</label>
864
+ </div>
861
865
  <div class="input-field col s12 m12 l12 col-startWithInconsistent">
862
866
  <input id="startWithInconsistent" type="checkbox" class="value"/>
863
867
  <label class="translate" for="startWithInconsistent">SettingsExclude</label>
package/admin/tab_m.html CHANGED
@@ -286,9 +286,14 @@
286
286
  width: 100px;
287
287
  font-weight: bold;
288
288
  }
289
+ .m span.label.coordinator {
290
+ display: inline-block;
291
+ width: 75px;
292
+ font-weight: bold;
293
+ }
289
294
  .m span.label.dash {
290
295
  display: inline-block;
291
- width: 140px;
296
+ width: 135px;
292
297
  font-weight: bold;
293
298
  }
294
299
  .m span.labelinfo {
package/io-package.json CHANGED
@@ -1,8 +1,34 @@
1
1
  {
2
2
  "common": {
3
3
  "name": "zigbee",
4
- "version": "3.2.1",
4
+ "version": "3.2.3",
5
5
  "news": {
6
+ "3.2.3": {
7
+ "en": "Improvements on debug UI\nOption 'resend_states' to publish state values to device on reconnect\nImproved group card\nImproved group info\nModified coordinator card (2 sides)\nretry on error 25\nclear stashed error messages\nZHC 25.50.0 or newer",
8
+ "de": "Verbesserungen bei debug UI\nOption 'resend_states' zum Veröffentlichen von Zustandswerten auf das Gerät auf dem Reconnect\nVerbesserte Gruppenkarte\nVerbesserte Gruppeninformationen\nModifizierte Koordinatorkarte (2 Seiten)\nretry auf fehler 25\nklare stashed fehlermeldungen\nZHC 25.50.0 oder neuer",
9
+ "ru": "Улучшения в Debug UI\nВариант «resend_states» для публикации значений состояния на устройстве при повторном подключении\nУлучшенная групповая карта\nУлучшенная групповая информация\nИзмененная карта координатора (2 стороны)\nвозвращение к ошибке 25\nскрытые сообщения об ошибках\nZHC 25.50.0 или новее",
10
+ "pt": "Melhorias na interface de depuração\nOpção 'resend_states' para publicar valores de estado para o dispositivo ao religar\nCartão de grupo melhorado\nInformação melhorada do grupo\nCartão coordenador modificado (2 lados)\ntentar novamente no erro 25\nlimpar mensagens de erro escondidas\nZHC 25.50.0 ou mais recente",
11
+ "nl": "Verbeteringen op debug UI\nOptie 'resend_states' om statuswaarden naar apparaat te publiceren bij opnieuw verbinden\nVerbeterde groepskaart\nVerbeterde groepsinfo\nGewijzigde coördinatiekaart (2 zijden)\nopnieuw proberen bij fout 25\nfoutmeldingen wissen\nZHC 25.50.0 of nieuwer",
12
+ "fr": "Amélioration de l'assurance-chômage de débogage\nOption 'resend_states' pour publier les valeurs d'état sur le périphérique sur la connexion\nCarte de groupe améliorée\nInformations de groupe améliorées\nCarte de coordonnateur modifiée (2 côtés)\nréessayer sur l'erreur 25\nmessages d'erreur clairs et cachés\nZHC 25.50.0 ou plus récent",
13
+ "it": "Miglioramenti sul debug UI\nOpzione 'resend_states' per pubblicare i valori di stato sul dispositivo sulla riconnessione\nScheda di gruppo migliorata\nInformazioni di gruppo migliorate\nCarta coordinatrice modificata (2 lati)\nriprovare sull'errore 25\nchiari messaggi di errore stanti\nZHC 25.50.0 o più recente",
14
+ "es": "Mejoras en el depuro UI\nOpción 'resend_states' para publicar valores estatales al dispositivo en la reconexión\nTarjeta de grupo mejorada\nInformación de grupo mejorada\nTarjeta de coordinación modificada (2 lados)\nretry on error 25\nmensajes de error claros\nZHC 25.50.0 o más",
15
+ "pl": "Ulepszenia dotyczące debugowania interfejsu użytkownika\nOpcja \"resend _ states\" do publikowania wartości stanu dla urządzenia przy ponownym połączeniu\nUlepszona karta grupowa\nUlepszone informacje grupowe\nZmodyfikowana karta koordynatora (2 strony)\nponowna próba błędu 25\njasne komunikaty błędów ukryte\nZHC 25.50.0 lub nowsze",
16
+ "uk": "Удосконалення на дебурзі UI\nВаріант 'resend_states' для публікації державних значень для пристрою на відключенні\nПокращена групова карта\nПокращена інформація групи\nЗмінена карта координатора (2 сторони)\nптиця по помилки 25\nчіткі повідомлення про помилку\nZHC 25.50.0 або новачок",
17
+ "zh-cn": "调试 UI 的改进\n选项“ resend_ states ” 以在重新连接时发布状态值以设备\n改进组卡\n改进组信息\n修改的协调员卡(2面)\n重试错误 25\n清除隐藏的错误消息\nZHC 25.50.0或更新"
18
+ },
19
+ "3.2.2": {
20
+ "en": "Bugfix on delete object.\nimproved device query.\nfixed delete device with local overrides.\n",
21
+ "de": "Bugfix auf Löschobjekt.\nverbesserte geräteabfrage.\nfeste löschvorrichtung mit lokalen overrides.\n",
22
+ "ru": "Bugfix для удаления объекта.\nулучшенный запрос устройства.\nфиксированное устройство удаления с локальными переопределениями.\n",
23
+ "pt": "Correcção de erros ao apagar o objecto.\npesquisa de dispositivos melhorada.\ndispositivo de exclusão fixo com substituições locais.\n",
24
+ "nl": "Bugfix bij verwijderen van object.\nverbeterde apparaatquery.\nvast delete apparaat met lokale overrides.\n",
25
+ "fr": "Correction sur objet de suppression.\nrequête de périphérique améliorée.\ndispositif de suppression fixe avec redéfinitions locales.\n",
26
+ "it": "Bugfix su delete object.\nmigliore query del dispositivo.\ndispositivo di cancellazione fisso con override locali.\n",
27
+ "es": "Bugfix en el objeto borrado.\nmejorada consulta de dispositivos.\ndispositivo de eliminación fijo con anulas locales.\n",
28
+ "pl": "Bugfix na usunąć obiekt.\nulepszone zapytanie urządzenia.\nnaprawione urządzenie usuwające z lokalnymi przekroczeniami.\n",
29
+ "uk": "Виправлення помилок при видаленні об'єкта.\nпокращений пристрій запиту.\nфіксований пристрій видалення з локальними перенаряддями.\n",
30
+ "zh-cn": "删除对象上的错误修正 .\n改进设备查询 .\n有本地覆盖的固定删除设备 .\n"
31
+ },
6
32
  "3.2.1": {
7
33
  "en": "fix bug #2640\n",
8
34
  "de": "fehler beheben #2640\n",
@@ -67,32 +93,6 @@
67
93
  "pl": "Usuń dodatkowe logowanie\nDodaj dodatkowe konfiguracje\nNie czytaj stanów z wyłączonych urządzeń\nIgnoruj dezaktywowane urządzenia dla aktualizacji stanu grupy\nZmień wyświetlacz dla dezaktywowanych urządzeń w drzewie obiektu (szary, bez połączonej ikony)\nbardziej szczegółowe debugowanie urządzenia\ndebug urządzenia UI ulepszenia\nParowanie i urządzenie Przyciski zapytania na kartach routera\nZHC 25.31.0, ZH 6.1.2 lub nowsze\nOpcje oparte na opcjach zdefiniowanych przez ZHC",
68
94
  "uk": "Видалення додаткового входу\nДодати додаткові конфігурації\nНе читати стани від деактивованих пристроїв\nІгноровані пристрої для оновлення групового стану\nЗміна відображення для деактивованих пристроїв в дереві об'єкта (сірий, не підключений значок)\nдокладніше пристрій debug\nпристрій Debug UI удосконалення\nПірсинг і пристрій Query гудзики на маршрутизаторних картках\nZHC 25.31.0, ZH 6.1.2 або новачок\nВаріанти, засновані на визначених параметрах ZHC",
69
95
  "zh-cn": "删除额外的日志\n添加额外的配置\n不读取已关闭设备的状态\n忽略组状态更新的已关闭设备\n更改对象树上已关闭设备的显示( gray, 无连接图标)\n更详细的设备调试\n设备调试 UI 改进\n路由器卡上的对齐和设备查询按钮\nZHC 25.31.0,ZH 6.1.2或更新\n基于 ZHC 定义选项的选项"
70
- },
71
- "3.1.2": {
72
- "en": "ZHC 25.x\nZH 6.x\nFix pairing bug\nadd ping messages to device debug to verify ping failure reasons\n",
73
- "de": "ZHC 25.x\nZH 6.x\nBehebung des Kopplungsfehlers\nFügen Sie Ping-Nachrichten zum Geräte-Debugging hinzu, um die Gründe für Ping-Ausfälle zu überprüfen.\n",
74
- "ru": "ZHC 25.x\nZH 6.x\nИсправлена ошибка сопряжения.\nдобавьте сообщения ping в отладке устройства для проверки причин отказа ping\n",
75
- "pt": "ZHC 25.x\nZH 6.x\nCorrigir bug de emparelhamento\nAdicionar mensagens de ping ao debug do dispositivo para verificar os motivos da falha no ping\n",
76
- "nl": "ZHC 25.x\nZH 6.x\nRepareer koppelingsfout\nVoeg pingberichten toe aan apparaatdebug om pingfoutredenen te controleren\n",
77
- "fr": "ZHC 25.x\nZH 6.x\nCorriger le bogue de couplage\najouter des messages de ping au débogage des appareils pour vérifier les raisons des échecs de ping\n",
78
- "it": "ZHC 25.x\nZH 6.x\nRisoluzione bug di accoppiamento\nAggiungi messaggi ping al debug del dispositivo per verificare le cause del fallimento del ping\n",
79
- "es": "ZHC 25.x\nZH 6.x\nCorregir error de emparejamiento\nañadir mensajes de ping al depurador del dispositivo para verificar las razones de fallo del ping\n",
80
- "pl": "ZHC 25.x\nZH 6.x\nNapraw błąd parowania\nDodaj wiadomości ping do debugowania urządzenia, aby zweryfikować przyczyny niepowodzenia pingu\n",
81
- "uk": "ЗХК 25.x\nЗХ 6.х\nВиправлено помилку з'єднання\nдодано повідомлення ping до відлагодження пристрою для перевірки причин невдачі ping\n",
82
- "zh-cn": "ZHC 25.x\nZH 6.x (英语)\n修复配对问题\n将ping消息添加到设备调试中,以验证ping失败的原因\n"
83
- },
84
- "3.0.5": {
85
- "en": "fix random error where devices are not shown due to illegal groups\ndrop support for node 18\nRequired node Versions Node 20.19.0 or 22.11.0 or newer (courtesy of ZH 4.4.1 / ZHC 24.8.0)",
86
- "de": "fehler beheben, bei denen geräte wegen illegaler gruppen nicht angezeigt werden\ndrop-unterstützung für knoten 183\nErforderliche Knotenversionen Nr. 20.19.0 oder 22.11.0 oder neuer (courtesy of ZH 4.4.1 / ZHC 24.8.0)",
87
- "ru": "исправить случайную ошибку, когда устройства не отображаются из-за незаконных групп\nподдержка drop для node 18\nТребуемый узел Версии Узел 20.19.0 или 22.11.0 или новее (документ ZH 4.4.1 / ZHC 24.8.0)",
88
- "pt": "corrigir erro aleatório onde os dispositivos não são mostrados devido a grupos ilegais\nsuporte de gota para o nó 18\nNó necessário Versões Node 20.19.0 ou 22.11.0 ou mais recente (cortesia de ZH 4.4.1 / ZHC 24.8.0)",
89
- "nl": "fix willekeurige fout wanneer apparaten niet worden getoond als gevolg van illegale groepen\ndrop ondersteuning voor knooppunt 18\nVereiste node Versies Node 20.19.0 of 22.11.0 of nieuwer (met dank aan ZH 4.4.1 / ZHC 24.8.0)",
90
- "fr": "corriger une erreur aléatoire lorsque les appareils ne sont pas montrés en raison de groupes illégaux\nsupport de chute pour noeud 18\nVersion requise Node 20.19.0 ou 22.11.0 ou plus récent (avec la permission de ZH 4.4.1 / ZHC 248.0)",
91
- "it": "correggere errore casuale in cui i dispositivi non vengono visualizzati a causa di gruppi illegali\nsupporto a goccia per nodo 18\nNodo richiesto Versioni Nodo 20.19.0 o 22.11.0 o più recente (cortesia di ZH 4.4.1 / ZHC 24.8.0)",
92
- "es": "corregir errores aleatorios donde no se muestran dispositivos debido a grupos ilegales\napoyo a los nodos 18\nNodo requerido Versiones Nodo 20.19.0 o 22.11.0 o nuevo (cortesía de ZH 4.4.1 / ZHC 24.8.0)",
93
- "pl": "naprawić błąd losowy w przypadku gdy urządzenia nie są wyświetlane z powodu nielegalnych grup\nobsługa zrzutu dla węzła 18\nWymagany węzeł Wersje Węzeł 20.19.0 lub 22.11.0 lub nowszy (uprzejmość ZH 4.4.1 / ZHC 24.8.0)",
94
- "uk": "виправити випадкові помилки, де пристрої не відображаються через незаконні групи\nпідтримка крапель для вузла 18 років\nПотрібні версії вузла Node 20.19.0 або 22.11.0 або новачка (кількість ЗЗ 4.4.1 / ЗЖК 24.8.0)",
95
- "zh-cn": "在设备因非法组而未显示时修复随机错误\n放弃节点支持 第 18 条\n要求的节点版本为20.19.0或22.11.0或更新(礼仪为ZH 4.4.1 / ZHC 24.8.0)"
96
96
  }
97
97
  },
98
98
  "titleLang": {
@@ -327,7 +327,8 @@
327
327
  "pingTimeout": 300,
328
328
  "pingCluster": "",
329
329
  "listDevicesAtStart": true,
330
- "useNewCompositeStates": false
330
+ "useNewCompositeStates": false,
331
+ "availableUpdateTime": 30
331
332
  },
332
333
  "instanceObjects": [
333
334
  {
@@ -72,7 +72,7 @@ class DeviceDebug extends EventEmitter {
72
72
  this.dataByDevice[item.deviceID] = DevData;
73
73
  }
74
74
  if (message.hasOwnProperty('message') && this.logStatus) {
75
- this.warn(`ELEVATED:${flag} (${dataId.toString(16).slice(-4)}) ${message.message}`)
75
+ this.warn(`ELEVATED:${flag} (${dataId?.toString(16).slice(-4)}) ${message.message}`)
76
76
  }
77
77
  }
78
78
  }
package/lib/commands.js CHANGED
@@ -177,6 +177,9 @@ class Commands {
177
177
  case 'aliveCheck':
178
178
  this.adapter.sendTo(obj.from, obj.command, {msg:'success'}, obj.callback);
179
179
  break;
180
+ case 'clearErrors':
181
+ this.adapter.sendTo(obj.from, obj.command, this.stController.clearStashedErrors(), obj.callback);
182
+ break;
180
183
  default:
181
184
  this.debug(`Commands: Command ${obj.command} is unknown`);
182
185
  //this.adapter.sendTo(obj.from, obj.command, obj.message, obj.callback);
@@ -445,6 +448,14 @@ class Commands {
445
448
  return undefined;
446
449
 
447
450
  }
451
+
452
+ function haveBindableClusters(clusters) {
453
+ const nonBindableClusters = [25,33, 4096]
454
+ if (Array.isArray(clusters)) {
455
+ return (clusters.filter((candidate) => !nonBindableClusters.includes(candidate)).length > 0);
456
+ }
457
+ return false;
458
+ }
448
459
  const rv = {};
449
460
  try {
450
461
  rv.device = {
@@ -463,19 +474,28 @@ class Commands {
463
474
  date_code:device.device.dateCode,
464
475
  build:device.device.softwareBuildID,
465
476
  interviewstate:device.device.interviewState || 'UNKNOWN',
477
+ BindSource: false,
478
+ isGroupable: false,
466
479
  }
467
480
  rv.endpoints = [];
481
+ let dBindSource = false;
482
+ let disGroupable = false;
468
483
  for (const ep_idx in device.endpoints) {
469
484
  const ep = device.endpoints[ep_idx];
485
+ const bindable = haveBindableClusters(ep.outputClusters);
486
+ dBindSource |= bindable;
470
487
  rv.endpoints.push({
471
488
  ID:ep.ID,
472
489
  epName: device.mapped?.endpoint ? getKey(device.mapped?.endpoint(device), ep.ID) : ep.ID,
473
490
  profile:ep.profileID,
474
491
  input_clusters:ep.inputClusters,
475
492
  output_clusters:ep.outputClusters,
493
+ BindSource: Boolean(bindable),
476
494
  })
477
- if (ep.inputClusters.includes(4)) rv.device.isGroupable = true;
495
+ disGroupable |= ep.inputClusters.includes(4);
478
496
  }
497
+ rv.device.isGroupable = Boolean(disGroupable);
498
+ rv.device.BindSource = Boolean(dBindSource);
479
499
  if (device.mapped) {
480
500
  rv.mapped = {
481
501
  model:device.mapped.model,
@@ -745,7 +765,7 @@ class Commands {
745
765
  else this.adapter.sendTo(from, command, {error:err}, callback);
746
766
  }
747
767
  this.adapter.sendTo(from, command, {}, callback);
748
- this.adapter.stController.localConfig.removeLocalData()
768
+ if (msg.dev) this.adapter.stController.localConfig.removeLocalData(devId, msg.model);
749
769
  } else {
750
770
  this.adapter.sendTo(from, command, {error: err}, callback);
751
771
  }
@@ -142,6 +142,12 @@ class localConfig extends EventEmitter {
142
142
  return defaultName;
143
143
  }
144
144
 
145
+ removeLocalData(dev, model) {
146
+ delete this.localData.by_id[dev];
147
+ if (model) delete this.localData.by_model[model];
148
+ this.retainData();
149
+ }
150
+
145
151
  IconForId(id, model, defaultIcon) {
146
152
  let modeloverride = {};
147
153
  this.debug('Icon for id with ' + id + ', ' + model + ' and ' + defaultIcon);
@@ -303,7 +309,6 @@ class localConfig extends EventEmitter {
303
309
  }
304
310
 
305
311
  writeData() {
306
- this.info('retaining local config: ' + JSON.stringify(this.localData));
307
312
  try {
308
313
  fs.writeFileSync(this.filename, JSON.stringify(this.localData, null, 2))
309
314
  this.info('Saved local configuration data');
@@ -3,7 +3,7 @@
3
3
  const safeJsonStringify = require('./json');
4
4
  const { EventEmitter } = require('events');
5
5
  const statesMapping = require('./devices');
6
- const { getAdId, getZbId } = require('./utils');
6
+ const { getAdId, getZbId, zbIdorIeeetoAdId } = require('./utils');
7
7
  const fs = require('fs');
8
8
  const localConfig = require('./localConfig');
9
9
  const path = require('path');
@@ -24,11 +24,10 @@ class StatesController extends EventEmitter {
24
24
  this.cleanupRequired = false;
25
25
  this.timeoutHandleUpload = null;
26
26
  this.ImagesToDownload = [];
27
- this.stashedErrors = {};
28
- this.stashedUnknownModels = {};
29
27
  this.debugMessages = { nodevice:{ in:[], out: []} };
30
28
  this.debugActive = true;
31
29
  this.deviceQueryBlock = [];
30
+ this.clearStashedErrors();
32
31
  }
33
32
 
34
33
  info(message, data) {
@@ -52,22 +51,16 @@ class StatesController extends EventEmitter {
52
51
  }
53
52
 
54
53
  getStashedErrors() {
55
- const rv = [];
56
- try {
57
- if (Object.keys(this.stashedErrors).length > 0)
58
- {
59
- rv.push('<p><b>Stashed Messages</b></p>')
60
- rv.push(Object.values(this.stashedErrors).join('<br>'));
61
- }
62
- if (Object.keys(this.stashedUnknownModels).length > 0) {
63
- rv.push('<p><b>Devices whithout Model definition</b></p>')
64
- rv.push(Object.values(this.stashedUnknownModels).join('<br>'));
65
- }
66
- }
67
- catch (error) {
68
- if (this.debugActive) this.debug(`Error collecting stashed errors: ${error && error.message ? error.message : 'no message available'}`);
69
- }
70
- return rv;
54
+ return this.stashedErrors;
55
+ }
56
+
57
+ clearStashedErrors() {
58
+ this.stashedErrors = {
59
+ errors:{},
60
+ unknownModels: {},
61
+ hasData:false
62
+ };
63
+ return this.stashedErrors;
71
64
  }
72
65
 
73
66
  debugMessagesById() {
@@ -382,18 +375,30 @@ class StatesController extends EventEmitter {
382
375
  }
383
376
 
384
377
  stashErrors(key, msg, error) {
385
- if (!this.stashedErrors.hasOwnProperty(key))
386
- {
387
- if (error) this.error(msg); else this.warn(msg);
388
- this.stashedErrors[key] = msg;
378
+ try {
379
+ if (!this.stashedErrors.errors.hasOwnProperty(key))
380
+ {
381
+ if (error) this.error(msg); else this.warn(msg);
382
+ this.stashedErrors.errors[key] = { message:msg, ts:[Date.now()], error:error };
383
+ }
384
+ else this.stashedErrors.errors[key].ts.push(Date.now());
385
+ this.stashedErrors.hasData = true;
386
+ this.adapter.setState('info.lasterror', JSON.stringify({ error: key, data:this.stashedErrors.errors[key]}))
389
387
  }
388
+ catch { /* */ }
390
389
  }
391
390
 
392
391
  stashUnknownModel(model, msg) {
393
- if (!this.stashedUnknownModels.hasOwnProperty(model)) {
394
- this.stashedUnknownModels[model] = msg;
395
- this.error(`Unknown ${model}: ${msg}`)
392
+ try {
393
+ if (!this.stashedErrors.unknownModels.hasOwnProperty(model)) {
394
+ this.stashedErrors.unknownModels[model] = { message:msg, ts:[Date.now()], error:true };
395
+ this.error(`Unknown ${model}: ${msg}`)
396
+ }
397
+ else this.stashedErrors.unknownModels[model].ts.push(Date.now());
398
+ this.stashedErrors.hasData = true;
399
+ this.adapter.setState('info.lasterror', JSON.stringify({ model: model, data:this.stashedErrors.unknownModels[model]}))
396
400
  }
401
+ catch { /* */ }
397
402
  }
398
403
 
399
404
  async triggerComposite(_deviceId, stateDesc, interactive) {
@@ -430,6 +435,50 @@ class StatesController extends EventEmitter {
430
435
  lfArr.push(state);
431
436
  }
432
437
 
438
+ parseOption(val) {
439
+ if (val === undefined || val === null) return {};
440
+ if (!Array.isArray(val)) {
441
+ try {
442
+ const valObj = JSON.parse(val);
443
+ return valObj;
444
+ }
445
+ catch { /* */ }
446
+ if (typeof val =='object') return val;
447
+ }
448
+ if (typeof val === 'string')
449
+ {
450
+ const keys = val.split(/[,;]/);
451
+ const rv = {};
452
+ for (const k of keys) {
453
+ rv[k] = null;
454
+ }
455
+ return rv;
456
+ }
457
+ this.warn(`illegal option ${JSON.stringify(val)}`)
458
+ return {};
459
+ }
460
+
461
+ async handleStateReset(entity, end_with_device_query) {
462
+ const debugID = Date.now();
463
+ const deviceId = entity.device.ieeeAddr;
464
+ const model = entity.mapped?.model || '';
465
+ const states = this.parseOption(entity.options.resend_states);
466
+ if (end_with_device_query) states.end_with_device_query = true;
467
+ await this.publishFromStates(deviceId, model, states, entity.options, debugID);
468
+ }
469
+
470
+ async publishFromStates(deviceId, model, stateIDs, options, debugID) {
471
+ const adId = zbIdorIeeetoAdId(this.adapter, deviceId, true);
472
+ for (const stateID of Object.keys(stateIDs).sort((a,b) => {if (a=='device_query' || a=='state') return (b=='device_query' ? -1 : 1); else return a.localeCompare(b)})) {
473
+ const state = await this.adapter.getStateAsync(`${adId}.${stateID}`);
474
+ if (stateIDs[stateID]!= null) state.val = stateIDs[stateID];
475
+ if (state && state.hasOwnProperty('val')) {
476
+ await this.publishFromState(deviceId, model, stateID, state, options, debugID)
477
+ }
478
+ }
479
+
480
+ }
481
+
433
482
  async publishFromState(deviceId, model, stateKey, state, options, debugID) {
434
483
  if (this.debugActive) this.debug(`Change state '${stateKey}' at device ${deviceId} type '${model}'`);
435
484
  const has_elevated_debug = this.checkDebugDevice(typeof deviceId == 'number' ? `group_${deviceId}` : deviceId);
@@ -460,7 +509,7 @@ class StatesController extends EventEmitter {
460
509
  if (value === undefined || value === '') {
461
510
  if (has_elevated_debug) {
462
511
  const message = (`no value for device ${deviceId} type '${model}'`);
463
- this.emit('device_debug', { ID:debugID, data: { states:[{id:state.ID, value:'--', payload:'error', ep:stateDesc.epname}],error: 'NOVAL' , IO:false }, message:message});
512
+ this.emit('device_debug', { ID:debugID, data: { states:[{id:state.ID, value:'--', payload:'error', ep:stateDesc.epname}],error: 'NOVAL1' , IO:false }, message:message});
464
513
  }
465
514
  return;
466
515
  }
@@ -1128,17 +1177,18 @@ class StatesController extends EventEmitter {
1128
1177
  } else {
1129
1178
  value = payload[statedesc.prop || statedesc.id];
1130
1179
  }
1180
+
1131
1181
  // checking value
1132
1182
  if (value === undefined || value === null) {
1133
1183
  continue;
1134
1184
  }
1135
1185
 
1136
1186
  let stateID = statedesc.id;
1137
-
1138
1187
  const message = `value generated '${JSON.stringify(value)}' from device ${devId} for '${statedesc.name}'`;
1139
1188
  if (has_elevated_debug) this.emit('device_debug', { ID:debugId, data: { states:[{id:stateID, value:value, payload:payload }],flag:'04', IO:true }, message});
1140
1189
  else if (this.debugActive) this.debug(message);
1141
1190
 
1191
+
1142
1192
  const common = {
1143
1193
  name: statedesc.name,
1144
1194
  type: statedesc.type,
@@ -1187,7 +1237,7 @@ class StatesController extends EventEmitter {
1187
1237
  }
1188
1238
  const message = `No value published for device ${devId}`;
1189
1239
  if (!has_published) {
1190
- if (has_elevated_debug) this.emit('device_debug', { ID:debugId, data:{ error:'NOVAL', IO:true }, message:message});
1240
+ if (has_elevated_debug) this.emit('device_debug', { ID:debugId, data:{ states:[{id:'no state', value:'no value', inError:true, payload:payload }], flag:'04', IO:true }, message:message});
1191
1241
  else if (this.debugActive) this.debug(message);
1192
1242
  }
1193
1243
  }
@@ -1226,11 +1276,11 @@ class StatesController extends EventEmitter {
1226
1276
  async processConverters(converters, devId, model, mappedModel, message, meta, debugId, has_elevated_debug) {
1227
1277
  let cnt = 0;
1228
1278
  const publish = (payload, dID) => {
1229
- if (typeof payload === 'object' && Object.keys(payload).length > 0) {
1279
+ if (typeof payload === 'object') {// && Object.keys(payload).length > 0) {
1230
1280
  this.publishToState(devId, model, payload,dID);
1231
1281
  }
1232
1282
  else if (has_elevated_debug)
1233
- this.emit('device_debug', {ID:debugId,data: { error:`NOVAL`, IO:true }, message:` payload ${JSON.stringify(payload)} is empty`})
1283
+ this.emit('device_debug', {ID:debugId,data: { error:`EPAYLD`, IO:true }, message:` payload ${JSON.stringify(payload)} is empty`})
1234
1284
  };
1235
1285
  const options = await new Promise((resolve, reject) => {
1236
1286
  this.collectOptions(devId, model, false, (options) => {
@@ -56,6 +56,7 @@ class DeviceAvailability extends BaseExtension {
56
56
  this.startReadDelay = config.readAllAtStart ? Math.max(500, Math.min(10000, config.startReadDelay * 1000)) : 0;
57
57
  this.debugDevices = [];
58
58
  this.pingCluster = pingClusters[config.pingCluster] ? pingClusters[config.pingCluster] : {};
59
+ this.availableTime = config.availableUpdateTime ? config.availableUpdateTime : Number.MAX_SAFE_INTEGER;
59
60
  }
60
61
 
61
62
  checkDebugDevice(dev) {
@@ -191,7 +192,7 @@ class DeviceAvailability extends BaseExtension {
191
192
  return;
192
193
  }
193
194
  if (this.isPingable(device)) {
194
- const pt = Date.now();
195
+ const debugID = Date.now();
195
196
  let pingCount = this.ping_counters[device.ieeeAddr];
196
197
  if (pingCount === undefined) {
197
198
  this.ping_counters[device.ieeeAddr] = {failed: 0, reported: 0};
@@ -199,43 +200,60 @@ class DeviceAvailability extends BaseExtension {
199
200
  }
200
201
  try {
201
202
  if (!this.pingCluster || !this.pingCluster.hasOwnProperty('id')) {
202
- this.debug(`Pinging '${ieeeAddr}' (${device.modelID}) via ZH Ping`)
203
+ const message = `Pinging '${ieeeAddr}' (${device.modelID}) via ZH Ping`
204
+ if (has_elevated_debug) {
205
+ this.zigbee.emit('device_debug', { ID:debugID, data: { ID: device.ieeeAddr, flag:'PI', IO:false, states:[{ id:'ping', value: 'zh_default', payload: { method:'default'}}]}, message:message});
206
+ }
207
+ else this.debug(message)
203
208
  await device.ping();
204
209
  }
205
210
  else {
206
211
  const zclData = {};
207
212
  zclData[this.pingCluster.attribute] = {};
208
- this.debug(`Pinging '${ieeeAddr}' (${device.modelID}) via ZCL Read with ${this.pingCluster.id}:${this.pingCluster.attribute}`)
213
+ const message = `Pinging '${ieeeAddr}' (${device.modelID}) via ZCL Read with ${this.pingCluster.id}:${this.pingCluster.attribute}`
214
+ if (has_elevated_debug) {
215
+ this.zigbee.emit('device_debug', { ID:debugID, data: { ID: device.ieeeAddr, flag:'PI', states:[{id:'ping', value:'cluster/id', payload: { method: 'custom', cluster:this.pingCluster.id, command:'read', zcl:zclData}}], IO:false}, message:message});
216
+ }
217
+ else this.debug(message)
209
218
  await this.zigbee.publish(device, this.pingCluster.id, 'read', zclData, null, undefined, 'foundation');
210
219
  }
211
- this.publishAvailability(device, true);
212
- if (has_elevated_debug) this.warn(`ELEVATED : Successfully pinged ${ieeeAddr} (${device.modelID}) in ${Date.now()-pt} ms`);
220
+ this.publishAvailability(device, true, false, has_elevated_debug ? debugID : undefined);
221
+ if (has_elevated_debug) {
222
+ const message = `Successfully pinged ${ieeeAddr} (${device.modelID}) in ${Date.now()-debugID} ms`
223
+ this.zigbee.emit('device_debug', { ID:debugID, data: {ID: device.ieeeAddr, flag:'SUCCESS', IO:false}, message:message});
224
+ }
213
225
  this.setTimerPingable(device, 1);
214
226
  this.ping_counters[device.ieeeAddr].failed = 0;
215
227
  } catch (error) {
216
228
  if (error && error.message && error.message.includes('UNSUPPORTED_ATTRIBUTE')) {
217
229
  // this error is acceptable, as it is raised off an answer of the device.
218
230
  this.publishAvailability(device, true);
219
- if (has_elevated_debug) this.warn(`ELEVATED : Successfully pinged ${ieeeAddr} (${device.modelID}) in ${Date.now()-pt} ms`);
231
+ if (has_elevated_debug)
232
+ this.zigbee.emit('device_debug', { ID:debugID, data: {ID: device.ieeeAddr, flag:'SUCCESS', IO:false}, message:`Successfully pinged ${ieeeAddr} (${device.modelID}) in ${Date.now()-debugID} ms`});
220
233
  this.setTimerPingable(device, 1);
221
234
  this.ping_counters[device.ieeeAddr].failed = 0;
222
235
  return;
223
236
  }
224
- if (has_elevated_debug) this.warn(`ELEVATED : Failed to ping ${ieeeAddr} (${device.modelID}) after ${Date.now()-pt} ms${error && error.message ? ' - '+error.message : ''}`);
225
- this.publishAvailability(device, false);
237
+ if (has_elevated_debug)
238
+ this.zigbee.emit('device_debug',{ ID:debugID, data: {ID: device.ieeeAddr, error:'PIFAIL', IO:false}, message:`Failed to ping ${ieeeAddr} (${device.modelID}) after ${Date.now()-debugID} ms${error && error.message ? ' - '+error.message : ''}`});
239
+ this.publishAvailability(device, false, false, has_elevated_debug ? debugID : undefined);
226
240
  if (pingCount.failed++ <= this.max_ping) {
227
- const msg = `Failed to ping ${ieeeAddr} ${device.modelID} for ${JSON.stringify(pingCount)} attempts`
241
+ const message = `Failed to ping ${ieeeAddr} ${device.modelID} for ${JSON.stringify(pingCount)} attempts`
228
242
  if (pingCount.failed < 2 && pingCount.reported < this.max_ping) {
229
- if (has_elevated_debug) this.warn(`ELEVATED: ${msg}`); else this.info(msg);
243
+ if (!has_elevated_debug)
244
+ this.info(message);
230
245
  pingCount.reported++;
231
246
  } else {
232
- this.debug(msg);
247
+ this.debug(message);
233
248
  }
234
249
  this.setTimerPingable(device, pingCount.failed);
235
250
  this.ping_counters[device.ieeeAddr] = pingCount;
236
251
  } else {
237
252
  const msg = `Stopping to ping ${ieeeAddr} ${device.modelID} after ${pingCount.failed} ping attempts`;
238
- if (has_elevated_debug) this.warn(`ELEVATED ${msg}`); else this.info(msg);
253
+ if (has_elevated_debug)
254
+ this.zigbee.emit('device_debug',{ ID:debugID, data: {ID: device.ieeeAddr, error:'PISTOP', IO:false}, message:msg});
255
+ else
256
+ this.info(msg);
239
257
  }
240
258
  }
241
259
  }
@@ -301,6 +319,9 @@ class DeviceAvailability extends BaseExtension {
301
319
  }
302
320
 
303
321
  async publishAvailability(device, available, force) {
322
+ // no device availability until the interview is done.
323
+ if (device?.interviewState == 'IN_PROGRESS')
324
+ return;
304
325
  const entity = await this.zigbee.resolveEntity(device);
305
326
  if (entity && entity.mapped) {
306
327
  const ieeeAddr = device.ieeeAddr;
@@ -308,13 +329,21 @@ class DeviceAvailability extends BaseExtension {
308
329
  this.onReconnect(device);
309
330
  }
310
331
 
311
- if (this.state[ieeeAddr] !== available || force) {
312
- this.state[ieeeAddr] = available;
332
+ const astate = this.state[ieeeAddr] || { available, ts:0 };
333
+ const now = Math.round(Date.now()/1000);
334
+
335
+ if (force || (astate.available !== available) || (now - astate.ts > this.availableTime)) {
336
+ this.state[ieeeAddr] = {
337
+ ts: now,
338
+ available
339
+ }
313
340
  const payload = {available: available};
314
341
  this.debug(`Publish available for ${ieeeAddr} = ${available}`);
315
342
  this.zigbee.emit('publish', utils.zbIdorIeeetoAdId(this.adapter, ieeeAddr, false), entity.mapped.model, payload);
316
- this.debug(`Publish LQ for ${ieeeAddr} = ${(available ? 10 : 0)}`);
317
- this.zigbee.emit('publish', utils.zbIdorIeeetoAdId(this.adapter, ieeeAddr, false), entity.mapped.model, {linkquality: (available ? 10 : 0)});
343
+ if (force || (astate.available !== available)) {
344
+ this.debug(`Publish LQ for ${ieeeAddr} = ${(available ? 10 : 0)}`);
345
+ this.zigbee.emit('publish', utils.zbIdorIeeetoAdId(this.adapter, ieeeAddr, false), entity.mapped.model, {linkquality: (available ? 10 : 0)});
346
+ }
318
347
  }
319
348
  }
320
349
  }
@@ -34,6 +34,10 @@ class DeviceEvent extends BaseExtension {
34
34
  }
35
35
  }
36
36
 
37
+ async deviceExposeChanged(device, mapped) {
38
+ this.warn(`deviceExposeChanged called with ${JSON.stringify(device.ieeeAddr)} / ${JSON.stringify(mapped.model)}`);
39
+ }
40
+
37
41
  async callOnEvent(device, type, data, mappedDevice) {
38
42
  if (!mappedDevice) {
39
43
  mappedDevice = await zigbeeHerdsmanConverters.findByDevice(device);
@@ -52,6 +56,7 @@ class DeviceEvent extends BaseExtension {
52
56
  case `deviceJoined`:
53
57
  {
54
58
  eventData.data = baseData;
59
+ eventData.deviceExposeChanged = function() { this.deviceExposeChanged(device, mappedDevice) } ;
55
60
  break;
56
61
  }
57
62
  case 'stop':
@@ -657,7 +657,7 @@ class ZigbeeController extends EventEmitter {
657
657
  mapped = await zigbeeHerdsmanConverters.findByDevice(device, false);
658
658
  }
659
659
  catch (error) {
660
-
660
+ // intentionally empty
661
661
  }
662
662
  if (!mapped) {
663
663
  if (device.type === 'Coordinator')
@@ -668,7 +668,7 @@ class ZigbeeController extends EventEmitter {
668
668
  endpoint: device.getEndpoint(1),
669
669
  name: 'Coordinator',
670
670
  };
671
- this.emit('stash_unknown_model', `resoveEntity${device.ieeeAddr}`,`Resolve Entity did not manage to find a mapped device for ${device.ieeeAddr} of type ${device.modelID}`);
671
+ if (device.interviewState != 'IN_PROGRESS') this.emit('stash_unknown_model', `resoveEntity${device.ieeeAddr}`,`Resolve Entity did not manage to find a mapped device for ${device.ieeeAddr} of type ${device.modelID}`);
672
672
  }
673
673
  const endpoints = mapped && mapped.endpoint ? mapped.endpoint(device) : null;
674
674
  let endpoint;
@@ -812,7 +812,7 @@ class ZigbeeController extends EventEmitter {
812
812
  else if (this._permitJoinInterval) {
813
813
  const timestr = this._permitJoinTime > 0 ? ` with ${this._permitJoinTime} second${this._permitJoinTime > 1 ? 's':''} remaining.`: '.';
814
814
  this.info(`Closed Zigbee network${timestr}`)
815
- this.emit('pairing', `Closed network${timestr}`);
815
+ this.emit('pairing', `Closed network${timestr}`, 0);
816
816
  clearInterval(this._permitJoinInterval);
817
817
  this._permitJoinInterval = null;
818
818
  }
@@ -906,11 +906,6 @@ class ZigbeeController extends EventEmitter {
906
906
  if (this.adapter.stController.checkDebugDevice(friendlyName)) {
907
907
  this.emit('device_debug', {ID: Date.now(), data: {flag:'da', states:[{id: '--', value:'--', payload:message}] , IO:true} ,message:`Device '${friendlyName}' announced itself`});
908
908
  }
909
- if (this.warnOnDeviceAnnouncement) {
910
- this.warn(`Device '${friendlyName}' announced itself${this.readAtAnnounce ? ', trying to read its status' : ''}`);
911
- } else {
912
- this.info(`Device '${friendlyName}' announced itself${this.readAtAnnounce ? ', trying to read its status' : ''}`);
913
- }
914
909
 
915
910
  if (entity.device && entity.device.modelID && entity.device.interviewState != 'SUCCESSFUL') {
916
911
  this.info(`ignoring device announcement for ${entity.device.modelID} due to interview state ${entity.device.interviewState}`);
@@ -918,6 +913,12 @@ class ZigbeeController extends EventEmitter {
918
913
  return;
919
914
  }
920
915
 
916
+ if (this.warnOnDeviceAnnouncement) {
917
+ this.warn(`Device '${friendlyName}' announced itself${this.readAtAnnounce ? ', trying to read its status' : ''}`);
918
+ } else {
919
+ this.info(`Device '${friendlyName}' announced itself${this.readAtAnnounce ? ', trying to read its status' : ''}`);
920
+ }
921
+
921
922
  const networkOpen = this.herdsman.getPermitJoin();
922
923
  /*
923
924
  if (networkOpen && entity.device && entity.device.modelID && entity.device.interviewState != 'IN_PROGRESS')
@@ -929,11 +930,16 @@ class ZigbeeController extends EventEmitter {
929
930
  */
930
931
  try {
931
932
  if (entity && entity.mapped) {
933
+ if (entity.options?.hasOwnProperty('resend_states')) {
934
+ // trigger setting the states
935
+ this.emit('resend_states',entity, this.readAtAnnounce );
936
+ }
937
+ else
938
+ if (this.readAtAnnounce) await this.doDeviceQuery(message.device || message.ieeeAddr, Date.now(), false);
932
939
  this.callExtensionMethod(
933
940
  'onZigbeeEvent',
934
941
  [{'device': message.device, 'type': 'deviceAnnounce', options: entity.options || {}}, entity ? entity.mapped : null]);
935
942
  this.callExtensionMethod('registerDevicePing', [message.device, entity]);
936
- if (this.readAtAnnounce) await this.doDeviceQuery(message.device || message.ieeeAddr, Date.now(), false);
937
943
  }
938
944
  } catch (error) {
939
945
  this.sendError(error);
@@ -1346,38 +1352,49 @@ class ZigbeeController extends EventEmitter {
1346
1352
  meta.state = preparedOptions.state;
1347
1353
  }
1348
1354
  }
1349
-
1350
- try {
1351
- const result = await converter.convertSet(target, key, preparedValue, meta);
1352
- const message = `convert result ${safeJsonStringify(result)} for device ${deviceId}`;
1353
- if (isGroup)
1354
- this.emit('published', deviceId, model, stateModel, stateList, options, debugID, has_elevated_debug );
1355
- if (has_elevated_debug) {
1356
- this.emit('device_debug', { ID:debugID, data: { flag: 'SUCCESS' , IO:false }, message:message});
1357
- }
1358
- else
1359
- if (this.debugActive) this.debug(message);
1360
- if (result !== undefined) {
1361
- if (stateModel && !isGroup && !stateDesc.noack) {
1362
- this.emit('acknowledge_state', deviceId, model, stateDesc, value );
1355
+ let retry = 2;
1356
+ do
1357
+ {
1358
+ try {
1359
+ const result = await converter.convertSet(target, key, preparedValue, meta);
1360
+ const message = `convert result ${safeJsonStringify(result)} for device ${deviceId}`;
1361
+ if (isGroup)
1362
+ this.emit('published', deviceId, model, stateModel, stateList, options, debugID, has_elevated_debug );
1363
+ if (has_elevated_debug) {
1364
+ this.emit('device_debug', { ID:debugID, data: { flag: 'SUCCESS' , IO:false }, message:message});
1363
1365
  }
1364
- // process sync state list
1365
- this.processSyncStatesList(deviceId, model, syncStateList);
1366
- }
1367
- else {
1366
+ else
1367
+ if (this.debugActive) this.debug(message);
1368
+ if (result !== undefined) {
1369
+ if (stateModel && !isGroup && !stateDesc.noack) {
1370
+ this.emit('acknowledge_state', deviceId, model, stateDesc, value );
1371
+ }
1372
+ // process sync state list
1373
+ this.processSyncStatesList(deviceId, model, syncStateList);
1374
+ }
1375
+ else {
1376
+ if (has_elevated_debug) {
1377
+ const message = `Convert does not return a result result for ${key} with ${safeJsonStringify(preparedValue)} on device ${deviceId}.`;
1378
+ this.emit('device_debug', { ID:debugID, data: { flag: '06' , IO:false }, message:message});
1379
+ }
1380
+ }
1381
+ retry = 0;
1382
+ } catch (error) {
1368
1383
  if (has_elevated_debug) {
1369
- const message = `Convert does not return a result result for ${key} with ${safeJsonStringify(preparedValue)} on device ${deviceId}.`;
1370
- this.emit('device_debug', { ID:debugID, data: { flag: '06' , IO:false }, message:message});
1384
+ const message = `caught error ${error?.message? error.message : 'no reason given'} when setting value for device ${deviceId}.`;
1385
+ this.emit('device_debug', { ID:debugID, data: { error: `EXSET${error.code == 25 ? retry : ''}` , IO:false },message:message});
1386
+ }
1387
+ if (error.code === 25 && retry > 0) {
1388
+ this.warn(`Error ${error.code} on send command to ${deviceId}. (${retry} tries left.), Error: ${error.message}`);
1389
+ retry--;
1390
+ }
1391
+ else {
1392
+ retry = 0;
1393
+ this.adapter.filterError(`Error ${error.code} on send command to ${deviceId}.` +
1394
+ ` Error: ${error.message}`, `Send command to ${deviceId} failed with`, error);
1371
1395
  }
1372
1396
  }
1373
- } catch (error) {
1374
- if (has_elevated_debug) {
1375
- const message = `caught error ${error && error.message ? error.message : 'no reason given'} when setting value for device ${deviceId}.`;
1376
- this.emit('device_debug', { ID:debugID, data: { error: 'EXSET' , IO:false },message:message});
1377
- }
1378
- this.adapter.filterError(`Error ${error.code} on send command to ${deviceId}.` +
1379
- ` Error: ${error.stack}`, `Send command to ${deviceId} failed with`, error);
1380
- }
1397
+ } while (retry > 0)
1381
1398
  });
1382
1399
  } catch (err) {
1383
1400
  const message = `No entity for ${deviceId} : ${err && err.message ? err.message : 'no error message'}`;
@@ -1506,8 +1523,11 @@ class ZigbeeController extends EventEmitter {
1506
1523
  if (this.debugActive) this.debug(`Device query for '${entity.device.ieeeAddr}' started`);
1507
1524
  else this.info(`Device query for '${entity.device.ieeeAddr}' started`);
1508
1525
 
1526
+ const payload = { key:'device_query', read_states:[], unread_states:[] };
1527
+ let cCount = 0;
1509
1528
  for (const converter of mappedModel.toZigbee) {
1510
1529
  if (converter.hasOwnProperty('convertGet')) {
1530
+ cCount++;
1511
1531
  const sources = [];
1512
1532
  if (converter.endpoints && epmap) {
1513
1533
  for (const epname of converter.endpoints) {
@@ -1517,24 +1537,33 @@ class ZigbeeController extends EventEmitter {
1517
1537
  }
1518
1538
  if (sources.length == 0) sources.push(entity.device.endpoints[0]);
1519
1539
  for (const source of sources) {
1520
- try {
1521
- await converter.convertGet(source, '', {device:entity.device});
1522
- this.debug(`read for state${converter.key.length ? '' : 's'} '${converter.key.join(',')}' of '${entity.device.ieeeAddr}/${source.ID}' after device query`);
1523
- } catch (error) {
1524
- if (elevated) {
1525
- const message = `Failed to read for state${converter.key.length ? '' : 's'} '${converter.key.join(',')}' of '${source.ID}' from query with '${error && error.message ? error.message : 'no error message'}`;
1526
- this.warn(`ELEVATED OE02.1 ${message}`);
1527
- this.emit('device_debug', { ID:debugID, data: { error: 'NOTREAD' , IO:false }, message:message });
1540
+ for (const k of converter.key)
1541
+ try {
1542
+ await converter.convertGet(source, k, {device:entity.device});
1543
+ payload.read_states.push(`${cCount}.${k}`);
1544
+ if (elevated) {
1545
+ const message = `read for state ${k} of '${converter.key.join(',')}' of '${entity.device.ieeeAddr}/${source.ID}' after device query`;
1546
+ this.warn(`ELEVATED O02.1 ${message}`);
1547
+ this.emit('device_debug', { ID:debugID, data: { flag: '03', IO:false }, message:message });
1548
+ }
1549
+ else
1550
+ this.debug(`read for state${converter.key.length ? '' : 's'} '${converter.key.join(',')}' of '${entity.device.ieeeAddr}/${source.ID}' after device query`);
1551
+ } catch (error) {
1552
+ payload.unread_states.push(`${cCount}.${k}`);
1553
+ if (elevated) {
1554
+ const message = `Failed to read for state ${k} of '${converter.key.join(',')}' of '${source.ID}' from query with '${error && error.message ? error.message : 'no error message'}`;
1555
+ this.warn(`ELEVATED OE02.1 ${message}`);
1556
+ this.emit('device_debug', { ID:debugID, data: { error: 'NOTREAD' , IO:false }, message:message });
1557
+ }
1558
+ else
1559
+ this.debug(`failed to read for state${converter.key.length ? '' : 's'} '${converter.key.join(',')}' of '${source.ID}'after device query`);
1528
1560
  }
1529
- else
1530
- this.debug(`failed to read for state${converter.key.length ? '' : 's'} '${converter.key.join(',')}' of '${source.ID}'after device query`);
1531
- }
1532
1561
  }
1533
1562
  }
1534
1563
  }
1535
1564
  if (elevated) {
1536
1565
  const message = `ELEVATED O07: Device query for '${entity.device.ieeeAddr}}' complete`;
1537
- this.emit('device_debug', { ID:debugID, data: { flag: 'qe' , IO:false }, message:message});
1566
+ this.emit('device_debug', { ID:debugID, data: { flag: 'qe' , IO:false , payload }, message:message});
1538
1567
  }
1539
1568
  else
1540
1569
  this.info(`Device query for '${entity.device.ieeeAddr}' complete`);
package/main.js CHANGED
@@ -216,8 +216,6 @@ class Zigbee extends utils.Adapter {
216
216
  const zigbeeOptions = this.getZigbeeOptions();
217
217
  this.zbController = new ZigbeeController(this);
218
218
 
219
-
220
-
221
219
  this.zbController.on('log', this.onLog.bind(this));
222
220
  this.zbController.on('ready', this.onZigbeeAdapterReady.bind(this));
223
221
  this.zbController.on('disconnect', this.onZigbeeAdapterDisconnected.bind(this));
@@ -229,6 +227,7 @@ class Zigbee extends utils.Adapter {
229
227
  this.zbController.on('publish', this.stController.publishToState.bind(this.stController));
230
228
  this.stController.on('send_payload', this.zbController.publishPayload.bind(this.zbController));
231
229
  this.stController.on('changed', this.zbController.publishFromState.bind(this.zbController));
230
+ this.zbController.on('resend_states', this.stController.handleStateReset.bind(this.stController));
232
231
  this.stController.on('device_query', this.zbController.deviceQuery.bind(this.zbController));
233
232
  this.zbController.on('acknowledge_state', this.acknowledgeState.bind(this));
234
233
  this.zbController.on('stash_error', this.stController.stashErrors.bind(this.stController));
@@ -247,7 +246,7 @@ class Zigbee extends utils.Adapter {
247
246
  this.log.info('Autostart Zigbee subsystem');
248
247
  this.doConnect();
249
248
  }
250
- else this.log.warn('Zigbee autostart option not set - omitting start of zigbee substystem!');
249
+ else this.log.warn('Zigbee autostart option not set - omitting start of zigbee subsystem!');
251
250
  }
252
251
  updateDebugLevel(state) {
253
252
  const dbActive = state === 'debug';
@@ -833,6 +832,7 @@ class Zigbee extends utils.Adapter {
833
832
  disableBackup: this.config.disableBackup,
834
833
  extPanIdFix: extPanIdFix,
835
834
  startWithInconsistent: override.startWithInconsistent ? override.startWithInconsistent: this.config.startWithInconsistent || false,
835
+ availableUpdateTime:this.config.availableUpdateTime,
836
836
  };
837
837
  }
838
838
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "iobroker.zigbee",
3
- "version": "3.2.1",
3
+ "version": "3.2.3",
4
4
  "author": {
5
5
  "name": "Kirov Ilya",
6
6
  "email": "kirovilya@gmail.com"
@@ -29,7 +29,7 @@
29
29
  "uri-js": "^4.4.1",
30
30
  "typescript": "^5.9.2",
31
31
  "zigbee-herdsman": "^6.0.0",
32
- "zigbee-herdsman-converters": "^25.37.0"
32
+ "zigbee-herdsman-converters": "^25.50.0"
33
33
  },
34
34
  "description": "Zigbee devices",
35
35
  "devDependencies": {