iobroker.zigbee 2.0.4 → 2.0.5

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/main.js CHANGED
@@ -36,6 +36,7 @@ const zigbeeHerdsmanPackage = require('zigbee-herdsman/package.json')
36
36
  const vm = require('vm');
37
37
  const util = require('util');
38
38
  const dmZigbee = require('./lib/devicemgmt.js');
39
+ const DeviceDebug = require('./lib/DeviceDebug');
39
40
 
40
41
  const createByteArray = function (hexString) {
41
42
  const bytes = [];
@@ -79,6 +80,9 @@ class Zigbee extends utils.Adapter {
79
80
  this.stController.on('changed', this.publishFromState.bind(this));
80
81
 
81
82
  this.deviceManagement = new dmZigbee(this);
83
+ this.deviceDebug = new DeviceDebug(this),
84
+ this.deviceDebug.on('log', this.onLog.bind(this));
85
+
82
86
 
83
87
  this.plugins = [
84
88
  new SerialListPlugin(this),
@@ -115,6 +119,19 @@ class Zigbee extends utils.Adapter {
115
119
  }
116
120
  }
117
121
 
122
+ warn(message) {
123
+ this.log.warn(message);
124
+ }
125
+ debug(message) {
126
+ this.log.debug(message);
127
+ }
128
+ error(message) {
129
+ this.log.error(message);
130
+ }
131
+ info(message) {
132
+ this.log.info(message);
133
+ }
134
+
118
135
  sendError(error, message) {
119
136
  try {
120
137
  if (this.supportsFeature && this.supportsFeature('PLUGINS')) {
@@ -210,12 +227,15 @@ class Zigbee extends utils.Adapter {
210
227
  this.zbController.on('new', this.newDevice.bind(this));
211
228
  this.zbController.on('leave', this.leaveDevice.bind(this));
212
229
  this.zbController.on('pairing', this.onPairing.bind(this));
213
- this.zbController.on('event', this.onZigbeeEvent.bind(this));
214
- this.zbController.on('msg', this.onZigbeeEvent.bind(this));
230
+ this.zbController.on('event', this.stController.onZigbeeEvent.bind(this.stController));
231
+ this.zbController.on('msg', this.stController.onZigbeeEvent.bind(this.stController));
215
232
  this.zbController.on('publish', this.publishToState.bind(this));
216
233
  this.zbController.configure(zigbeeOptions);
217
234
  await this.callPluginMethod('configure', [zigbeeOptions]);
218
235
 
236
+ // elevated debug handling
237
+ this.deviceDebug.start(this.stController, this.zbController);
238
+
219
239
  this.reconnectCounter = 1;
220
240
  this.doConnect();
221
241
  }
@@ -287,7 +307,7 @@ class Zigbee extends utils.Adapter {
287
307
  require,
288
308
  module: {},
289
309
  };
290
- const mN = (fs.existsSync(moduleName) ? moduleName : this.expandFileName(moduleName).replace('.', '_'));
310
+ const mN = (fs.existsSync(moduleName) ? moduleName : this.expandFileName(moduleName).replace('zigbee.', 'zigbee_'));
291
311
  if (!fs.existsSync(mN)) {
292
312
  this.log.warn(`External converter not loaded - neither ${moduleName} nor ${mN} exist.`);
293
313
  }
@@ -353,25 +373,15 @@ class Zigbee extends utils.Adapter {
353
373
  const toAdd = {...definition};
354
374
  delete toAdd['homeassistant'];
355
375
  try {
376
+ const t = Date.now();
356
377
  if (zigbeeHerdsmanConverters.hasOwnProperty('addExternalDefinition')) {
357
378
  zigbeeHerdsmanConverters.addExternalDefinition(toAdd);
358
- this.log.info('added external converter using addExternalDefinition')
379
+ this.log.info(`added external converter using addExternalDefinition (${Date.now()-t} ms)`)
359
380
  }
360
381
  else if (zigbeeHerdsmanConverters.hasOwnProperty('addDefinition')) {
361
382
  zigbeeHerdsmanConverters.addDefinition(toAdd);
362
- this.log.info('added external converter using addDefinition')
383
+ this.log.info(`added external converter using addDefinition (${Date.now()-t} ms)`);
363
384
  }
364
-
365
- /*
366
- for (const zigbeeModel of toAdd.zigbeeModel)
367
- {
368
- try {
369
- zigbeeHerdsmanConverters.addToExternalDefinitionsLookup(zigbeeModel, toAdd.toAdd);
370
- } catch (e) {
371
- this.log.error(`unable to apply external converter ${JSON.stringify(toAdd)} for device ${zigbeeModel}: ${e && e.message ? e.message : 'no error message available'}`);
372
- }
373
- }
374
- */
375
385
  } catch (e) {
376
386
  this.log.error(`unable to apply external converter for ${JSON.stringify(toAdd.model)}: ${e && e.message ? e.message : 'no error message available'}`);
377
387
  }
@@ -510,7 +520,6 @@ class Zigbee extends utils.Adapter {
510
520
  for (const device of devicesFromDB) {
511
521
  const entity = await this.zbController.resolveEntity(device);
512
522
  if (entity) {
513
- // this.log.warn('sync dev states for ' + (entity.mapped ? entity.mapped.model : entity.device.modelID));
514
523
  const model = entity.mapped ? entity.mapped.model : entity.device.modelID;
515
524
  this.stController.updateDev(device.ieeeAddr.substr(2), model, model, () =>
516
525
  this.stController.syncDevStates(device, model));
@@ -553,154 +562,6 @@ class Zigbee extends utils.Adapter {
553
562
  });
554
563
  }
555
564
 
556
- async onZigbeeEvent(type, entity, message) {
557
- this.log.debug(`Type ${type} device ${safeJsonStringify(entity)} incoming event: ${safeJsonStringify(message)}`);
558
-
559
- const device = entity.device;
560
- const mappedModel = entity.mapped;
561
- const model = entity.mapped ? entity.mapped.model : entity.device.modelID;
562
- const cluster = message.cluster;
563
- const devId = device.ieeeAddr.substr(2);
564
- const meta = {device};
565
-
566
- const has_elevated_debug = this.stController.checkDebugDevice(devId);
567
-
568
- if (has_elevated_debug) {
569
- const shortMessage = {};
570
- for(const propertyName in message) {
571
- shortMessage[propertyName] = message[propertyName];
572
- }
573
- shortMessage.device = device.ieeeAddr;
574
- shortMessage.meta = undefined;
575
- shortMessage.endpoint = (message.endpoint.ID ? message.endpoint.ID: -1);
576
- this.log.warn(`ELEVATED I00: Zigbee Event of Type ${type} from device ${safeJsonStringify(device.ieeeAddr)}, incoming event: ${safeJsonStringify(shortMessage)}`);
577
- }
578
- // this assigment give possibility to use iobroker logger in code of the converters, via meta.logger
579
- meta.logger = this.log;
580
-
581
- await this.checkIfModelUpdate(entity);
582
-
583
- let _voltage = 0;
584
- let _temperature = 0;
585
- let _humidity = 0;
586
-
587
- let isMessure = false;
588
- let isBattKey = false;
589
-
590
- if (mappedModel && mappedModel.meta && mappedModel.meta.battery) {
591
- const isVoltage = mappedModel.meta.battery.hasOwnProperty('voltageToPercentage');
592
-
593
- if (isVoltage) {
594
- const keys = Object.keys(message.data);
595
-
596
- for (const key of keys) {
597
- const value = message.data[key];
598
-
599
- if (value && value[1]) {
600
- if (key == 65282 && value[1][1]) {
601
- _voltage = value[1][1].elmVal;
602
- isBattKey = true;
603
- break;
604
- }
605
- if (key == 65281) {
606
- _voltage = value[1];
607
- isBattKey = true;
608
- _temperature = value[100];
609
- _temperature = _temperature /100;
610
- _humidity = value[101];
611
- _humidity = _humidity / 100;
612
- isMessure = true;
613
- break;
614
- }
615
- }
616
- }
617
- }
618
- }
619
-
620
- // always publish link_quality and battery
621
- if (message.linkquality) { // send battery with
622
- this.publishToState(devId, model, {linkquality: message.linkquality});
623
- if (isBattKey) {
624
- this.publishToState(devId, model, {voltage: _voltage});
625
- const battProz = zigbeeHerdsmanConvertersUtils.batteryVoltageToPercentage(_voltage,entity.mapped.meta.battery.voltageToPercentage);
626
- this.publishToState(devId, model, {battery: battProz});
627
- }
628
- if (isMessure) {
629
- this.publishToState(devId, model, {temperature: _temperature});
630
- this.publishToState(devId, model, {humidity: _humidity});
631
- }
632
- }
633
-
634
- // publish raw event to "from_zigbee"
635
- // some cleanup
636
- const msgForState = Object.assign({}, message);
637
- delete msgForState['device'];
638
- delete msgForState['endpoint'];
639
-
640
- msgForState['endpoint_id'] = message.endpoint.ID;
641
- this.publishToState(devId, model, {msg_from_zigbee: safeJsonStringify(msgForState)});
642
-
643
- if (!entity.mapped) {
644
- return;
645
- }
646
-
647
- let converters = mappedModel.fromZigbee.filter(c => c && c.cluster === cluster && (
648
- Array.isArray(c.type) ? c.type.includes(type) : c.type === type));
649
-
650
-
651
- if (!converters.length && type === 'readResponse') {
652
- converters = mappedModel.fromZigbee.filter(c => c.cluster === cluster && (
653
- Array.isArray(c.type) ? c.type.includes('attributeReport') : c.type === 'attributeReport'));
654
- }
655
-
656
- if (!converters.length) {
657
- if (type !== 'readResponse') {
658
- this.log.debug(`No converter available for '${mappedModel.model}' '${devId}' with cluster '${cluster}' and type '${type}'`);
659
- if (has_elevated_debug)
660
- this.log.warn(`ELEVATED IE00: No converter available for '${mappedModel.model}' '${devId}' with cluster '${cluster}' and type '${type}'`);
661
- }
662
- return;
663
- }
664
-
665
- meta.state = { state: '' }; // for tuya
666
-
667
- this.processConverters(converters, devId, model, mappedModel, message, meta)
668
- .catch((error) => {
669
- // 'Error: Expected one of: 0, 1, got: 'undefined''
670
- if (cluster !== '64529') {
671
- this.log.error(`Error while processing converters DEVICE_ID: '${devId}' cluster '${cluster}' type '${type}'`);
672
- }
673
- });
674
- }
675
-
676
- async processConverters(converters, devId, model, mappedModel, message, meta) {
677
- for (const converter of converters) {
678
- const publish = (payload) => {
679
- this.log.debug(`Publish ${safeJsonStringify(payload)} to ${safeJsonStringify(devId)}`);
680
- if (typeof payload === 'object') {
681
- this.publishToState(devId, model, payload);
682
- }
683
- };
684
-
685
- const options = await new Promise((resolve, reject) => {
686
- this.stController.collectOptions(devId, model, (options) => {
687
- resolve(options);
688
- });
689
- });
690
-
691
- const payload = await new Promise((resolve, reject) => {
692
- const payloadConv = converter.convert(mappedModel, message, publish, options, meta);
693
- if (typeof payloadConv === 'object') {
694
- resolve(payloadConv);
695
- }
696
- });
697
-
698
- publish(payload);
699
- }
700
- }
701
-
702
-
703
-
704
565
  publishToState(devId, model, payload) {
705
566
  this.stController.publishToState(devId, model, payload);
706
567
  }
@@ -721,15 +582,18 @@ class Zigbee extends utils.Adapter {
721
582
  });
722
583
  }
723
584
 
724
- async publishFromState(deviceId, model, stateModel, stateList, options) {
585
+ async publishFromState(deviceId, model, stateModel, stateList, options, debugID) {
725
586
  let isGroup = false;
726
587
  const has_elevated_debug = this.stController.checkDebugDevice(deviceId)
727
588
 
728
589
  if (has_elevated_debug)
729
590
  {
730
591
  const stateNames = [];
731
- stateList.forEach( state => stateNames.push(state.id));
732
- this.log.warn(`ELEVATED O03: Publishing to ${deviceId} of model ${model} ${stateNames.join(', ')}`);
592
+ for (const state of stateList) {
593
+ stateNames.push(state.stateDesc.id);
594
+ }
595
+ const message = `Publishing to ${deviceId} of model ${model} with ${stateNames.join(', ')}`;
596
+ this.emit('device_debug', { ID:debugID, data: { ID: deviceId, flag: '03', IO:false }, message: message});
733
597
  }
734
598
  else
735
599
  this.log.debug(`publishFromState : ${deviceId} ${model} ${safeJsonStringify(stateList)}`);
@@ -744,7 +608,10 @@ class Zigbee extends utils.Adapter {
744
608
 
745
609
  if (!mappedModel) {
746
610
  this.log.debug(`No mapped model for ${model}`);
747
- if (has_elevated_debug) this.log.error(`ELEVATED OE01: No mapped model ${deviceId} (model ${model})`)
611
+ if (has_elevated_debug) {
612
+ const message=`No mapped model ${deviceId} (model ${model})`;
613
+ this.emit('device_debug', { ID:debugID, data: { error: 'NOMODEL' , IO:false }, message: message});
614
+ }
748
615
  return;
749
616
  }
750
617
 
@@ -761,13 +628,20 @@ class Zigbee extends utils.Adapter {
761
628
  if (stateDesc.id === 'send_payload') {
762
629
  try {
763
630
  const json_value = JSON.parse(value);
764
- const payload = {device: deviceId.replace('0x', ''), payload: json_value};
631
+ const payload = {device: deviceId.replace('0x', ''), payload: json_value, model:model, stateModel:stateModel};
632
+ if (has_elevated_debug) this.emit('device_debug', { ID:debugID, data: { flag: '04' ,payload:value ,states:[{id:stateDesc.id, value:json_value, payload:'none'}], IO:false }});
633
+
765
634
  const result = await this.sendPayload(payload);
766
635
  if (result.hasOwnProperty('success') && result.success) {
767
636
  this.acknowledgeState(deviceId, model, stateDesc, value);
768
637
  }
638
+ else {
639
+ this.error('Error in SendPayload: '+result.error.message);
640
+ }
769
641
  } catch (error) {
770
- this.log.warn(`send_payload: ${value} does not parse as JSON Object : ${error.message}`);
642
+ const message = `send_payload: ${value} does not parse as JSON Object : ${error.message}`;
643
+ if (has_elevated_debug) this.emit('device_debug', { ID:debugID, data: { error: 'EXSEND' ,states:[{id:stateDesc.id, value:value, payload:error.message}], IO:false }, message:message});
644
+ else this.error(message);
771
645
  return;
772
646
  }
773
647
  return;
@@ -775,8 +649,10 @@ class Zigbee extends utils.Adapter {
775
649
 
776
650
  if (stateDesc.isOption || stateDesc.compositeState) {
777
651
  // acknowledge state with given value
778
- if (has_elevated_debug)
779
- this.log.warn('ELEVATED OC: changed state: ' + JSON.stringify(changedState));
652
+ if (has_elevated_debug) {
653
+ const message = 'changed state: ' + JSON.stringify(changedState);
654
+ this.emit('device_debug', { ID:debugID, data: { flag: 'cc', states:[{id:stateDesc.id, value:value, payload:'none (OC State)'}] , IO:false }, message:message});
655
+ }
780
656
  else
781
657
  this.log.debug('changed composite state: ' + JSON.stringify(changedState));
782
658
 
@@ -784,20 +660,18 @@ class Zigbee extends utils.Adapter {
784
660
  if (stateDesc.compositeState && stateDesc.compositeTimeout) {
785
661
  this.stController.triggerComposite(deviceId, model, stateDesc, changedState.source.includes('.admin.'));
786
662
  }
787
- // process sync state list
788
- //this.processSyncStatesList(deviceId, modelId, syncStateList);
789
- // if this is the device query state => trigger the device query
790
-
791
663
  // on activation of the 'device_query' state trigger hardware query where possible
792
664
  if (stateDesc.id === 'device_query') {
793
665
  if (this.query_device_block.indexOf(deviceId) > -1) {
794
- this.log.warn(`Device query for '${entity.device.ieeeAddr}' blocked`);
666
+ this.log.info(`Device query for '${entity.device.ieeeAddr}' blocked`);
795
667
  return;
796
668
  }
797
669
  if (mappedModel) {
798
670
  this.query_device_block.push(deviceId);
799
- if (has_elevated_debug)
800
- this.log.warn(`ELEVATED O06: Device query for '${entity.device.ieeeAddr}/${entity.device.endpoints[0].ID}' triggered`);
671
+ if (has_elevated_debug) {
672
+ const message = `Device query for '${entity.device.ieeeAddr}/${entity.device.endpoints[0].ID}' triggered`;
673
+ this.emit('device_debug', { ID:debugID, data: { flag: 'qs' ,states:[{id:stateDesc.id, value:value, payload:'none for device query'}], IO:false }, message:message});
674
+ }
801
675
  else
802
676
  this.log.debug(`Device query for '${entity.device.ieeeAddr}' started`);
803
677
  for (const converter of mappedModel.toZigbee) {
@@ -807,7 +681,9 @@ class Zigbee extends utils.Adapter {
807
681
  await converter.convertGet(entity.device.endpoints[0], ckey, {});
808
682
  } catch (error) {
809
683
  if (has_elevated_debug) {
810
- this.log.warn(`ELEVATED OE02.1 Failed to read state '${JSON.stringify(ckey)}'of '${entity.device.ieeeAddr}/${entity.device.endpoints[0].ID}' from query with '${error && error.message ? error.message : 'no error message'}`);
684
+ const message = `Failed to read state '${JSON.stringify(ckey)}'of '${entity.device.ieeeAddr}/${entity.device.endpoints[0].ID}' from query with '${error && error.message ? error.message : 'no error message'}`;
685
+ this.log.warn(`ELEVATED OE02.1 ${message}`);
686
+ this.emit('device_debug', { ID:debugID, data: { error: 'NOTREAD' , IO:false }, message:message });
811
687
  }
812
688
  else
813
689
  this.log.info(`failed to read state ${JSON.stringify(ckey)} of ${entity.device.ieeeAddr}/${entity.device.endpoints[0].ID} after device query`);
@@ -815,8 +691,10 @@ class Zigbee extends utils.Adapter {
815
691
  }
816
692
  }
817
693
  }
818
- if (has_elevated_debug)
819
- this.log.warn(`ELEVATED O07: Device query for '${entity.device.ieeeAddr}/${entity.device.endpoints[0].ID}' complete`);
694
+ if (has_elevated_debug) {
695
+ const message = `ELEVATED O07: Device query for '${entity.device.ieeeAddr}/${entity.device.endpoints[0].ID}' complete`;
696
+ this.emit('device_debug', { ID:debugID, data: { flag: 'qe' , IO:false }, message:message});
697
+ }
820
698
  else
821
699
  this.log.info(`Device query for '${entity.device.ieeeAddr}' done`);
822
700
  const idToRemove = deviceId;
@@ -840,38 +718,51 @@ class Zigbee extends utils.Adapter {
840
718
  this.log.debug(`Type of toZigbee is '${typeof c}', Contains key ${(c.hasOwnProperty('key')?JSON.stringify(c.key):'false ')}`)
841
719
  if (!c.hasOwnProperty('key'))
842
720
  {
843
- if (c.hasOwnProperty('convertSet') && converter === undefined)
721
+ if (converter === undefined)
844
722
  {
845
723
  converter = c;
846
- if (has_elevated_debug)
847
- this.log.warn(`ELEVATED O04.${msg_counter}: Setting converter to keyless converter for ${deviceId} of type ${model}`)
724
+ if (has_elevated_debug) {
725
+ const message = `Setting converter to keyless converter for ${deviceId} of type ${model}`;
726
+ this.emit('device_debug', { ID:debugID, data: { flag: `s4.${msg_counter}` , IO:false }, message:message});
727
+ }
848
728
  else
849
- this.log.debug(`Setting converter to keyless converter for ${deviceId} of type ${model}`)
729
+ this.log.debug(`Setting converter to keyless converter for ${deviceId} of type ${model}`);
850
730
  msg_counter++;
851
731
  }
852
732
  else
853
733
  {
854
734
  if (has_elevated_debug)
855
- this.log.warn(`ELEVATED O04.${msg_counter}: ignoring keyless converter for ${deviceId} of type ${model}`)
735
+ {
736
+ const message = `ignoring keyless converter for ${deviceId} of type ${model}`;
737
+ this.emit('device_debug', { ID:debugID, data: { flag: `i4.${msg_counter}` , IO:false} , message:message});
738
+ }
856
739
  else
857
- this.log.debug(`ignoring keyless converter for ${deviceId} of type ${model}`)
740
+ this.log.debug(`ignoring keyless converter for ${deviceId} of type ${model}`);
858
741
  msg_counter++;
859
742
  }
860
743
  continue;
861
744
  }
862
745
  if (c.key.includes(stateDesc.prop) || c.key.includes(stateDesc.setattr) || c.key.includes(stateDesc.id))
863
746
  {
864
- if (has_elevated_debug)
865
- this.log.warn(`ELEVATED O04.${msg_counter}: ${(converter===undefined?'Setting':'Overriding')}' converter to converter with key(s)'${JSON.stringify(c.key)}}`)
747
+ const message = `${(converter===undefined?'Setting':'Overriding')}' converter to converter with key(s)'${JSON.stringify(c.key)}}`;
748
+ if (has_elevated_debug) {
749
+ this.emit('device_debugug', { ID:debugID, data: { flag: `${converter===undefined ? 's' : 'o'}4.${msg_counter}` , IO:false }, message:message});
750
+
751
+ }
866
752
  else
867
- this.log.debug(`${(converter===undefined?'Setting':'Overriding')}' converter to converter with key(s)'${JSON.stringify(c.key)}}`)
753
+ this.log.debug(message);
868
754
  converter = c;
869
755
  msg_counter++;
870
756
  }
871
757
  }
872
758
  if (converter === undefined) {
873
- this.log.error(`No converter available for '${model}' with key '${stateDesc.id}' `);
874
- this.sendError(`No converter available for '${model}' with key '${stateDesc.id}' `);
759
+ const message = `No converter available for '${model}' with key '${stateDesc.id}' `;
760
+ if (has_elevated_debug) {
761
+ this.emit('device_debug', { ID:debugID, data: { error: 'NOCONV',states:[{id:stateDesc.id, value:value, payload:'no converter'}] , IO:false }, message:message});
762
+ }
763
+ else {
764
+ this.log.warn(message);
765
+ }
875
766
  return;
876
767
  }
877
768
 
@@ -890,10 +781,12 @@ class Zigbee extends utils.Adapter {
890
781
 
891
782
  const epName = stateDesc.epname !== undefined ? stateDesc.epname : (stateDesc.prop || stateDesc.id);
892
783
  const key = stateDesc.setattr || stateDesc.prop || stateDesc.id;
893
- if (has_elevated_debug)
894
- this.log.warn(`ELEVATED O04: convert ${key}, ${safeJsonStringify(preparedValue)}, ${safeJsonStringify(preparedOptions)} for device ${deviceId} with Endpoint ${epName}`);
784
+ const message = `convert ${key}, ${safeJsonStringify(preparedValue)}, ${safeJsonStringify(preparedOptions)} for device ${deviceId} with Endpoint ${epName}`;
785
+ if (has_elevated_debug) {
786
+ this.emit('device_debug', { ID:debugID, data: { flag: '04', payload: {key:key, ep: stateDesc.epname, value:preparedValue, options:preparedOptions}, IO:false }, message:message});
787
+ }
895
788
  else
896
- this.log.debug(`convert ${key}, ${safeJsonStringify(preparedValue)}, ${safeJsonStringify(preparedOptions)}`);
789
+ this.log.debug(message);
897
790
 
898
791
  let target;
899
792
  if (model === 'group') {
@@ -923,6 +816,9 @@ class Zigbee extends utils.Adapter {
923
816
  meta.message.state = preparedValue;
924
817
  }
925
818
  }
819
+ if (has_elevated_debug) {
820
+ this.emit('device_debug', { ID:debugID, data: { states:[{id:stateDesc.id, value:value, payload:preparedValue, ep:stateDesc.epname}] , IO:false }});
821
+ }
926
822
 
927
823
  if (preparedOptions !== undefined) {
928
824
  if (preparedOptions.hasOwnProperty('state')) {
@@ -932,33 +828,52 @@ class Zigbee extends utils.Adapter {
932
828
 
933
829
  try {
934
830
  const result = await converter.convertSet(target, key, preparedValue, meta);
935
- if (has_elevated_debug)
936
- this.log.warn(`ELEVATED O05: convert result ${safeJsonStringify(result)} for device ${deviceId}`);
831
+ const message = `convert result ${safeJsonStringify(result)} for device ${deviceId}`;
832
+ if (has_elevated_debug) {
833
+ this.emit('device_debug', { ID:debugID, data: { flag: 'SUCCESS' , IO:false }, message:message});
834
+ }
937
835
  else
938
- this.log.debug(`convert result ${safeJsonStringify(result)}`);
836
+ this.log.debug(message);
939
837
  if (result !== undefined) {
940
- if (stateModel && !isGroup) {
838
+ if (stateModel && !isGroup && !stateDesc.noack) {
941
839
  this.acknowledgeState(deviceId, model, stateDesc, value);
942
840
  }
943
841
  // process sync state list
944
842
  this.processSyncStatesList(deviceId, model, syncStateList);
945
843
  }
946
- else
947
- if (has_elevated_debug)
948
- this.log.error(`ELEVATED OE2: Error convert result for ${key} with ${safeJsonStringify(preparedValue)} is undefined on device ${deviceId}.`);
949
-
844
+ else {
845
+ if (has_elevated_debug) {
846
+ const message = `Convert does not return a result result for ${key} with ${safeJsonStringify(preparedValue)} on device ${deviceId}.`;
847
+ this.emit('device_debug', { ID:debugID, data: { flag: '06' , IO:false }, message:message});
848
+ }
849
+ }
950
850
  } catch (error) {
951
- if (has_elevated_debug)
952
- this.log.error(`ELEVATED OE3: caught error ${safeJsonStringify(error)} when setting value for device ${deviceId}.`);
851
+ if (has_elevated_debug) {
852
+ const message = `caught error ${safeJsonStringify(error)} when setting value for device ${deviceId}.`;
853
+ this.emit('device_debug', { ID:debugID, data: { error: 'EXSET' , IO:false },message:message});
854
+ }
953
855
  this.filterError(`Error ${error.code} on send command to ${deviceId}.` +
954
856
  ` Error: ${error.stack}`, `Send command to ${deviceId} failed with`, error);
955
857
  }
956
858
  });
957
859
  } catch (err) {
958
- this.log.error(`No entity for ${deviceId} : ${err && err.message ? err.message : 'no error message'}`);
860
+ const message = `No entity for ${deviceId} : ${err && err.message ? err.message : 'no error message'}`;
861
+ this.emit('device_debug', { ID:debugID, data: { error: 'EXPUB' , IO:false }, message:message});
959
862
  }
960
863
  }
961
864
 
865
+
866
+ extractEP(key, endpoints) {
867
+ try {
868
+ if (endpoints) for (const ep of Object.keys(endpoints)) {
869
+ if (key.endsWith('_'+ep)) return { setattr: key.replace('_'+ep, ''), epname:ep }
870
+ }
871
+ }
872
+ catch {
873
+ return {};
874
+ }
875
+ return {};
876
+ }
962
877
  // This function is introduced to explicitly allow user level scripts to send Commands
963
878
  // directly to the zigbee device. It utilizes the zigbee-herdsman-converters to generate
964
879
  // the exact zigbee message to be sent and can be used to set device options which are
@@ -972,7 +887,6 @@ class Zigbee extends utils.Adapter {
972
887
  // endpoint: optional: the endpoint to send the data to, if supported.
973
888
  //
974
889
  async sendPayload(payload) {
975
- this.log.debug(`publishToDevice called with ${safeJsonStringify(payload)}`);
976
890
  let payloadObj = {};
977
891
  if (typeof payload === 'string') {
978
892
  try {
@@ -987,7 +901,7 @@ class Zigbee extends utils.Adapter {
987
901
  }
988
902
  } else if (typeof payload === 'object') {
989
903
  payloadObj = payload;
990
- }
904
+ } else return { success: false, error: 'illegal type of payload: ' + typeof payload};
991
905
 
992
906
  if (payloadObj.hasOwnProperty('device') && payloadObj.hasOwnProperty('payload')) {
993
907
  try {
@@ -1012,16 +926,24 @@ class Zigbee extends utils.Adapter {
1012
926
  this.sendError(`Illegal payload type for ${safeJsonStringify(payloadObj.device)}`);
1013
927
  return {success: false, error: `Illegal payload type for ${safeJsonStringify(payloadObj.device)}`};
1014
928
  }
929
+ const endpoints = mappedModel && mappedModel.endpoint ? mappedModel.endpoint(entity.device) : null;
1015
930
  for (const key in payloadObj.payload) {
1016
931
  if (payloadObj.payload[key] != undefined) {
1017
932
  const datatype = typeof payloadObj.payload[key];
933
+ const epobj = this.extractEP(key, endpoints);
934
+ if (payloadObj.endpoint) {
935
+ epobj.epname = payloadObj.endpoint;
936
+ delete epobj.setattr;
937
+ }
1018
938
  stateList.push({
1019
939
  stateDesc: {
1020
940
  id: key,
1021
941
  prop: key,
1022
942
  role: 'state',
1023
943
  type: datatype,
1024
- epname: payloadObj.endpoint,
944
+ noack:true,
945
+ epname: epobj.epname,
946
+ setattr: epobj.setattr,
1025
947
  },
1026
948
  value: payloadObj.payload[key],
1027
949
  index: 0,
@@ -1030,8 +952,7 @@ class Zigbee extends utils.Adapter {
1030
952
  }
1031
953
  }
1032
954
  try {
1033
- this.log.debug(`Calling publish to state for ${safeJsonStringify(payloadObj.device)} with ${safeJsonStringify(stateList)}`);
1034
- await this.publishFromState(`0x${payload.device}`, '', undefined, stateList, payload.options);
955
+ await this.publishFromState(`0x${payload.device}`, payload.model, payload.stateModel, stateList, payload.options, Date.now());
1035
956
  return {success: true};
1036
957
  } catch (error) {
1037
958
  this.log.error(`Error ${error.code} on send command to ${payload.device}.` + ` Error: ${error.stack} ` + `Send command to ${payload.device} failed with ` + error);
@@ -1048,13 +969,18 @@ class Zigbee extends utils.Adapter {
1048
969
 
1049
970
 
1050
971
  newDevice(entity) {
1051
- this.log.debug(`New device event: ${safeJsonStringify(entity)}`);
1052
- this.stController.AddModelFromHerdsman(entity.device, entity.mapped.model)
1053
972
  const dev = entity.device;
973
+ const model = (entity.mapped) ? entity.mapped.model : dev.modelID;
974
+ this.log.debug(`New device event: ${safeJsonStringify(entity)}`);
975
+ if (!entity.mapped && !entity.device.interviewing) {
976
+ const msg = `New device: '${dev.ieeeAddr}' does not have a known model. please provide an external converter for '${dev.modelID}'.`;
977
+ this.log.warn(msg);
978
+ this.logToPairing(msg, true);
979
+ }
980
+ this.stController.AddModelFromHerdsman(entity.device, model)
1054
981
  if (dev) {
1055
982
  this.getObject(dev.ieeeAddr.substr(2), (err, obj) => {
1056
983
  if (!obj) {
1057
- const model = (entity.mapped) ? entity.mapped.model : entity.device.modelID;
1058
984
  this.log.debug(`new device ${dev.ieeeAddr} ${dev.networkAddress} ${model} `);
1059
985
  this.logToPairing(`New device joined '${dev.ieeeAddr}' model ${model}`, true);
1060
986
  this.stController.updateDev(dev.ieeeAddr.substr(2), model, model, () =>
@@ -1123,7 +1049,7 @@ class Zigbee extends utils.Adapter {
1123
1049
  getZigbeeOptions() {
1124
1050
  // file path for db
1125
1051
  let dbDir = path.join(utils.getAbsoluteInstanceDataDir(this), '');
1126
- dbDir = dbDir.replace('.', '_');
1052
+ dbDir = dbDir.replace('zigbee.', 'zigbee_');
1127
1053
 
1128
1054
  if (this.systemConfig && !fs.existsSync(dbDir)) {
1129
1055
  try {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "iobroker.zigbee",
3
- "version": "2.0.4",
3
+ "version": "2.0.5",
4
4
  "author": {
5
5
  "name": "Kirov Ilya",
6
6
  "email": "kirovilya@gmail.com"
@@ -28,8 +28,8 @@
28
28
  "ajv": "^8.17.1",
29
29
  "uri-js": "^4.4.1",
30
30
  "typescript": "^5.6.3",
31
- "zigbee-herdsman": "3.2.7",
32
- "zigbee-herdsman-converters": "21.38.0"
31
+ "zigbee-herdsman": "^3.3.0",
32
+ "zigbee-herdsman-converters": "23.6.0"
33
33
  },
34
34
  "description": "Zigbee devices",
35
35
  "devDependencies": {
@@ -47,7 +47,8 @@
47
47
  "gulp-jsdoc3": "^3.0.0",
48
48
  "gulp-replace": "^1.1.4",
49
49
  "mixin-deep": "^2.0.1",
50
- "mocha": "^11.1.0"
50
+ "mocha": "^11.1.0",
51
+ "@iobroker/dev-server": "^0.7.8"
51
52
  },
52
53
  "homepage": "https://github.com/ioBroker/ioBroker.zigbee",
53
54
  "keywords": [
@@ -85,7 +86,6 @@
85
86
  "test:package": "mocha test/package --exit",
86
87
  "test:unit": "mocha test/unit --exit",
87
88
  "test:integration": "mocha test/integration --exit",
88
- "watch:parcel": "parcel admin/src/index.tsx -d admin/build --hmr-port 1235",
89
89
  "test:js": "mocha --opts test/mocha.custom.opts",
90
90
  "lint": "eslint .",
91
91
  "release": "release-script",