iobroker.zigbee 2.0.1 → 2.0.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.
@@ -1,9 +1,9 @@
1
1
  'use strict';
2
2
 
3
- const EventEmitter = require('events').EventEmitter;
3
+ const safeJsonStringify = require('./json');
4
+ const { EventEmitter } = require('events');
4
5
  const statesMapping = require('./devices');
5
- const getAdId = require('./utils').getAdId;
6
- const getZbId = require('./utils').getZbId;
6
+ const { getAdId, getZbId } = require('./utils');
7
7
  const fs = require('fs');
8
8
  const axios = require('axios');
9
9
  const localConfig = require('./localConfig');
@@ -14,7 +14,8 @@ const localConfig = require('./localConfig');
14
14
  const { exec } = require('child_process');
15
15
  const { tmpdir } = require('os');
16
16
  const path = require('path');
17
- const { numberWithinRange } = require('zigbee-herdsman-converters/lib/utils');
17
+ const { throwDeprecation } = require('process');
18
+ const zigbeeHerdsmanConvertersUtils = require('zigbee-herdsman-converters/lib/utils');
18
19
 
19
20
 
20
21
  class StatesController extends EventEmitter {
@@ -31,6 +32,8 @@ class StatesController extends EventEmitter {
31
32
  this.timeoutHandleUpload = null;
32
33
  this.ImagesToDownload = [];
33
34
  this.stashedErrors = {};
35
+ this.stashedUnknownModels = [];
36
+ this.debugMessages = { nodevice:{ in:[], out: []} };
34
37
  }
35
38
 
36
39
  info(message, data) {
@@ -71,8 +74,35 @@ class StatesController extends EventEmitter {
71
74
  }
72
75
 
73
76
  getStashedErrors() {
74
- if (!this.stashedErrors) return [];
75
- return Object.values(this.stashedErrors);
77
+ const rv = [];
78
+ if (Object.keys(this.stashedErrors).length > 0)
79
+ {
80
+ rv.push('<p><b>Stashed Messages</b></p>')
81
+ rv.push(Object.values(this.stashedErrors).join('<br>'));
82
+ }
83
+ if (this.stashedUnknownModels.length > 0) {
84
+ rv.push('<p><b>Devices whithout Model definition</b></p>')
85
+ rv.push()
86
+ }
87
+ return rv;
88
+ }
89
+
90
+ debugMessagesById() {
91
+ return this.debugMessages;
92
+ }
93
+
94
+ async AddModelFromHerdsman(device, model) {
95
+ // this.warn('addModelFromHerdsman ' + JSON.stringify(model) + ' ' + JSON.stringify(this.localConfig.getOverrideWithKey(model, 'legacy', true)));
96
+ if (this.localConfig.getOverrideWithKey(model, 'legacy', true)) {
97
+ //this.warn('legacy for ' + model);
98
+ await this.addLegacyDevice(model);
99
+ }
100
+ else {
101
+ const pre = statesMapping.devices.length;
102
+ await statesMapping.addExposeToDevices(device, this, model);
103
+ const post = statesMapping.devices.length;
104
+ //this.warn('expose for ' + model + ' '+ pre + '->'+post);
105
+ }
76
106
  }
77
107
 
78
108
  async getDebugDevices(callback) {
@@ -128,6 +158,7 @@ class StatesController extends EventEmitter {
128
158
  if (!this.adapter.zbController || !this.adapter.zbController.connected()) {
129
159
  return;
130
160
  }
161
+ const debugId = Date.now();
131
162
  if (state && !state.ack) {
132
163
  if (id.endsWith('pairingCountdown') || id.endsWith('pairingMessage') || id.endsWith('connection')) {
133
164
  return;
@@ -142,16 +173,18 @@ class StatesController extends EventEmitter {
142
173
  return;
143
174
  }
144
175
 
145
- if (this.checkDebugDevice(id))
146
- this.warn(`ELEVATED O01: User state change of state ${id} with value ${state.val} (ack: ${state.ack}) from ${state.from}`);
147
-
148
- this.debug(`User stateChange ${id} ${JSON.stringify(state)}`);
149
176
  const devId = getAdId(this.adapter, id); // iobroker device id
150
177
  let deviceId = getZbId(id); // zigbee device id
178
+
179
+ if (this.checkDebugDevice(id)) {
180
+ const message = `User state change of state ${id} with value ${state.val} (ack: ${state.ack}) from ${state.from}`;
181
+ this.emit('device_debug', { ID:debugId, data: { ID: deviceId, flag:'01' }, message:message});
182
+ } else
183
+ this.debug(`User stateChange ${id} ${JSON.stringify(state)}`);
151
184
  // const stateKey = id.split('.')[3];
152
185
  const arr = /zigbee.[0-9].[^.]+.(\S+)/gm.exec(id);
153
186
  if (arr[1] === undefined) {
154
- this.warn(`unable to extract id from state ${id}`);
187
+ //this.warn(`unable to extract id from state ${id}`);
155
188
  return;
156
189
  }
157
190
  const stateKey = arr[1];
@@ -175,7 +208,7 @@ class StatesController extends EventEmitter {
175
208
 
176
209
  }
177
210
  this.collectOptions(id.split('.')[2], model, options =>
178
- this.publishFromState(deviceId, model, stateKey, state, options));
211
+ this.publishFromState(deviceId, model, stateKey, state, options, debugId));
179
212
  }
180
213
  });
181
214
  }
@@ -228,7 +261,13 @@ class StatesController extends EventEmitter {
228
261
  } else {
229
262
  stateModel = statesMapping.findModel(model);
230
263
  if (!stateModel) {
231
- this.info(`Device ${deviceId} "${model}" not present in statesMapping - relying on exposes for device definition.`);
264
+ // try to get the model from the exposes
265
+ if (!this.stashedErrors.hasOwnProperty(`${deviceId}.nomodel`))
266
+ {
267
+ this.warn(`Device ${deviceId} "${model}" not found.`);
268
+ this.stashedErrors[`${deviceId}.nomodel`] = `Device ${deviceId} "${model}" not found.`;
269
+ }
270
+
232
271
  states = statesMapping.commonStates;
233
272
  } else {
234
273
  states = stateModel.states;
@@ -275,29 +314,38 @@ class StatesController extends EventEmitter {
275
314
  }, (stateDesc.compositeTimeout ? stateDesc.compositeTimeout : 100) * factor);
276
315
  }
277
316
 
278
- async publishFromState(deviceId, model, stateKey, state, options) {
317
+ async publishFromState(deviceId, model, stateKey, state, options, debugId) {
279
318
  this.debug(`Change state '${stateKey}' at device ${deviceId} type '${model}'`);
280
319
  const elevated = this.checkDebugDevice(deviceId);
281
320
 
282
- if (elevated) this.warn(`ELEVATED O02: Change state '${stateKey}' at device ${deviceId} type '${model}'`);
321
+ if (elevated) {
322
+ const message = (`Change state '${stateKey}' at device ${deviceId} type '${model}'`);
323
+ this.emit('device_debug', { ID:debugId, data: { ID: deviceId, model: model, flag:'02', IO:false }, message:message});
324
+ }
283
325
 
284
326
  const devStates = await this.getDevStates(deviceId, model);
285
327
  if (!devStates) {
286
- if (elevated) this.error(`ELEVATED OE1: no device states for device ${deviceId} type '${model}'`);
328
+ if (elevated) {
329
+ const message = (`no device states for device ${deviceId} type '${model}'`);
330
+ this.emit('device_debug', { ID:debugId, data: { error: 'NOSTATES' , IO:false }, message:message});
331
+ }
287
332
  return;
288
333
  }
289
334
  const commonStates = statesMapping.commonStates.find(statedesc => stateKey === statedesc.id);
290
335
  const stateDesc = (commonStates === undefined ? devStates.states.find(statedesc => stateKey === statedesc.id) : commonStates);
291
336
  const stateModel = devStates.stateModel;
292
337
  if (!stateDesc) {
293
- this.error(`No state available for '${model}' with key '${stateKey}'`);
338
+ const message = (`No state available for '${model}' with key '${stateKey}'`);
339
+ if (elevated) this.emit('device_debug', { ID:debugId, data: { states:[{id:state.ID, value:'unknown', payload:'unknown'}], error: 'NOSTKEY' , IO:false }, message:message});
294
340
  return;
295
341
  }
296
342
 
297
343
  const value = state.val;
298
344
  if (value === undefined || value === '') {
299
- if (elevated)
300
- this.error(`ELEVATED OE2: no value for device ${deviceId} type '${model}'`);
345
+ if (elevated) {
346
+ const message = (`no value for device ${deviceId} type '${model}'`);
347
+ this.emit('device_debug', { ID:debugId, data: { states:[{id:state.ID, value:'--', payload:'error', ep:stateDesc.epname}],error: 'NOVAL' , IO:false }, message:message});
348
+ }
301
349
  return;
302
350
  }
303
351
  let stateList = [{stateDesc: stateDesc, value: value, index: 0, timeout: 0, source:state.from}];
@@ -314,7 +362,8 @@ class StatesController extends EventEmitter {
314
362
  }
315
363
  } catch (e) {
316
364
  this.sendError(e);
317
- this.error('Exception caught in publishfromstate');
365
+ if (elevated) this.emit('device_debug', { ID:debugId, data: { states:[{id:state.ID, value:state.val, payload:'unknown'}], error: 'EXLINK' , IO:false }});
366
+ this.error('Exception caught in publishfromstate: ' + (e && e.message ? e.message : 'no error message given'));
318
367
  }
319
368
 
320
369
  });
