iobroker.zigbee 2.0.3 → 2.0.4
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 +38 -51
- package/admin/admin.js +19 -211
- package/admin/img/philips_hue_lom001.png +0 -0
- package/admin/tab_m.html +8 -13
- 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 +62 -28
- package/lib/commands.js +1 -16
- package/lib/developer.js +0 -0
- package/lib/exposes.js +1 -1
- package/lib/groups.js +8 -6
- package/lib/localConfig.js +2 -1
- package/lib/ota.js +6 -6
- package/lib/statescontroller.js +97 -270
- package/lib/zbDeviceAvailability.js +2 -2
- package/lib/zbDeviceConfigure.js +15 -22
- package/lib/zigbeecontroller.js +6 -13
- package/main.js +193 -131
- package/package.json +2 -2
package/lib/statescontroller.js
CHANGED
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
'use strict';
|
|
2
2
|
|
|
3
|
-
const
|
|
4
|
-
const { EventEmitter } = require('events');
|
|
3
|
+
const EventEmitter = require('events').EventEmitter;
|
|
5
4
|
const statesMapping = require('./devices');
|
|
6
|
-
const
|
|
5
|
+
const getAdId = require('./utils').getAdId;
|
|
6
|
+
const getZbId = require('./utils').getZbId;
|
|
7
7
|
const fs = require('fs');
|
|
8
8
|
const axios = require('axios');
|
|
9
9
|
const localConfig = require('./localConfig');
|
|
@@ -14,8 +14,7 @@ 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 {
|
|
18
|
-
const zigbeeHerdsmanConvertersUtils = require('zigbee-herdsman-converters/lib/utils');
|
|
17
|
+
const { numberWithinRange } = require('zigbee-herdsman-converters/lib/utils');
|
|
19
18
|
|
|
20
19
|
|
|
21
20
|
class StatesController extends EventEmitter {
|
|
@@ -33,7 +32,6 @@ class StatesController extends EventEmitter {
|
|
|
33
32
|
this.ImagesToDownload = [];
|
|
34
33
|
this.stashedErrors = {};
|
|
35
34
|
this.stashedUnknownModels = [];
|
|
36
|
-
this.debugMessages = { nodevice:{ in:[], out: []} };
|
|
37
35
|
}
|
|
38
36
|
|
|
39
37
|
info(message, data) {
|
|
@@ -87,10 +85,6 @@ class StatesController extends EventEmitter {
|
|
|
87
85
|
return rv;
|
|
88
86
|
}
|
|
89
87
|
|
|
90
|
-
debugMessagesById() {
|
|
91
|
-
return this.debugMessages;
|
|
92
|
-
}
|
|
93
|
-
|
|
94
88
|
async AddModelFromHerdsman(device, model) {
|
|
95
89
|
// this.warn('addModelFromHerdsman ' + JSON.stringify(model) + ' ' + JSON.stringify(this.localConfig.getOverrideWithKey(model, 'legacy', true)));
|
|
96
90
|
if (this.localConfig.getOverrideWithKey(model, 'legacy', true)) {
|
|
@@ -158,7 +152,6 @@ class StatesController extends EventEmitter {
|
|
|
158
152
|
if (!this.adapter.zbController || !this.adapter.zbController.connected()) {
|
|
159
153
|
return;
|
|
160
154
|
}
|
|
161
|
-
const debugId = Date.now();
|
|
162
155
|
if (state && !state.ack) {
|
|
163
156
|
if (id.endsWith('pairingCountdown') || id.endsWith('pairingMessage') || id.endsWith('connection')) {
|
|
164
157
|
return;
|
|
@@ -173,18 +166,16 @@ class StatesController extends EventEmitter {
|
|
|
173
166
|
return;
|
|
174
167
|
}
|
|
175
168
|
|
|
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)}`);
|
|
176
173
|
const devId = getAdId(this.adapter, id); // iobroker device id
|
|
177
174
|
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)}`);
|
|
184
175
|
// const stateKey = id.split('.')[3];
|
|
185
176
|
const arr = /zigbee.[0-9].[^.]+.(\S+)/gm.exec(id);
|
|
186
177
|
if (arr[1] === undefined) {
|
|
187
|
-
|
|
178
|
+
this.warn(`unable to extract id from state ${id}`);
|
|
188
179
|
return;
|
|
189
180
|
}
|
|
190
181
|
const stateKey = arr[1];
|
|
@@ -208,7 +199,7 @@ class StatesController extends EventEmitter {
|
|
|
208
199
|
|
|
209
200
|
}
|
|
210
201
|
this.collectOptions(id.split('.')[2], model, options =>
|
|
211
|
-
this.publishFromState(deviceId, model, stateKey, state, options
|
|
202
|
+
this.publishFromState(deviceId, model, stateKey, state, options));
|
|
212
203
|
}
|
|
213
204
|
});
|
|
214
205
|
}
|
|
@@ -314,38 +305,29 @@ class StatesController extends EventEmitter {
|
|
|
314
305
|
}, (stateDesc.compositeTimeout ? stateDesc.compositeTimeout : 100) * factor);
|
|
315
306
|
}
|
|
316
307
|
|
|
317
|
-
async publishFromState(deviceId, model, stateKey, state, options
|
|
308
|
+
async publishFromState(deviceId, model, stateKey, state, options) {
|
|
318
309
|
this.debug(`Change state '${stateKey}' at device ${deviceId} type '${model}'`);
|
|
319
310
|
const elevated = this.checkDebugDevice(deviceId);
|
|
320
311
|
|
|
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
|
-
}
|
|
312
|
+
if (elevated) this.warn(`ELEVATED O02: Change state '${stateKey}' at device ${deviceId} type '${model}'`);
|
|
325
313
|
|
|
326
314
|
const devStates = await this.getDevStates(deviceId, model);
|
|
327
315
|
if (!devStates) {
|
|
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
|
-
}
|
|
316
|
+
if (elevated) this.error(`ELEVATED OE1: no device states for device ${deviceId} type '${model}'`);
|
|
332
317
|
return;
|
|
333
318
|
}
|
|
334
319
|
const commonStates = statesMapping.commonStates.find(statedesc => stateKey === statedesc.id);
|
|
335
320
|
const stateDesc = (commonStates === undefined ? devStates.states.find(statedesc => stateKey === statedesc.id) : commonStates);
|
|
336
321
|
const stateModel = devStates.stateModel;
|
|
337
322
|
if (!stateDesc) {
|
|
338
|
-
|
|
339
|
-
if (elevated) this.emit('device_debug', { ID:debugId, data: { states:[{id:state.ID, value:'unknown', payload:'unknown'}], error: 'NOSTKEY' , IO:false }, message:message});
|
|
323
|
+
this.error(`No state available for '${model}' with key '${stateKey}'`);
|
|
340
324
|
return;
|
|
341
325
|
}
|
|
342
326
|
|
|
343
327
|
const value = state.val;
|
|
344
328
|
if (value === undefined || value === '') {
|
|
345
|
-
if (elevated)
|
|
346
|
-
|
|
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
|
-
}
|
|
329
|
+
if (elevated)
|
|
330
|
+
this.error(`ELEVATED OE2: no value for device ${deviceId} type '${model}'`);
|
|
349
331
|
return;
|
|
350
332
|
}
|
|
351
333
|
let stateList = [{stateDesc: stateDesc, value: value, index: 0, timeout: 0, source:state.from}];
|
|
@@ -362,8 +344,7 @@ class StatesController extends EventEmitter {
|
|
|
362
344
|
}
|
|
363
345
|
} catch (e) {
|
|
364
346
|
this.sendError(e);
|
|
365
|
-
|
|
366
|
-
this.error('Exception caught in publishfromstate: ' + (e && e.message ? e.message : 'no error message given'));
|
|
347
|
+
this.error('Exception caught in publishfromstate');
|
|
367
348
|
}
|
|
368
349
|
|
|
369
350
|
});
|
|
@@ -378,7 +359,7 @@ class StatesController extends EventEmitter {
|
|
|
378
359
|
readAfterWriteStates = readAfterWriteStates.concat(readAfterWriteStateDesc.id));
|
|
379
360
|
}
|
|
380
361
|
|
|
381
|
-
this.emit('changed', deviceId, model, stateModel, stateList, options
|
|
362
|
+
this.emit('changed', deviceId, model, stateModel, stateList, options);
|
|
382
363
|
}
|
|
383
364
|
|
|
384
365
|
async renameDevice(id, newName) {
|
|
@@ -432,7 +413,7 @@ class StatesController extends EventEmitter {
|
|
|
432
413
|
let statename = state._id;
|
|
433
414
|
const arr = /zigbee.[0-9].[^.]+.(\S+)/gm.exec(statename);
|
|
434
415
|
if (arr[1] === undefined) {
|
|
435
|
-
this.
|
|
416
|
+
this.warn(`unable to extract id from state ${statename}`);
|
|
436
417
|
const idx = statename.lastIndexOf('.');
|
|
437
418
|
if (idx > -1) {
|
|
438
419
|
statename = statename.slice(idx + 1);
|
|
@@ -774,7 +755,7 @@ class StatesController extends EventEmitter {
|
|
|
774
755
|
this.adapter.fileExists(namespace, target, async (err,result) => {
|
|
775
756
|
if (result) return;
|
|
776
757
|
const src = `${tmpdir()}/${path.basename(target)}`;
|
|
777
|
-
|
|
758
|
+
const msg = `downloading ${url} to ${src}`;
|
|
778
759
|
if (this.ImagesToDownload.indexOf(url) ==-1) {
|
|
779
760
|
await this.downloadIcon(url, src)
|
|
780
761
|
try {
|
|
@@ -792,6 +773,7 @@ class StatesController extends EventEmitter {
|
|
|
792
773
|
this.info(`copied ${src} to ${target}.`)
|
|
793
774
|
fs.rm(src, (err) => {
|
|
794
775
|
if (err) this.warn(`error removing ${src} : ${JSON.stringify(err)}`);
|
|
776
|
+
else this.info(`removed ${src}`)
|
|
795
777
|
});
|
|
796
778
|
})
|
|
797
779
|
}
|
|
@@ -879,257 +861,102 @@ class StatesController extends EventEmitter {
|
|
|
879
861
|
this.emit('debugmessage', {id: id, message:message});
|
|
880
862
|
}
|
|
881
863
|
|
|
882
|
-
async publishToState(devId, model, payload
|
|
883
|
-
|
|
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
|
-
}
|
|
918
|
-
|
|
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
|
-
}
|
|
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 }});
|
|
951
|
-
|
|
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
|
-
}
|
|
980
|
-
}
|
|
981
|
-
catch (error) {
|
|
982
|
-
this.error('Something went horribly wrong: ' + (error && error.message ? error.message : 'no reason given'));
|
|
983
|
-
}
|
|
984
|
-
}
|
|
985
|
-
|
|
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
|
-
};
|
|
864
|
+
async publishToState(devId, model, payload) {
|
|
865
|
+
const devStates = await this.getDevStates(`0x${devId}`, model);
|
|
993
866
|
|
|
994
|
-
|
|
995
|
-
this.collectOptions(devId, model, (options) => {
|
|
996
|
-
resolve(options);
|
|
997
|
-
});
|
|
998
|
-
});
|
|
867
|
+
const has_elevated_debug = (this.checkDebugDevice(devId) && !payload.hasOwnProperty('msg_from_zigbee'));
|
|
999
868
|
|
|
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
|
-
});
|
|
1006
869
|
|
|
1007
|
-
|
|
870
|
+
if (has_elevated_debug)
|
|
871
|
+
{
|
|
872
|
+
this.elevatedMessage(devId, `ELEVATED I01: message received '${JSON.stringify(payload)}' from device ${devId} type '${model}'`, false);
|
|
1008
873
|
}
|
|
1009
|
-
|
|
1010
|
-
|
|
1011
|
-
|
|
1012
|
-
|
|
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
|
-
|
|
874
|
+
if (!devStates) {
|
|
875
|
+
if (has_elevated_debug)
|
|
876
|
+
this.elevatedMessage(devId, `ELEVATED IE02: no device states for device ${devId} type '${model}'`, true)
|
|
877
|
+
return;
|
|
1035
878
|
}
|
|
1036
|
-
//
|
|
1037
|
-
|
|
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;
|
|
879
|
+
// find states for payload
|
|
880
|
+
let has_published = false;
|
|
1047
881
|
|
|
1048
|
-
if (
|
|
1049
|
-
|
|
882
|
+
if (devStates.states !== undefined) {
|
|
883
|
+
try {
|
|
884
|
+
const states = statesMapping.commonStates.concat(
|
|
885
|
+
devStates.states.filter(statedesc => payload.hasOwnProperty(statedesc.prop || statedesc.id))
|
|
886
|
+
);
|
|
887
|
+
|
|
888
|
+
for (const stateInd in states) {
|
|
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
|
+
}
|
|
1050
900
|
|
|
1051
|
-
|
|
1052
|
-
const keys = Object.keys(message.data);
|
|
901
|
+
let stateID = statedesc.id;
|
|
1053
902
|
|
|
1054
|
-
|
|
1055
|
-
|
|
903
|
+
if (has_elevated_debug) {
|
|
904
|
+
this.elevatedMessage(devId, `ELEVATED I02: value generated '${JSON.stringify(value)}' from device ${devId} for '${statedesc.name}'`, false);
|
|
905
|
+
}
|
|
1056
906
|
|
|
1057
|
-
|
|
1058
|
-
|
|
1059
|
-
|
|
1060
|
-
|
|
1061
|
-
|
|
907
|
+
const common = {
|
|
908
|
+
name: statedesc.name,
|
|
909
|
+
type: statedesc.type,
|
|
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;
|
|
1062
923
|
}
|
|
1063
|
-
|
|
1064
|
-
|
|
1065
|
-
|
|
1066
|
-
|
|
1067
|
-
|
|
1068
|
-
|
|
1069
|
-
|
|
1070
|
-
|
|
1071
|
-
|
|
924
|
+
common.name = value.name ? value.name : value.stateid;
|
|
925
|
+
common.role = value.role ? `value.${value.role}` : 'number';
|
|
926
|
+
value = value.value;
|
|
927
|
+
}
|
|
928
|
+
|
|
929
|
+
// if needs to return value to back after timeout
|
|
930
|
+
if (statedesc.isEvent) {
|
|
931
|
+
this.updateStateWithTimeout(devId, statedesc.id, value, common, 300, (typeof value == typeof (!value) ? !value : ''));
|
|
932
|
+
} else {
|
|
933
|
+
if (statedesc.prepublish) {
|
|
934
|
+
this.collectOptions(devId, model, options =>
|
|
935
|
+
statedesc.prepublish(devId, value, newvalue =>
|
|
936
|
+
this.updateState(devId, stateID, newvalue, common), options)
|
|
937
|
+
);
|
|
938
|
+
} else {
|
|
939
|
+
this.updateState(devId, stateID, value, common);
|
|
1072
940
|
}
|
|
1073
941
|
}
|
|
942
|
+
has_published = true;
|
|
1074
943
|
}
|
|
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);
|
|
1075
948
|
}
|
|
1076
|
-
|
|
949
|
+
if (!has_published && has_elevated_debug) {
|
|
950
|
+
this.elevatedMessage(devId, `ELEVATED IE04: No value published for device ${devId}`, true);
|
|
1077
951
|
|
|
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
952
|
}
|
|
1086
|
-
if (isMessure) {
|
|
1087
|
-
this.publishToState(devId, model, {temperature: _temperature}, debugId);
|
|
1088
|
-
this.publishToState(devId, model, {humidity: _humidity}), debugId;
|
|
1089
|
-
}
|
|
1090
|
-
}
|
|
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
953
|
}
|
|
1109
|
-
|
|
1110
|
-
|
|
1111
|
-
|
|
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;
|
|
954
|
+
else {
|
|
955
|
+
if (has_elevated_debug)
|
|
956
|
+
this.elevatedMessage(devId, `ELEVATED IE05: No states matching the payload ${JSON.stringify(payload)} for device ${devId}`, true);
|
|
1117
957
|
}
|
|
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
|
-
});
|
|
1129
958
|
}
|
|
1130
959
|
|
|
1131
|
-
|
|
1132
|
-
|
|
1133
960
|
}
|
|
1134
961
|
|
|
1135
962
|
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.warn(`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.warn(`Stopping to ping ${ieeeAddr} ${device.modelID} after ${pingCount.failed} ping attempts`);
|
|
190
190
|
}
|
|
191
191
|
}
|
|
192
192
|
}
|
package/lib/zbDeviceConfigure.js
CHANGED
|
@@ -14,8 +14,6 @@ class DeviceConfigure extends BaseExtension {
|
|
|
14
14
|
this.configuring = new Set();
|
|
15
15
|
this.attempts = {};
|
|
16
16
|
this.name = 'DeviceConfigure';
|
|
17
|
-
|
|
18
|
-
this.configureKeys = {};
|
|
19
17
|
}
|
|
20
18
|
|
|
21
19
|
setOptions(options) {
|
|
@@ -29,24 +27,23 @@ class DeviceConfigure extends BaseExtension {
|
|
|
29
27
|
if (!mappedDevice || !mappedDevice.configure) {
|
|
30
28
|
return false;
|
|
31
29
|
}
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
30
|
+
if (device.meta.hasOwnProperty('configured') &&
|
|
31
|
+
zigbeeHerdsmanConverters.getConfigureKey(mappedDevice)) {
|
|
32
|
+
return false;
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
return (device.interviewing !== true && this.checkDelayedConfigure(device.ieeeAddr)>0);
|
|
37
36
|
}
|
|
38
37
|
|
|
39
38
|
checkDelayedConfigure(device, num) {
|
|
40
39
|
if (!this.delayedConfigure.hasOwnProperty(device.ieeeAddr)) {
|
|
41
40
|
if (num && num > 0) {
|
|
42
|
-
// this.warn('adding dev ' + device.ieeeAddr + ' to delayedConfigure with ' + num + ' attempts');
|
|
43
41
|
this.delayedConfigure[device.ieeeAddr] = { maxAttempts:num };
|
|
44
42
|
return num;
|
|
45
43
|
}
|
|
46
44
|
return 0;
|
|
47
45
|
}
|
|
48
46
|
const dc = this.delayedConfigure[device.ieeeAddr];
|
|
49
|
-
// this.warn('checkDelayedConfigure for ' + device.ieeeAddr + ' with ' + JSON.stringify(dc));
|
|
50
47
|
dc.maxAttempts--;
|
|
51
48
|
if (dc.maxAttempts > 0) return dc.maxAttempts;
|
|
52
49
|
if (num && num > 0) {
|
|
@@ -93,7 +90,6 @@ class DeviceConfigure extends BaseExtension {
|
|
|
93
90
|
try {
|
|
94
91
|
const device = data.device;
|
|
95
92
|
if (this.shouldConfigure(device, mappedDevice)) {
|
|
96
|
-
this.debug('ShouldConfigure ' + device.ieeeAddr);
|
|
97
93
|
this.configure(device, mappedDevice);
|
|
98
94
|
}
|
|
99
95
|
} catch (error) {
|
|
@@ -102,7 +98,6 @@ class DeviceConfigure extends BaseExtension {
|
|
|
102
98
|
}
|
|
103
99
|
}
|
|
104
100
|
|
|
105
|
-
|
|
106
101
|
onDeviceRemove(device) {
|
|
107
102
|
try {
|
|
108
103
|
if (this.configuring.has(device.ieeeAddr)) {
|
|
@@ -146,16 +141,14 @@ class DeviceConfigure extends BaseExtension {
|
|
|
146
141
|
const coordinatorEndpoint = await this.zigbee.getDevicesByType('Coordinator')[0].endpoints[0];
|
|
147
142
|
try {
|
|
148
143
|
if (mappedDevice) {
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
}
|
|
144
|
+
this.info(`-> Configuring ${device.ieeeAddr} ${device.modelID}`);
|
|
145
|
+
const promises = [];
|
|
146
|
+
promises.push(mappedDevice.configure);
|
|
147
|
+
await Promise.all(promises.map(callback => callback(device, coordinatorEndpoint, mappedDevice)))
|
|
148
|
+
|
|
149
|
+
//await mappedDevice.configure(device, coordinatorEndpoint, this);
|
|
150
|
+
|
|
157
151
|
device.meta.configured = zigbeeHerdsmanConverters.getConfigureKey(mappedDevice);
|
|
158
|
-
this.configureKeys[device.ieeeAddr] = device.meta.configured;
|
|
159
152
|
device.save();
|
|
160
153
|
this.info(`DeviceConfigure successful ${device.ieeeAddr} ${device.modelID}`);
|
|
161
154
|
this.delayedConfigureAttempt(device, true);
|
|
@@ -169,8 +162,8 @@ class DeviceConfigure extends BaseExtension {
|
|
|
169
162
|
if (error && error.message && error.message.match(/(\d+)ms/gm)) {
|
|
170
163
|
// timeout message - we do want to start the configure chain
|
|
171
164
|
const num = this.delayedConfigureAttempt(device, false);
|
|
172
|
-
this.
|
|
173
|
-
return `
|
|
165
|
+
this.info(`Delayed configure for ${device.ieeeAddr} ${device.modelID}: ${num} attempts remaining`)
|
|
166
|
+
return `Delayed configure for ${device.ieeeAddr} ${device.modelID}: ${num} attempts remaining`;
|
|
174
167
|
} else {
|
|
175
168
|
this.sendError(error);
|
|
176
169
|
this.warn(`${device.ieeeAddr} ${device.modelID} Failed to configure. --> ${error && error.message ? error.message : ' no error message given'} `);
|