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/README.md +83 -56
- package/admin/admin.js +217 -19
- package/admin/img/philips_hue_lom001.png +0 -0
- package/admin/tab_m.html +13 -8
- package/docs/tutorial/groups-1.png +0 -0
- package/docs/tutorial/groups-2.png +0 -0
- package/docs/tutorial/tab-dev-1.png +0 -0
- package/io-package.json +15 -49
- package/lib/DeviceDebug.js +4 -2
- package/lib/commands.js +21 -2
- package/lib/developer.js +0 -0
- package/lib/exposes.js +8 -3
- package/lib/groups.js +6 -8
- package/lib/localConfig.js +4 -5
- package/lib/ota.js +6 -6
- package/lib/statescontroller.js +276 -101
- package/lib/zbDeviceAvailability.js +2 -2
- package/lib/zbDeviceConfigure.js +92 -58
- package/lib/zigbeecontroller.js +44 -46
- package/main.js +143 -217
- package/package.json +5 -5
package/lib/statescontroller.js
CHANGED
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
'use strict';
|
|
2
2
|
|
|
3
|
-
const
|
|
3
|
+
const safeJsonStringify = require('./json');
|
|
4
|
+
const { EventEmitter } = require('events');
|
|
4
5
|
const statesMapping = require('./devices');
|
|
5
|
-
const getAdId = require('./utils')
|
|
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 {
|
|
17
|
+
const { throwDeprecation } = require('process');
|
|
18
|
+
const zigbeeHerdsmanConvertersUtils = require('zigbee-herdsman-converters/lib/utils');
|
|
18
19
|
|
|
19
20
|
|
|
20
21
|
class StatesController extends EventEmitter {
|
|
@@ -32,6 +33,7 @@ class StatesController extends EventEmitter {
|
|
|
32
33
|
this.ImagesToDownload = [];
|
|
33
34
|
this.stashedErrors = {};
|
|
34
35
|
this.stashedUnknownModels = [];
|
|
36
|
+
this.debugMessages = { nodevice:{ in:[], out: []} };
|
|
35
37
|
}
|
|
36
38
|
|
|
37
39
|
info(message, data) {
|
|
@@ -85,6 +87,10 @@ class StatesController extends EventEmitter {
|
|
|
85
87
|
return rv;
|
|
86
88
|
}
|
|
87
89
|
|
|
90
|
+
debugMessagesById() {
|
|
91
|
+
return this.debugMessages;
|
|
92
|
+
}
|
|
93
|
+
|
|
88
94
|
async AddModelFromHerdsman(device, model) {
|
|
89
95
|
// this.warn('addModelFromHerdsman ' + JSON.stringify(model) + ' ' + JSON.stringify(this.localConfig.getOverrideWithKey(model, 'legacy', true)));
|
|
90
96
|
if (this.localConfig.getOverrideWithKey(model, 'legacy', true)) {
|
|
@@ -152,6 +158,7 @@ class StatesController extends EventEmitter {
|
|
|
152
158
|
if (!this.adapter.zbController || !this.adapter.zbController.connected()) {
|
|
153
159
|
return;
|
|
154
160
|
}
|
|
161
|
+
const debugId = Date.now();
|
|
155
162
|
if (state && !state.ack) {
|
|
156
163
|
if (id.endsWith('pairingCountdown') || id.endsWith('pairingMessage') || id.endsWith('connection')) {
|
|
157
164
|
return;
|
|
@@ -166,16 +173,18 @@ class StatesController extends EventEmitter {
|
|
|
166
173
|
return;
|
|
167
174
|
}
|
|
168
175
|
|
|
169
|
-
if (this.checkDebugDevice(id))
|
|
170
|
-
this.warn(`ELEVATED O01: User state change of state ${id} with value ${state.val} (ack: ${state.ack}) from ${state.from}`);
|
|
171
|
-
|
|
172
|
-
this.debug(`User stateChange ${id} ${JSON.stringify(state)}`);
|
|
173
176
|
const devId = getAdId(this.adapter, id); // iobroker device id
|
|
174
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)}`);
|
|
175
184
|
// const stateKey = id.split('.')[3];
|
|
176
185
|
const arr = /zigbee.[0-9].[^.]+.(\S+)/gm.exec(id);
|
|
177
186
|
if (arr[1] === undefined) {
|
|
178
|
-
this.warn(`unable to extract id from state ${id}`);
|
|
187
|
+
//this.warn(`unable to extract id from state ${id}`);
|
|
179
188
|
return;
|
|
180
189
|
}
|
|
181
190
|
const stateKey = arr[1];
|
|
@@ -199,7 +208,7 @@ class StatesController extends EventEmitter {
|
|
|
199
208
|
|
|
200
209
|
}
|
|
201
210
|
this.collectOptions(id.split('.')[2], model, options =>
|
|
202
|
-
this.publishFromState(deviceId, model, stateKey, state, options));
|
|
211
|
+
this.publishFromState(deviceId, model, stateKey, state, options, debugId));
|
|
203
212
|
}
|
|
204
213
|
});
|
|
205
214
|
}
|
|
@@ -305,29 +314,38 @@ class StatesController extends EventEmitter {
|
|
|
305
314
|
}, (stateDesc.compositeTimeout ? stateDesc.compositeTimeout : 100) * factor);
|
|
306
315
|
}
|
|
307
316
|
|
|
308
|
-
async publishFromState(deviceId, model, stateKey, state, options) {
|
|
317
|
+
async publishFromState(deviceId, model, stateKey, state, options, debugId) {
|
|
309
318
|
this.debug(`Change state '${stateKey}' at device ${deviceId} type '${model}'`);
|
|
310
319
|
const elevated = this.checkDebugDevice(deviceId);
|
|
311
320
|
|
|
312
|
-
if (elevated)
|
|
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
|
+
}
|
|
313
325
|
|
|
314
326
|
const devStates = await this.getDevStates(deviceId, model);
|
|
315
327
|
if (!devStates) {
|
|
316
|
-
if (elevated)
|
|
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
|
+
}
|
|
317
332
|
return;
|
|
318
333
|
}
|
|
319
334
|
const commonStates = statesMapping.commonStates.find(statedesc => stateKey === statedesc.id);
|
|
320
335
|
const stateDesc = (commonStates === undefined ? devStates.states.find(statedesc => stateKey === statedesc.id) : commonStates);
|
|
321
336
|
const stateModel = devStates.stateModel;
|
|
322
337
|
if (!stateDesc) {
|
|
323
|
-
|
|
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});
|
|
324
340
|
return;
|
|
325
341
|
}
|
|
326
342
|
|
|
327
343
|
const value = state.val;
|
|
328
344
|
if (value === undefined || value === '') {
|
|
329
|
-
if (elevated)
|
|
330
|
-
|
|
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
|
+
}
|
|
331
349
|
return;
|
|
332
350
|
}
|
|
333
351
|
let stateList = [{stateDesc: stateDesc, value: value, index: 0, timeout: 0, source:state.from}];
|
|
@@ -344,7 +362,8 @@ class StatesController extends EventEmitter {
|
|
|
344
362
|
}
|
|
345
363
|
} catch (e) {
|
|
346
364
|
this.sendError(e);
|
|
347
|
-
this.
|
|
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'));
|
|
348
367
|
}
|
|
349
368
|
|
|
350
369
|
});
|
|
@@ -359,7 +378,7 @@ class StatesController extends EventEmitter {
|
|
|
359
378
|
readAfterWriteStates = readAfterWriteStates.concat(readAfterWriteStateDesc.id));
|
|
360
379
|
}
|
|
361
380
|
|
|
362
|
-
this.emit('changed', deviceId, model, stateModel, stateList, options);
|
|
381
|
+
this.emit('changed', deviceId, model, stateModel, stateList, options, debugId);
|
|
363
382
|
}
|
|
364
383
|
|
|
365
384
|
async renameDevice(id, newName) {
|
|
@@ -413,7 +432,7 @@ class StatesController extends EventEmitter {
|
|
|
413
432
|
let statename = state._id;
|
|
414
433
|
const arr = /zigbee.[0-9].[^.]+.(\S+)/gm.exec(statename);
|
|
415
434
|
if (arr[1] === undefined) {
|
|
416
|
-
this.
|
|
435
|
+
this.debug(`unable to extract id from state ${statename}`);
|
|
417
436
|
const idx = statename.lastIndexOf('.');
|
|
418
437
|
if (idx > -1) {
|
|
419
438
|
statename = statename.slice(idx + 1);
|
|
@@ -447,8 +466,10 @@ class StatesController extends EventEmitter {
|
|
|
447
466
|
}
|
|
448
467
|
}
|
|
449
468
|
} else {
|
|
450
|
-
|
|
451
|
-
|
|
469
|
+
if (!markOnly) {
|
|
470
|
+
this.debug(`keeping connected state ${JSON.stringify(statename)} of ${devId} `);
|
|
471
|
+
messages.push(`keeping connecte state ${JSON.stringify(statename)} of ${devId} `);
|
|
472
|
+
}
|
|
452
473
|
}
|
|
453
474
|
});
|
|
454
475
|
}
|
|
@@ -559,7 +580,7 @@ class StatesController extends EventEmitter {
|
|
|
559
580
|
value = minval
|
|
560
581
|
if (!this.stashedErrors.hasOwnProperty(`${stateId}.min`))
|
|
561
582
|
{
|
|
562
|
-
this.
|
|
583
|
+
this.warn(`State value for ${stateId} has value "${nval}" less than min "${minval}". - this eror is recorded and not repeated`);
|
|
563
584
|
this.stashedErrors[`${stateId}.min`] = `State value for ${stateId} has value "${nval}." less than min "${minval}"`;
|
|
564
585
|
}
|
|
565
586
|
}
|
|
@@ -570,7 +591,7 @@ class StatesController extends EventEmitter {
|
|
|
570
591
|
value = maxval;
|
|
571
592
|
if (!this.stashedErrors.hasOwnProperty(`${stateId}.max`))
|
|
572
593
|
{
|
|
573
|
-
this.
|
|
594
|
+
this.warn(`State value for ${stateId} has value "${nval}" more than max "${maxval}". - this eror is recorded and not repeated`);
|
|
574
595
|
this.stashedErrors[`${stateId}.max`] = `State value for ${stateId} has value "${nval}" more than max "${maxval}".`;
|
|
575
596
|
}
|
|
576
597
|
}
|
|
@@ -755,7 +776,7 @@ class StatesController extends EventEmitter {
|
|
|
755
776
|
this.adapter.fileExists(namespace, target, async (err,result) => {
|
|
756
777
|
if (result) return;
|
|
757
778
|
const src = `${tmpdir()}/${path.basename(target)}`;
|
|
758
|
-
const msg = `downloading ${url} to ${src}`;
|
|
779
|
+
//const msg = `downloading ${url} to ${src}`;
|
|
759
780
|
if (this.ImagesToDownload.indexOf(url) ==-1) {
|
|
760
781
|
await this.downloadIcon(url, src)
|
|
761
782
|
try {
|
|
@@ -773,7 +794,6 @@ class StatesController extends EventEmitter {
|
|
|
773
794
|
this.info(`copied ${src} to ${target}.`)
|
|
774
795
|
fs.rm(src, (err) => {
|
|
775
796
|
if (err) this.warn(`error removing ${src} : ${JSON.stringify(err)}`);
|
|
776
|
-
else this.info(`removed ${src}`)
|
|
777
797
|
});
|
|
778
798
|
})
|
|
779
799
|
}
|
|
@@ -861,102 +881,257 @@ class StatesController extends EventEmitter {
|
|
|
861
881
|
this.emit('debugmessage', {id: id, message:message});
|
|
862
882
|
}
|
|
863
883
|
|
|
864
|
-
async publishToState(devId, model, payload) {
|
|
865
|
-
|
|
884
|
+
async publishToState(devId, model, payload, debugId) {
|
|
885
|
+
try {
|
|
886
|
+
if (!debugId) debugId = Date.now();
|
|
887
|
+
const devStates = await this.getDevStates(`0x${devId}`, model);
|
|
888
|
+
|
|
889
|
+
const has_elevated_debug = (this.checkDebugDevice(devId) && !payload.hasOwnProperty('msg_from_zigbee'));
|
|
890
|
+
|
|
891
|
+
const message = `message received '${JSON.stringify(payload)}' from device ${devId} type '${model}'`;
|
|
892
|
+
if (has_elevated_debug) this.emit('device_debug', { ID:debugId, data: { deviceID: devId, flag:'01', IO:true }, message:message});
|
|
893
|
+
else this.debug(message);
|
|
894
|
+
if (!devStates) {
|
|
895
|
+
const message = `no device states for device ${devId} type '${model}'`;
|
|
896
|
+
if (has_elevated_debug)this.emit('device_debug', { ID:debugId, data: { error:'NOSTATE',states:[{ id:'--', value:'--', payload:payload}], IO:true }, message:message});
|
|
897
|
+
else this.debug(message);
|
|
898
|
+
return;
|
|
899
|
+
}
|
|
900
|
+
// find states for payload
|
|
901
|
+
let has_published = false;
|
|
902
|
+
if (devStates.states !== undefined) {
|
|
903
|
+
try {
|
|
904
|
+
const states = statesMapping.commonStates.concat(
|
|
905
|
+
devStates.states.filter(statedesc => payload.hasOwnProperty(statedesc.prop || statedesc.id))
|
|
906
|
+
);
|
|
907
|
+
|
|
908
|
+
for (const stateInd in states) {
|
|
909
|
+
const statedesc = states[stateInd];
|
|
910
|
+
let value;
|
|
911
|
+
if (statedesc.getter) {
|
|
912
|
+
value = statedesc.getter(payload);
|
|
913
|
+
} else {
|
|
914
|
+
value = payload[statedesc.prop || statedesc.id];
|
|
915
|
+
}
|
|
916
|
+
// checking value
|
|
917
|
+
if (value === undefined || value === null) {
|
|
918
|
+
continue;
|
|
919
|
+
}
|
|
866
920
|
|
|
867
|
-
|
|
921
|
+
let stateID = statedesc.id;
|
|
922
|
+
|
|
923
|
+
const message = `value generated '${JSON.stringify(value)}' from device ${devId} for '${statedesc.name}'`;
|
|
924
|
+
if (has_elevated_debug) this.emit('device_debug', { ID:debugId, data: { states:[{id:stateID, value:value, payload:payload }],flag:'02', IO:true }, message});
|
|
925
|
+
else this.debug(message);
|
|
926
|
+
|
|
927
|
+
const common = {
|
|
928
|
+
name: statedesc.name,
|
|
929
|
+
type: statedesc.type,
|
|
930
|
+
unit: statedesc.unit,
|
|
931
|
+
read: statedesc.read,
|
|
932
|
+
write: statedesc.write,
|
|
933
|
+
icon: statedesc.icon,
|
|
934
|
+
role: statedesc.role,
|
|
935
|
+
min: statedesc.min,
|
|
936
|
+
max: statedesc.max,
|
|
937
|
+
};
|
|
938
|
+
|
|
939
|
+
if (typeof value === 'object' && value.hasOwnProperty('stateid')) {
|
|
940
|
+
stateID = `${stateID}.${value.stateid}`;
|
|
941
|
+
if (value.hasOwnProperty('unit')) {
|
|
942
|
+
common.unit = value.unit;
|
|
943
|
+
}
|
|
944
|
+
common.name = value.name ? value.name : value.stateid;
|
|
945
|
+
common.role = value.role ? `value.${value.role}` : 'number';
|
|
946
|
+
value = value.value;
|
|
947
|
+
}
|
|
868
948
|
|
|
949
|
+
// if needs to return value to back after timeout
|
|
950
|
+
if (statedesc.isEvent) {
|
|
951
|
+
this.updateStateWithTimeout(devId, statedesc.id, value, common, 300, (typeof value == typeof (!value) ? !value : ''));
|
|
952
|
+
if (has_elevated_debug) this.emit('device_debug', { ID:debugId, data: { flag:'SUCCESS', IO:true }});
|
|
869
953
|
|
|
870
|
-
|
|
871
|
-
|
|
872
|
-
|
|
954
|
+
} else {
|
|
955
|
+
if (statedesc.prepublish) {
|
|
956
|
+
this.collectOptions(devId, model, options =>
|
|
957
|
+
statedesc.prepublish(devId, value, newvalue => {
|
|
958
|
+
this.updateState(devId, stateID, newvalue, common) }, options)
|
|
959
|
+
);
|
|
960
|
+
if (has_elevated_debug) this.emit('device_debug', { ID:debugId, data: { flag:'SUCCESS', IO:true }});
|
|
961
|
+
} else {
|
|
962
|
+
this.updateState(devId, stateID, value, common, debugId);
|
|
963
|
+
if (has_elevated_debug) this.emit('device_debug', { ID:debugId, data: { flag:'SUCCESS', IO:true }});
|
|
964
|
+
}
|
|
965
|
+
}
|
|
966
|
+
has_published = true;
|
|
967
|
+
}
|
|
968
|
+
} catch (e) {
|
|
969
|
+
const message = `unable to enumerate states of ${devId} for payload ${JSON.stringify(payload)}, ${(e ? e.name : 'undefined')} (${(e ? e.message : '')}).`;
|
|
970
|
+
if (has_elevated_debug) this.emit('device_debug', { ID:debugId, data:{ error:'ESTATE', IO:true }, message:message});
|
|
971
|
+
else this.debug(message);
|
|
972
|
+
}
|
|
973
|
+
const message = `No value published for device ${devId}`;
|
|
974
|
+
if (!has_published && has_elevated_debug) this.emit('device_debug', { ID:debugId, data:{ error:'NOVAL', IO:true }, message:message});
|
|
975
|
+
else this.debug(message);
|
|
976
|
+
}
|
|
977
|
+
else {
|
|
978
|
+
const message = `ELEVATED IE05 - NOSTATE: No states matching the payload ${JSON.stringify(payload)} for device ${devId}`;
|
|
979
|
+
if (has_elevated_debug) this.emit('device_debug', { ID:debugId, data:{ error:'NOSTATE', IO:true }, message});
|
|
980
|
+
else this.debug(message);
|
|
981
|
+
}
|
|
873
982
|
}
|
|
874
|
-
|
|
875
|
-
|
|
876
|
-
this.elevatedMessage(devId, `ELEVATED IE02: no device states for device ${devId} type '${model}'`, true)
|
|
877
|
-
return;
|
|
983
|
+
catch (error) {
|
|
984
|
+
this.error('Something went horribly wrong: ' + (error && error.message ? error.message : 'no reason given'));
|
|
878
985
|
}
|
|
879
|
-
|
|
880
|
-
let has_published = false;
|
|
986
|
+
}
|
|
881
987
|
|
|
882
|
-
|
|
883
|
-
|
|
884
|
-
|
|
885
|
-
|
|
886
|
-
|
|
887
|
-
|
|
888
|
-
|
|
889
|
-
const statedesc = states[stateInd];
|
|
890
|
-
let value;
|
|
891
|
-
if (statedesc.getter) {
|
|
892
|
-
value = statedesc.getter(payload);
|
|
893
|
-
} else {
|
|
894
|
-
value = payload[statedesc.prop || statedesc.id];
|
|
895
|
-
}
|
|
896
|
-
// checking value
|
|
897
|
-
if (value === undefined || value === null) {
|
|
898
|
-
continue;
|
|
899
|
-
}
|
|
988
|
+
async processConverters(converters, devId, model, mappedModel, message, meta, debugId) {
|
|
989
|
+
for (const converter of converters) {
|
|
990
|
+
const publish = (payload, dID) => {
|
|
991
|
+
if (typeof payload === 'object') {
|
|
992
|
+
this.publishToState(devId, model, payload,dID);
|
|
993
|
+
}
|
|
994
|
+
};
|
|
900
995
|
|
|
901
|
-
|
|
996
|
+
const options = await new Promise((resolve, reject) => {
|
|
997
|
+
this.collectOptions(devId, model, (options) => {
|
|
998
|
+
resolve(options);
|
|
999
|
+
});
|
|
1000
|
+
});
|
|
902
1001
|
|
|
903
|
-
|
|
904
|
-
|
|
905
|
-
|
|
1002
|
+
const payload = await new Promise((resolve, reject) => {
|
|
1003
|
+
const payloadConv = converter.convert(mappedModel, message, publish, options, meta);
|
|
1004
|
+
if (typeof payloadConv === 'object') {
|
|
1005
|
+
resolve(payloadConv);
|
|
1006
|
+
}
|
|
1007
|
+
});
|
|
906
1008
|
|
|
907
|
-
|
|
908
|
-
|
|
909
|
-
|
|
910
|
-
unit: statedesc.unit,
|
|
911
|
-
read: statedesc.read,
|
|
912
|
-
write: statedesc.write,
|
|
913
|
-
icon: statedesc.icon,
|
|
914
|
-
role: statedesc.role,
|
|
915
|
-
min: statedesc.min,
|
|
916
|
-
max: statedesc.max,
|
|
917
|
-
};
|
|
918
|
-
|
|
919
|
-
if (typeof value === 'object' && value.hasOwnProperty('stateid')) {
|
|
920
|
-
stateID = `${stateID}.${value.stateid}`;
|
|
921
|
-
if (value.hasOwnProperty('unit')) {
|
|
922
|
-
common.unit = value.unit;
|
|
923
|
-
}
|
|
924
|
-
common.name = value.name ? value.name : value.stateid;
|
|
925
|
-
common.role = value.role ? `value.${value.role}` : 'number';
|
|
926
|
-
value = value.value;
|
|
927
|
-
}
|
|
1009
|
+
publish(payload, debugId);
|
|
1010
|
+
}
|
|
1011
|
+
}
|
|
928
1012
|
|
|
929
|
-
|
|
930
|
-
|
|
931
|
-
|
|
932
|
-
|
|
933
|
-
|
|
934
|
-
|
|
935
|
-
|
|
936
|
-
|
|
937
|
-
|
|
938
|
-
|
|
939
|
-
|
|
1013
|
+
|
|
1014
|
+
async onZigbeeEvent(type, entity, message) {
|
|
1015
|
+
this.debug(`Type ${type} device ${safeJsonStringify(entity)} incoming event: ${safeJsonStringify(message)}`);
|
|
1016
|
+
|
|
1017
|
+
const device = entity.device;
|
|
1018
|
+
const mappedModel = entity.mapped;
|
|
1019
|
+
const model = entity.mapped ? entity.mapped.model : entity.device.modelID;
|
|
1020
|
+
const cluster = message.cluster;
|
|
1021
|
+
const devId = device.ieeeAddr.substr(2);
|
|
1022
|
+
const meta = {device};
|
|
1023
|
+
|
|
1024
|
+
const has_elevated_debug = this.checkDebugDevice(devId);
|
|
1025
|
+
const debugId = Date.now();
|
|
1026
|
+
|
|
1027
|
+
// raw message data for logging and msg_from_zigbee
|
|
1028
|
+
const msgForState = Object.assign({}, message);
|
|
1029
|
+
delete msgForState['device'];
|
|
1030
|
+
delete msgForState['endpoint'];
|
|
1031
|
+
msgForState['endpoint_id'] = message.endpoint.ID;
|
|
1032
|
+
|
|
1033
|
+
if (has_elevated_debug) {
|
|
1034
|
+
const message = `Zigbee Event of Type ${type} from device ${device.ieeeAddr}, incoming event: ${safeJsonStringify(msgForState)}`;
|
|
1035
|
+
this.emit('device_debug', { ID:debugId, data: { ID: device.ieeeAddr, payload:safeJsonStringify(msgForState), flag:'01', IO:true }, message:message});
|
|
1036
|
+
|
|
1037
|
+
}
|
|
1038
|
+
// this assigment give possibility to use iobroker logger in code of the converters, via meta.logger
|
|
1039
|
+
meta.logger = this;
|
|
1040
|
+
|
|
1041
|
+
await this.adapter.checkIfModelUpdate(entity);
|
|
1042
|
+
|
|
1043
|
+
let _voltage = 0;
|
|
1044
|
+
let _temperature = 0;
|
|
1045
|
+
let _humidity = 0;
|
|
1046
|
+
|
|
1047
|
+
let isMessure = false;
|
|
1048
|
+
let isBattKey = false;
|
|
1049
|
+
|
|
1050
|
+
if (mappedModel && mappedModel.meta && mappedModel.meta.battery) {
|
|
1051
|
+
const isVoltage = mappedModel.meta.battery.hasOwnProperty('voltageToPercentage');
|
|
1052
|
+
|
|
1053
|
+
if (isVoltage) {
|
|
1054
|
+
const keys = Object.keys(message.data);
|
|
1055
|
+
|
|
1056
|
+
for (const key of keys) {
|
|
1057
|
+
const value = message.data[key];
|
|
1058
|
+
|
|
1059
|
+
if (value && value[1]) {
|
|
1060
|
+
if (key == 65282 && value[1][1]) {
|
|
1061
|
+
_voltage = value[1][1].elmVal;
|
|
1062
|
+
isBattKey = true;
|
|
1063
|
+
break;
|
|
1064
|
+
}
|
|
1065
|
+
if (key == 65281) {
|
|
1066
|
+
_voltage = value[1];
|
|
1067
|
+
isBattKey = true;
|
|
1068
|
+
_temperature = value[100];
|
|
1069
|
+
_temperature = _temperature /100;
|
|
1070
|
+
_humidity = value[101];
|
|
1071
|
+
_humidity = _humidity / 100;
|
|
1072
|
+
isMessure = true;
|
|
1073
|
+
break;
|
|
940
1074
|
}
|
|
941
1075
|
}
|
|
942
|
-
has_published = true;
|
|
943
1076
|
}
|
|
944
|
-
} catch (e) {
|
|
945
|
-
this.debug(`No states in device ${devId} : payload ${JSON.stringify(payload)}`);
|
|
946
|
-
if (has_elevated_debug)
|
|
947
|
-
this.elevatedMessage(devId, `ELEVATED IE03: error when enumerating states of ${devId} for payload ${JSON.stringify(payload)}, ${(e ? e.name : 'undefined')} (${(e ? e.message : '')}).`, true);
|
|
948
1077
|
}
|
|
949
|
-
|
|
950
|
-
this.elevatedMessage(devId, `ELEVATED IE04: No value published for device ${devId}`, true);
|
|
1078
|
+
}
|
|
951
1079
|
|
|
1080
|
+
// always publish link_quality and battery
|
|
1081
|
+
if (message.linkquality) { // send battery with
|
|
1082
|
+
this.publishToState(devId, model, {linkquality: message.linkquality}, debugId);
|
|
1083
|
+
if (isBattKey) {
|
|
1084
|
+
this.publishToState(devId, model, {voltage: _voltage}, debugId);
|
|
1085
|
+
const battProz = zigbeeHerdsmanConvertersUtils.batteryVoltageToPercentage(_voltage,entity.mapped.meta.battery.voltageToPercentage);
|
|
1086
|
+
this.publishToState(devId, model, {battery: battProz}, debugId);
|
|
1087
|
+
}
|
|
1088
|
+
if (isMessure) {
|
|
1089
|
+
this.publishToState(devId, model, {temperature: _temperature}, debugId);
|
|
1090
|
+
this.publishToState(devId, model, {humidity: _humidity}), debugId;
|
|
952
1091
|
}
|
|
953
1092
|
}
|
|
954
|
-
|
|
955
|
-
|
|
956
|
-
|
|
1093
|
+
|
|
1094
|
+
// publish raw event to "from_zigbee"
|
|
1095
|
+
// some cleanup
|
|
1096
|
+
|
|
1097
|
+
this.publishToState(devId, model, {msg_from_zigbee: safeJsonStringify(msgForState)}, -1);
|
|
1098
|
+
|
|
1099
|
+
if (!entity.mapped) {
|
|
1100
|
+
return;
|
|
1101
|
+
}
|
|
1102
|
+
|
|
1103
|
+
let converters = mappedModel.fromZigbee.filter(c => c && c.cluster === cluster && (
|
|
1104
|
+
Array.isArray(c.type) ? c.type.includes(type) : c.type === type));
|
|
1105
|
+
|
|
1106
|
+
|
|
1107
|
+
if (!converters.length && type === 'readResponse') {
|
|
1108
|
+
converters = mappedModel.fromZigbee.filter(c => c.cluster === cluster && (
|
|
1109
|
+
Array.isArray(c.type) ? c.type.includes('attributeReport') : c.type === 'attributeReport'));
|
|
957
1110
|
}
|
|
1111
|
+
|
|
1112
|
+
if (!converters.length) {
|
|
1113
|
+
if (type !== 'readResponse') {
|
|
1114
|
+
const message = `No converter available for '${mappedModel.model}' '${devId}' with cluster '${cluster}' and type '${type}'`;
|
|
1115
|
+
if (has_elevated_debug) this.emit('device_debug', { ID:debugId, data: { error:'NOCONV', IO:true }, message:message});
|
|
1116
|
+
else this.debug(message);
|
|
1117
|
+
}
|
|
1118
|
+
return;
|
|
1119
|
+
}
|
|
1120
|
+
|
|
1121
|
+
meta.state = { state: '' }; // for tuya
|
|
1122
|
+
|
|
1123
|
+
this.processConverters(converters, devId, model, mappedModel, message, meta, debugId)
|
|
1124
|
+
.catch((error) => {
|
|
1125
|
+
// 'Error: Expected one of: 0, 1, got: 'undefined''
|
|
1126
|
+
if (cluster !== '64529') {
|
|
1127
|
+
if (has_elevated_debug) this.emit('device_debug', { ID:debugId, data: { error:'EPROC', IO:true }});
|
|
1128
|
+
this.error(`Error while processing converters DEVICE_ID: '${devId}' cluster '${cluster}' type '${type}'`);
|
|
1129
|
+
}
|
|
1130
|
+
});
|
|
958
1131
|
}
|
|
959
1132
|
|
|
1133
|
+
|
|
1134
|
+
|
|
960
1135
|
}
|
|
961
1136
|
|
|
962
1137
|
module.exports = StatesController;
|
|
@@ -178,7 +178,7 @@ class DeviceAvailability extends BaseExtension {
|
|
|
178
178
|
this.publishAvailability(device, false);
|
|
179
179
|
if (pingCount.failed++ <= this.max_ping) {
|
|
180
180
|
if (pingCount.failed < 2 && pingCount.reported < this.max_ping) {
|
|
181
|
-
this.
|
|
181
|
+
this.info(`Failed to ping ${ieeeAddr} ${device.modelID}`);
|
|
182
182
|
pingCount.reported++;
|
|
183
183
|
} else {
|
|
184
184
|
this.debug(`Failed to ping ${ieeeAddr} ${device.modelID} on ${pingCount} consecutive attempts`);
|
|
@@ -186,7 +186,7 @@ class DeviceAvailability extends BaseExtension {
|
|
|
186
186
|
this.setTimerPingable(device, pingCount.failed);
|
|
187
187
|
this.ping_counters[device.ieeeAddr] = pingCount;
|
|
188
188
|
} else {
|
|
189
|
-
this.
|
|
189
|
+
this.info(`Stopping to ping ${ieeeAddr} ${device.modelID} after ${pingCount.failed} ping attempts`);
|
|
190
190
|
}
|
|
191
191
|
}
|
|
192
192
|
}
|