@@ -329,7 +378,7 @@ class StatesController extends EventEmitter {
329
378
  readAfterWriteStates = readAfterWriteStates.concat(readAfterWriteStateDesc.id));
330
379
  }
331
380
 
332
- this.emit('changed', deviceId, model, stateModel, stateList, options);
381
+ this.emit('changed', deviceId, model, stateModel, stateList, options, debugId);
333
382
  }
334
383
 
335
384
  async renameDevice(id, newName) {
@@ -383,7 +432,7 @@ class StatesController extends EventEmitter {
383
432
  let statename = state._id;
384
433
  const arr = /zigbee.[0-9].[^.]+.(\S+)/gm.exec(statename);
385
434
  if (arr[1] === undefined) {
386
- this.warn(`unable to extract id from state ${statename}`);
435
+ this.debug(`unable to extract id from state ${statename}`);
387
436
  const idx = statename.lastIndexOf('.');
388
437
  if (idx > -1) {
389
438
  statename = statename.slice(idx + 1);
@@ -615,6 +664,11 @@ class StatesController extends EventEmitter {
615
664
  //this.warn('devices are' + modelarr2.join(','));
616
665
  }
617
666
 
667
+ async addLegacyDevice(model) {
668
+ statesMapping.setLegacyDevices([model]);
669
+ statesMapping.getByModel();
670
+ }
671
+
618
672
  async getDefaultGroupIcon(id) {
619
673
  const regexResult = id.match(new RegExp(/group_(\d+)/));
620
674
  if (!regexResult) return '';
@@ -720,7 +774,7 @@ class StatesController extends EventEmitter {
720
774
  this.adapter.fileExists(namespace, target, async (err,result) => {
721
775
  if (result) return;
722
776
  const src = `${tmpdir()}/${path.basename(target)}`;
723
- const msg = `downloading ${url} to ${src}`;
777
+ //const msg = `downloading ${url} to ${src}`;
724
778
  if (this.ImagesToDownload.indexOf(url) ==-1) {
725
779
  await this.downloadIcon(url, src)
726
780
  try {
@@ -738,7 +792,6 @@ class StatesController extends EventEmitter {
738
792
  this.info(`copied ${src} to ${target}.`)
739
793
  fs.rm(src, (err) => {
740
794
  if (err) this.warn(`error removing ${src} : ${JSON.stringify(err)}`);
741
- else this.info(`removed ${src}`)
742
795
  });
743
796
  })
744
797
  }
@@ -807,7 +860,12 @@ class StatesController extends EventEmitter {
807
860
  async getExposes() {
808
861
  await this.localConfig.init();
809
862
  await this.applyLegacyDevices();
810
- statesMapping.fillStatesWithExposes();
863
+ try {
864
+ statesMapping.fillStatesWithExposes(this);
865
+ }
866
+ catch (error) {
867
+ this.error(`Error applying exposes: ${error && error.message ? error.message : 'no error message'} ${error && error.stack ? error.stack : ''}`);
868
+ }
811
869
  }
812
870
 
813
871
  async elevatedMessage(device, message, isError) {
@@ -821,102 +879,257 @@ class StatesController extends EventEmitter {
821
879
  this.emit('debugmessage', {id: id, message:message});
822
880
  }
823
881
 
824
- async publishToState(devId, model, payload) {
825
- const devStates = await this.getDevStates(`0x${devId}`, model);
882
+ async publishToState(devId, model, payload, debugId) {
883
+ try {
884
+ if (!debugId) debugId = Date.now();
885
+ const devStates = await this.getDevStates(`0x${devId}`, model);
886
+
887
+ const has_elevated_debug = (this.checkDebugDevice(devId) && !payload.hasOwnProperty('msg_from_zigbee'));
888
+
889
+ const message = `message received '${JSON.stringify(payload)}' from device ${devId} type '${model}'`;
890
+ if (has_elevated_debug) this.emit('device_debug', { ID:debugId, data: { deviceID: devId, flag:'01', IO:true }, message:message});
891
+ else this.debug(message);
892
+ if (!devStates) {
893
+ const message = `no device states for device ${devId} type '${model}'`;
894
+ if (has_elevated_debug)this.emit('device_debug', { ID:debugId, data: { error:'NOSTATE',states:[{ id:'--', value:'--', payload:payload}], IO:true }, message:message});
895
+ else this.debug(message);
896
+ return;
897
+ }
898
+ // find states for payload
899
+ let has_published = false;
900
+ if (devStates.states !== undefined) {
901
+ try {
902
+ const states = statesMapping.commonStates.concat(
903
+ devStates.states.filter(statedesc => payload.hasOwnProperty(statedesc.prop || statedesc.id))
904
+ );
905
+
906
+ for (const stateInd in states) {
907
+ const statedesc = states[stateInd];
908
+ let value;
909
+ if (statedesc.getter) {
910
+ value = statedesc.getter(payload);
911
+ } else {
912
+ value = payload[statedesc.prop || statedesc.id];
913
+ }
914
+ // checking value
915
+ if (value === undefined || value === null) {
916
+ continue;
917
+ }
826
918
 
827
- const has_elevated_debug = (this.checkDebugDevice(devId) && !payload.hasOwnProperty('msg_from_zigbee'));
919
+ let stateID = statedesc.id;
920
+
921
+ const message = `value generated '${JSON.stringify(value)}' from device ${devId} for '${statedesc.name}'`;
922
+ if (has_elevated_debug) this.emit('device_debug', { ID:debugId, data: { states:[{id:stateID, value:value, payload:payload }],flag:'02', IO:true }, message});
923
+ else this.debug(message);
924
+
925
+ const common = {
926
+ name: statedesc.name,
927
+ type: statedesc.type,
928
+ unit: statedesc.unit,
929
+ read: statedesc.read,
930
+ write: statedesc.write,
931
+ icon: statedesc.icon,
932
+ role: statedesc.role,
933
+ min: statedesc.min,
934
+ max: statedesc.max,
935
+ };
936
+
937
+ if (typeof value === 'object' && value.hasOwnProperty('stateid')) {
938
+ stateID = `${stateID}.${value.stateid}`;
939
+ if (value.hasOwnProperty('unit')) {
940
+ common.unit = value.unit;
941
+ }
942
+ common.name = value.name ? value.name : value.stateid;
943
+ common.role = value.role ? `value.${value.role}` : 'number';
944
+ value = value.value;
945
+ }
828
946
 
947
+ // if needs to return value to back after timeout
948
+ if (statedesc.isEvent) {
949
+ this.updateStateWithTimeout(devId, statedesc.id, value, common, 300, (typeof value == typeof (!value) ? !value : ''));
950
+ if (has_elevated_debug) this.emit('device_debug', { ID:debugId, data: { flag:'SUCCESS', IO:true }});
829
951
 
830
- if (has_elevated_debug)
831
- {
832
- this.elevatedMessage(devId, `ELEVATED I01: message received '${JSON.stringify(payload)}' from device ${devId} type '${model}'`, false);
952
+ } else {
953
+ if (statedesc.prepublish) {
954
+ this.collectOptions(devId, model, options =>
955
+ statedesc.prepublish(devId, value, newvalue => {
956
+ this.updateState(devId, stateID, newvalue, common) }, options)
957
+ );
958
+ if (has_elevated_debug) this.emit('device_debug', { ID:debugId, data: { flag:'SUCCESS', IO:true }});
959
+ } else {
960
+ this.updateState(devId, stateID, value, common, debugId);
961
+ if (has_elevated_debug) this.emit('device_debug', { ID:debugId, data: { flag:'SUCCESS', IO:true }});
962
+ }
963
+ }
964
+ has_published = true;
965
+ }
966
+ } catch (e) {
967
+ const message = `unable to enumerate states of ${devId} for payload ${JSON.stringify(payload)}, ${(e ? e.name : 'undefined')} (${(e ? e.message : '')}).`;
968
+ if (has_elevated_debug) this.emit('device_debug', { ID:debugId, data:{ error:'ESTATE', IO:true }, message:message});
969
+ else this.debug(message);
970
+ }
971
+ const message = `No value published for device ${devId}`;
972
+ if (!has_published && has_elevated_debug) this.emit('device_debug', { ID:debugId, data:{ error:'NOVAL', IO:true }, message:message});
973
+ else this.debug(message);
974
+ }
975
+ else {
976
+ const message = `ELEVATED IE05 - NOSTATE: No states matching the payload ${JSON.stringify(payload)} for device ${devId}`;
977
+ if (has_elevated_debug) this.emit('device_debug', { ID:debugId, data:{ error:'NOSTATE', IO:true }, message});
978
+ else this.debug(message);
979
+ }
833
980
  }
834
- if (!devStates) {
835
- if (has_elevated_debug)
836
- this.elevatedMessage(devId, `ELEVATED IE02: no device states for device ${devId} type '${model}'`, true)
837
- return;
981
+ catch (error) {
982
+ this.error('Something went horribly wrong: ' + (error && error.message ? error.message : 'no reason given'));
838
983
  }
839
- // find states for payload
840
- let has_published = false;
984
+ }
841
985
 
842
- if (devStates.states !== undefined) {
843
- try {
844
- const states = statesMapping.commonStates.concat(
845
- devStates.states.filter(statedesc => payload.hasOwnProperty(statedesc.prop || statedesc.id))
846
- );
847
-
848
- for (const stateInd in states) {
849
- const statedesc = states[stateInd];
850
- let value;
851
- if (statedesc.getter) {
852
- value = statedesc.getter(payload);
853
- } else {
854
- value = payload[statedesc.prop || statedesc.id];
855
- }
856
- // checking value
857
- if (value === undefined || value === null) {
858
- continue;
859
- }
986
+ async processConverters(converters, devId, model, mappedModel, message, meta, debugId) {
987
+ for (const converter of converters) {
988
+ const publish = (payload, dID) => {
989
+ if (typeof payload === 'object') {
990
+ this.publishToState(devId, model, payload,dID);
991
+ }
992
+ };
860
993
 
861
- let stateID = statedesc.id;
994
+ const options = await new Promise((resolve, reject) => {
995
+ this.collectOptions(devId, model, (options) => {
996
+ resolve(options);
997
+ });
998
+ });
862
999
 
863
- if (has_elevated_debug) {
864
- this.elevatedMessage(devId, `ELEVATED I02: value generated '${JSON.stringify(value)}' from device ${devId} for '${statedesc.name}'`, false);
865
- }
1000
+ const payload = await new Promise((resolve, reject) => {
1001
+ const payloadConv = converter.convert(mappedModel, message, publish, options, meta);
1002
+ if (typeof payloadConv === 'object') {
1003
+ resolve(payloadConv);
1004
+ }
1005
+ });
866
1006
 
867
- const common = {
868
- name: statedesc.name,
869
- type: statedesc.type,
870
- unit: statedesc.unit,
871
- read: statedesc.read,
872
- write: statedesc.write,
873
- icon: statedesc.icon,
874
- role: statedesc.role,
875
- min: statedesc.min,
876
- max: statedesc.max,
877
- };
878
-
879
- if (typeof value === 'object' && value.hasOwnProperty('stateid')) {
880
- stateID = `${stateID}.${value.stateid}`;
881
- if (value.hasOwnProperty('unit')) {
882
- common.unit = value.unit;
883
- }
884
- common.name = value.name ? value.name : value.stateid;
885
- common.role = value.role ? `value.${value.role}` : 'number';
886
- value = value.value;
887
- }
1007
+ publish(payload, debugId);
1008
+ }
1009
+ }
888
1010
 
889
- // if needs to return value to back after timeout
890
- if (statedesc.isEvent) {
891
- this.updateStateWithTimeout(devId, statedesc.id, value, common, 300, !value);
892
- } else {
893
- if (statedesc.prepublish) {
894
- this.collectOptions(devId, model, options =>
895
- statedesc.prepublish(devId, value, newvalue =>
896
- this.updateState(devId, stateID, newvalue, common), options)
897
- );
898
- } else {
899
- this.updateState(devId, stateID, value, common);
1011
+
1012
+ async onZigbeeEvent(type, entity, message) {
1013
+ this.debug(`Type ${type} device ${safeJsonStringify(entity)} incoming event: ${safeJsonStringify(message)}`);
1014
+
1015
+ const device = entity.device;
1016
+ const mappedModel = entity.mapped;
1017
+ const model = entity.mapped ? entity.mapped.model : entity.device.modelID;
1018
+ const cluster = message.cluster;
1019
+ const devId = device.ieeeAddr.substr(2);
1020
+ const meta = {device};
1021
+
1022
+ const has_elevated_debug = this.checkDebugDevice(devId);
1023
+ const debugId = Date.now();
1024
+
1025
+ // raw message data for logging and msg_from_zigbee
1026
+ const msgForState = Object.assign({}, message);
1027
+ delete msgForState['device'];
1028
+ delete msgForState['endpoint'];
1029
+ msgForState['endpoint_id'] = message.endpoint.ID;
1030
+
1031
+ if (has_elevated_debug) {
1032
+ const message = `Zigbee Event of Type ${type} from device ${device.ieeeAddr}, incoming event: ${safeJsonStringify(msgForState)}`;
1033
+ this.emit('device_debug', { ID:debugId, data: { ID: device.ieeeAddr, payload:safeJsonStringify(msgForState), flag:'01', IO:true }, message:message});
1034
+
1035
+ }
1036
+ // this assigment give possibility to use iobroker logger in code of the converters, via meta.logger
1037
+ meta.logger = this;
1038
+
1039
+ await this.adapter.checkIfModelUpdate(entity);
1040
+
1041
+ let _voltage = 0;
1042
+ let _temperature = 0;
1043
+ let _humidity = 0;
1044
+
1045
+ let isMessure = false;
1046
+ let isBattKey = false;
1047
+
1048
+ if (mappedModel && mappedModel.meta && mappedModel.meta.battery) {
1049
+ const isVoltage = mappedModel.meta.battery.hasOwnProperty('voltageToPercentage');
1050
+
1051
+ if (isVoltage) {
1052
+ const keys = Object.keys(message.data);
1053
+
1054
+ for (const key of keys) {
1055
+ const value = message.data[key];
1056
+
1057
+ if (value && value[1]) {
1058
+ if (key == 65282 && value[1][1]) {
1059
+ _voltage = value[1][1].elmVal;
1060
+ isBattKey = true;
1061
+ break;
1062
+ }
1063
+ if (key == 65281) {
1064
+ _voltage = value[1];
1065
+ isBattKey = true;
1066
+ _temperature = value[100];
1067
+ _temperature = _temperature /100;
1068
+ _humidity = value[101];
1069
+ _humidity = _humidity / 100;
1070
+ isMessure = true;
1071
+ break;
900
1072
  }
901
1073
  }
902
- has_published = true;
903
1074
  }
904
- } catch (e) {
905
- this.debug(`No states in device ${devId} : payload ${JSON.stringify(payload)}`);
906
- if (has_elevated_debug)
907
- this.elevatedMessage(devId, `ELEVATED IE03: error when enumerating states of ${devId} for payload ${JSON.stringify(payload)}, ${(e ? e.name : 'undefined')} (${(e ? e.message : '')}).`, true);
908
1075
  }
909
- if (!has_published && has_elevated_debug) {
910
- this.elevatedMessage(devId, `ELEVATED IE04: No value published for device ${devId}`, true);
1076
+ }
911
1077
 
1078
+ // always publish link_quality and battery
1079
+ if (message.linkquality) { // send battery with
1080
+ this.publishToState(devId, model, {linkquality: message.linkquality}, debugId);
1081
+ if (isBattKey) {
1082
+ this.publishToState(devId, model, {voltage: _voltage}, debugId);
1083
+ const battProz = zigbeeHerdsmanConvertersUtils.batteryVoltageToPercentage(_voltage,entity.mapped.meta.battery.voltageToPercentage);
1084
+ this.publishToState(devId, model, {battery: battProz}, debugId);
1085
+ }
1086
+ if (isMessure) {
1087
+ this.publishToState(devId, model, {temperature: _temperature}, debugId);
1088
+ this.publishToState(devId, model, {humidity: _humidity}), debugId;
912
1089
  }
913
1090
  }
914
- else {
915
- if (has_elevated_debug)
916
- this.elevatedMessage(devId, `ELEVATED IE05: No states matching the payload ${JSON.stringify(payload)} for device ${devId}`, true);
1091
+
1092
+ // publish raw event to "from_zigbee"
1093
+ // some cleanup
1094
+
1095
+ this.publishToState(devId, model, {msg_from_zigbee: safeJsonStringify(msgForState)}, -1);
1096
+
1097
+ if (!entity.mapped) {
1098
+ return;
1099
+ }
1100
+
1101
+ let converters = mappedModel.fromZigbee.filter(c => c && c.cluster === cluster && (
1102
+ Array.isArray(c.type) ? c.type.includes(type) : c.type === type));
1103
+
1104
+
1105
+ if (!converters.length && type === 'readResponse') {
1106
+ converters = mappedModel.fromZigbee.filter(c => c.cluster === cluster && (
1107
+ Array.isArray(c.type) ? c.type.includes('attributeReport') : c.type === 'attributeReport'));
1108
+ }
1109
+
1110
+ if (!converters.length) {
1111
+ if (type !== 'readResponse') {
1112
+ const message = `No converter available for '${mappedModel.model}' '${devId}' with cluster '${cluster}' and type '${type}'`;
1113
+ if (has_elevated_debug) this.emit('device_debug', { ID:debugId, data: { error:'NOCONV', IO:true }, message:message});
1114
+ else this.debug(message);
1115
+ }
1116
+ return;
917
1117
  }
1118
+
1119
+ meta.state = { state: '' }; // for tuya
1120
+
1121
+ this.processConverters(converters, devId, model, mappedModel, message, meta, debugId)
1122
+ .catch((error) => {
1123
+ // 'Error: Expected one of: 0, 1, got: 'undefined''
1124
+ if (cluster !== '64529') {
1125
+ if (has_elevated_debug) this.emit('device_debug', { ID:debugId, data: { error:'EPROC', IO:true }});
1126
+ this.error(`Error while processing converters DEVICE_ID: '${devId}' cluster '${cluster}' type '${type}'`);
1127
+ }
1128
+ });
918
1129
  }
919
1130
 
1131
+
1132
+
920
1133
  }
921
1134
 
922
1135
  module.exports = StatesController;
package/lib/utils.js CHANGED
@@ -106,11 +106,13 @@ function flatten(arr) {
106
106
  flat.concat(Array.isArray(toFlatten) ? flatten(toFlatten) : toFlatten), []);
107
107
  }
108
108
 
109
- const forceEndDevice = flatten(
109
+ const forceEndDevice = ['QBKG03LM', 'QBKG04LM', 'ZNMS13LM', 'ZNMS12LM'];
110
+
111
+ /*flatten(
110
112
  ['QBKG03LM', 'QBKG04LM', 'ZNMS13LM', 'ZNMS12LM']
111
113
  .map(model => zigbeeHerdsmanConverters.findByModel(model))
112
114
  .map(mappedModel => mappedModel.zigbeeModel));
113
-
115
+ */
114
116
  // Xiaomi uses 4151 and 4447 (lumi.plug) as manufacturer ID.
115
117
  const xiaomiManufacturerID = [4151, 4447];
116
118
  const ikeaTradfriManufacturerID = [4476];
@@ -6,13 +6,7 @@ const utils = require('./utils');
6
6
 
7
7
  // Some EndDevices should be pinged
8
8
  // e.g. E11-G13 https://github.com/Koenkk/zigbee2mqtt/issues/775#issuecomment-453683846
9
- const forcedPingable = [
10
- zigbeeHerdsmanConverters.findByModel('E11-G13'),
11
- zigbeeHerdsmanConverters.findByModel('53170161'),
12
- zigbeeHerdsmanConverters.findByModel('V3-BTZB'),
13
- zigbeeHerdsmanConverters.findByModel('SPZB0001'),
14
- zigbeeHerdsmanConverters.findByModel('014G2461')
15
- ];
9
+ const forcedPingable = ['E11-G13','53170161','V3-BTZB','SPZB0001','014G2461'];
16
10
 
17
11
  // asgothian: 29.12.2020: Removed color and color_temp from readable
18
12
  // state candidates as most states do not provide a getter to ikea_transform
@@ -184,7 +178,7 @@ class DeviceAvailability extends BaseExtension {
184
178
  this.publishAvailability(device, false);
185
179
  if (pingCount.failed++ <= this.max_ping) {
186
180
  if (pingCount.failed < 2 && pingCount.reported < this.max_ping) {
187
- this.warn(`Failed to ping ${ieeeAddr} ${device.modelID}`);
181
+ this.info(`Failed to ping ${ieeeAddr} ${device.modelID}`);
188
182
  pingCount.reported++;
189
183
  } else {
190
184
  this.debug(`Failed to ping ${ieeeAddr} ${device.modelID} on ${pingCount} consecutive attempts`);
@@ -192,7 +186,7 @@ class DeviceAvailability extends BaseExtension {
192
186
  this.setTimerPingable(device, pingCount.failed);
193
187
  this.ping_counters[device.ieeeAddr] = pingCount;
194
188
  } else {
195
- this.warn(`Stopping to ping ${ieeeAddr} ${device.modelID} after ${pingCount.failed} ping attempts`);
189
+ this.info(`Stopping to ping ${ieeeAddr} ${device.modelID} after ${pingCount.failed} ping attempts`);
196
190
  }
197
191
  }
198
192
  }