iobroker.zigbee 2.0.5 → 3.0.0
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 +7 -1
- package/admin/admin.js +281 -102
- package/admin/index_m.html +168 -124
- package/admin/tab_m.html +7 -3
- package/docs/de/img/Bild30.png +0 -0
- package/docs/de/img/Bild38.png +0 -0
- package/docs/de/img/Info.png +0 -0
- package/docs/de/img/Zigbee_config_de.jpg +0 -0
- package/docs/de/img/battery.png +0 -0
- package/docs/de/img/debug.png +0 -0
- package/docs/de/img/delete.png +0 -0
- package/docs/de/img/disconnected.png +0 -0
- package/docs/de/img/edit_grp.png +0 -0
- package/docs/de/img/edit_image.png +0 -0
- package/docs/de/img/grp_nok.png +0 -0
- package/docs/de/img/grp_ok.png +0 -0
- package/docs/de/img/on_off.png +0 -0
- package/docs/de/img/reconfigure.png +0 -0
- package/docs/de/readme.md +52 -43
- package/docs/en/img/Zigbee_config_en.png +0 -0
- package/docs/en/img/Zigbee_pairing_en.png +0 -0
- package/docs/en/readme.md +71 -66
- package/io-package.json +18 -18
- package/lib/DeviceDebug.js +2 -1
- package/lib/commands.js +165 -33
- package/lib/devices.js +2 -2
- package/lib/exposes.js +8 -30
- package/lib/localConfig.js +2 -2
- package/lib/seriallist.js +9 -2
- package/lib/statescontroller.js +132 -38
- package/lib/utils.js +41 -11
- package/lib/zbDeviceConfigure.js +9 -2
- package/lib/zigbeecontroller.js +120 -94
- package/main.js +131 -70
- package/package.json +8 -8
- package/docs/en/img/Bild23.png +0 -0
- package/docs/en/img/Bild25.png +0 -0
- package/docs/en/img/Bild26.png +0 -0
- package/docs/en/img/Bild4.png +0 -0
- package/docs/en/img/Bild9.png +0 -0
package/lib/statescontroller.js
CHANGED
|
@@ -34,6 +34,7 @@ class StatesController extends EventEmitter {
|
|
|
34
34
|
this.stashedErrors = {};
|
|
35
35
|
this.stashedUnknownModels = [];
|
|
36
36
|
this.debugMessages = { nodevice:{ in:[], out: []} };
|
|
37
|
+
this.debugActive = true;
|
|
37
38
|
}
|
|
38
39
|
|
|
39
40
|
info(message, data) {
|
|
@@ -75,14 +76,19 @@ class StatesController extends EventEmitter {
|
|
|
75
76
|
|
|
76
77
|
getStashedErrors() {
|
|
77
78
|
const rv = [];
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
79
|
+
try {
|
|
80
|
+
if (Object.keys(this.stashedErrors).length > 0)
|
|
81
|
+
{
|
|
82
|
+
rv.push('<p><b>Stashed Messages</b></p>')
|
|
83
|
+
rv.push(Object.values(this.stashedErrors).join('<br>'));
|
|
84
|
+
}
|
|
85
|
+
if (this.stashedUnknownModels.length > 0) {
|
|
86
|
+
rv.push('<p><b>Devices whithout Model definition</b></p>')
|
|
87
|
+
rv.push()
|
|
88
|
+
}
|
|
82
89
|
}
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
rv.push()
|
|
90
|
+
catch (error) {
|
|
91
|
+
if (this.debugActive) this.debug(`Error collecting stashed errors: ${error && error.message ? error.message : 'no message available'}`);
|
|
86
92
|
}
|
|
87
93
|
return rv;
|
|
88
94
|
}
|
|
@@ -94,14 +100,94 @@ class StatesController extends EventEmitter {
|
|
|
94
100
|
async AddModelFromHerdsman(device, model) {
|
|
95
101
|
// this.warn('addModelFromHerdsman ' + JSON.stringify(model) + ' ' + JSON.stringify(this.localConfig.getOverrideWithKey(model, 'legacy', true)));
|
|
96
102
|
if (this.localConfig.getOverrideWithKey(model, 'legacy', true)) {
|
|
97
|
-
|
|
103
|
+
this.debug('Applying legacy definition for ' + model);
|
|
98
104
|
await this.addLegacyDevice(model);
|
|
99
105
|
}
|
|
100
106
|
else {
|
|
101
|
-
|
|
102
|
-
await statesMapping.addExposeToDevices(device, this, model);
|
|
103
|
-
const
|
|
104
|
-
//
|
|
107
|
+
this.debug('Generating states from exposes for ' + model);
|
|
108
|
+
const modelDesc = await statesMapping.addExposeToDevices(device, this, model);
|
|
109
|
+
const srcIcon = modelDesc.icon;
|
|
110
|
+
// download icon if it external and not undefined
|
|
111
|
+
if (model === undefined) {
|
|
112
|
+
const dev_name = this.verifyDeviceName(device.ieeeAddr.substr(2), model, device.modelID);
|
|
113
|
+
this.warn(`download icon ${dev_name} for undefined Device not available. Check your devices.`);
|
|
114
|
+
} else {
|
|
115
|
+
const model_modif = model.replace(/\//g, '-');
|
|
116
|
+
const pathToAdminIcon = `img/${model_modif}.png`;
|
|
117
|
+
|
|
118
|
+
const namespace = `${this.adapter.name}.admin`;
|
|
119
|
+
if (srcIcon.startsWith('http')) {
|
|
120
|
+
this.adapter.fileExists(namespace, srcIcon, async(err, result) => {
|
|
121
|
+
if (result) {
|
|
122
|
+
this.debug(`icon ${modelDesc.icon} found - no copy needed`);
|
|
123
|
+
return;
|
|
124
|
+
}
|
|
125
|
+
try {
|
|
126
|
+
this.downloadIconToAdmin(srcIcon, pathToAdminIcon)
|
|
127
|
+
modelDesc.icon = pathToAdminIcon;
|
|
128
|
+
} catch (e) {
|
|
129
|
+
this.warn(`ERROR : icon not found at ${srcIcon}`);
|
|
130
|
+
}
|
|
131
|
+
return;
|
|
132
|
+
});
|
|
133
|
+
return;
|
|
134
|
+
}
|
|
135
|
+
const base64Match = srcIcon.match(/data:image\/(.+);base64,/);
|
|
136
|
+
if (base64Match) {
|
|
137
|
+
modelDesc.icon = pathToAdminIcon;
|
|
138
|
+
this.adapter.fileExists(namespace, pathToAdminIcon, async (err,result) => {
|
|
139
|
+
if (result) {
|
|
140
|
+
this.debug(`no need to save icon to ${pathToAdminIcon}`);
|
|
141
|
+
return;
|
|
142
|
+
}
|
|
143
|
+
this.debug(`Saving base64 data to ${pathToAdminIcon}`)
|
|
144
|
+
const buffer = new Buffer(srcIcon.replace(base64Match[0],''), 'base64');
|
|
145
|
+
this.adapter.writeFile(pathToAdminIcon, buffer);
|
|
146
|
+
});
|
|
147
|
+
return;
|
|
148
|
+
}
|
|
149
|
+
modelDesc.icon = pathToAdminIcon;
|
|
150
|
+
this.adapter.fileExists(namespace, pathToAdminIcon, async(err, result) => {
|
|
151
|
+
if (result) {
|
|
152
|
+
this.debug(`icon ${modelDesc.icon} found - no copy needed`);
|
|
153
|
+
return;
|
|
154
|
+
}
|
|
155
|
+
// try 3 options for source file
|
|
156
|
+
let src = srcIcon; // as given
|
|
157
|
+
const locations=[];
|
|
158
|
+
if (!fs.existsSync(src)) {
|
|
159
|
+
locations.push(src);
|
|
160
|
+
src = path.normalize(this.adapter.expandFileName(src));
|
|
161
|
+
} // assumed relative to data folder
|
|
162
|
+
if (!fs.existsSync(src)) {
|
|
163
|
+
locations.push(src);
|
|
164
|
+
src = path.normalize(this.adapter.expandFileName(path.basename(src)));
|
|
165
|
+
} // filename relative to data folder
|
|
166
|
+
if (!fs.existsSync(src)) {
|
|
167
|
+
locations.push(src);
|
|
168
|
+
src = path.normalize(this.adapter.expandFileName(path.join('img',path.basename(src))));
|
|
169
|
+
} // img/<filename> relative to data folder
|
|
170
|
+
if (!fs.existsSync(src)) {
|
|
171
|
+
this.warn(`Unable to copy icon from device definition, looked at ${locations.join(', ')} and ${src}`)
|
|
172
|
+
return;
|
|
173
|
+
}
|
|
174
|
+
fs.readFile(src, (err, data) => {
|
|
175
|
+
if (err) {
|
|
176
|
+
this.error('unable to read ' + src + ' : '+ (err && err.message? err.message:' no message given'))
|
|
177
|
+
return;
|
|
178
|
+
}
|
|
179
|
+
if (data) {
|
|
180
|
+
this.adapter.writeFile(namespace, pathToAdminIcon, data, (err) => {
|
|
181
|
+
if (err) {
|
|
182
|
+
this.error('error writing file ' + path + JSON.stringify(err))
|
|
183
|
+
return;
|
|
184
|
+
}
|
|
185
|
+
this.info('Updated image file ' + pathToAdminIcon);
|
|
186
|
+
});
|
|
187
|
+
}
|
|
188
|
+
})
|
|
189
|
+
})
|
|
190
|
+
}
|
|
105
191
|
}
|
|
106
192
|
}
|
|
107
193
|
|
|
@@ -158,6 +244,9 @@ class StatesController extends EventEmitter {
|
|
|
158
244
|
if (!this.adapter.zbController || !this.adapter.zbController.connected()) {
|
|
159
245
|
return;
|
|
160
246
|
}
|
|
247
|
+
if (id.includes('logLevel')) {
|
|
248
|
+
if (state) this.adapter.updateDebugLevel(state.val);
|
|
249
|
+
}
|
|
161
250
|
const debugId = Date.now();
|
|
162
251
|
if (state && !state.ack) {
|
|
163
252
|
if (id.endsWith('pairingCountdown') || id.endsWith('pairingMessage') || id.endsWith('connection')) {
|
|
@@ -180,7 +269,7 @@ class StatesController extends EventEmitter {
|
|
|
180
269
|
const message = `User state change of state ${id} with value ${state.val} (ack: ${state.ack}) from ${state.from}`;
|
|
181
270
|
this.emit('device_debug', { ID:debugId, data: { ID: deviceId, flag:'01' }, message:message});
|
|
182
271
|
} else
|
|
183
|
-
this.debug(`User stateChange ${id} ${JSON.stringify(state)}`);
|
|
272
|
+
if (this.debugActive) this.debug(`User stateChange ${id} ${JSON.stringify(state)}`);
|
|
184
273
|
// const stateKey = id.split('.')[3];
|
|
185
274
|
const arr = /zigbee.[0-9].[^.]+.(\S+)/gm.exec(id);
|
|
186
275
|
if (arr[1] === undefined) {
|
|
@@ -195,7 +284,7 @@ class StatesController extends EventEmitter {
|
|
|
195
284
|
return;
|
|
196
285
|
}
|
|
197
286
|
if (obj.common.deactivated) {
|
|
198
|
-
this.debug('State Change detected on deactivated Device - ignored');
|
|
287
|
+
if (this.debugActive) this.debug('State Change detected on deactivated Device - ignored');
|
|
199
288
|
return;
|
|
200
289
|
}
|
|
201
290
|
if (model === 'group') {
|
|
@@ -315,7 +404,7 @@ class StatesController extends EventEmitter {
|
|
|
315
404
|
}
|
|
316
405
|
|
|
317
406
|
async publishFromState(deviceId, model, stateKey, state, options, debugId) {
|
|
318
|
-
this.debug(`Change state '${stateKey}' at device ${deviceId} type '${model}'`);
|
|
407
|
+
if (this.debugActive) this.debug(`Change state '${stateKey}' at device ${deviceId} type '${model}'`);
|
|
319
408
|
const elevated = this.checkDebugDevice(deviceId);
|
|
320
409
|
|
|
321
410
|
if (elevated) {
|
|
@@ -390,7 +479,7 @@ class StatesController extends EventEmitter {
|
|
|
390
479
|
objName = (obj.common.type == 'group' ? stateId.replace('_',' ') : obj.common.type);
|
|
391
480
|
}
|
|
392
481
|
this.localConfig.updateDeviceName(stateId, newName);
|
|
393
|
-
this.debug('rename device: newname ~' + newName + '~ objName ~' + objName + '~')
|
|
482
|
+
if (this.debugActive) this.debug('rename device: newname ~' + newName + '~ objName ~' + objName + '~')
|
|
394
483
|
this.adapter.extendObject(id, {common: {name: objName}});
|
|
395
484
|
}
|
|
396
485
|
|
|
@@ -432,7 +521,7 @@ class StatesController extends EventEmitter {
|
|
|
432
521
|
let statename = state._id;
|
|
433
522
|
const arr = /zigbee.[0-9].[^.]+.(\S+)/gm.exec(statename);
|
|
434
523
|
if (arr[1] === undefined) {
|
|
435
|
-
this.debug(`unable to extract id from state ${statename}`);
|
|
524
|
+
if (this.debugActive) this.debug(`unable to extract id from state ${statename}`);
|
|
436
525
|
const idx = statename.lastIndexOf('.');
|
|
437
526
|
if (idx > -1) {
|
|
438
527
|
statename = statename.slice(idx + 1);
|
|
@@ -467,7 +556,7 @@ class StatesController extends EventEmitter {
|
|
|
467
556
|
}
|
|
468
557
|
} else {
|
|
469
558
|
if (!markOnly) {
|
|
470
|
-
this.debug(`keeping connected state ${JSON.stringify(statename)} of ${devId} `);
|
|
559
|
+
if (this.debugActive) this.debug(`keeping connected state ${JSON.stringify(statename)} of ${devId} `);
|
|
471
560
|
messages.push(`keeping connecte state ${JSON.stringify(statename)} of ${devId} `);
|
|
472
561
|
}
|
|
473
562
|
}
|
|
@@ -565,8 +654,8 @@ class StatesController extends EventEmitter {
|
|
|
565
654
|
if (value !== undefined) {
|
|
566
655
|
const type = stobj ? stobj.common.type : new_common.type;
|
|
567
656
|
if (type === 'number') {
|
|
568
|
-
const minval = (stobj ? stobj.common.min: new_common.min)
|
|
569
|
-
const maxval = (stobj ? stobj.common.max: new_common.max)
|
|
657
|
+
const minval = (stobj ? stobj.common.min : new_common.min);
|
|
658
|
+
const maxval = (stobj ? stobj.common.max : new_common.max);
|
|
570
659
|
let nval = (typeof value == 'number' ? value : parseFloat(value));
|
|
571
660
|
if (isNaN(nval)) {
|
|
572
661
|
if (minval !== undefined && typeof minval === 'number')
|
|
@@ -574,7 +663,7 @@ class StatesController extends EventEmitter {
|
|
|
574
663
|
else
|
|
575
664
|
nval = 0;
|
|
576
665
|
}
|
|
577
|
-
if (nval < minval) {
|
|
666
|
+
if (typeof minval == 'number' && nval < minval) {
|
|
578
667
|
hasChanges = true;
|
|
579
668
|
new_common.color = '#FF0000'
|
|
580
669
|
value = minval
|
|
@@ -584,7 +673,7 @@ class StatesController extends EventEmitter {
|
|
|
584
673
|
this.stashedErrors[`${stateId}.min`] = `State value for ${stateId} has value "${nval}." less than min "${minval}"`;
|
|
585
674
|
}
|
|
586
675
|
}
|
|
587
|
-
if (nval > maxval) {
|
|
676
|
+
if (typeof maxval == 'number' && nval > maxval) {
|
|
588
677
|
hasChanges = true;
|
|
589
678
|
hasChanges = true;
|
|
590
679
|
new_common.color = '#FF0000'
|
|
@@ -605,10 +694,10 @@ class StatesController extends EventEmitter {
|
|
|
605
694
|
this.setState_typed(stateId, value, true, stobj.common.type);
|
|
606
695
|
}
|
|
607
696
|
} else {
|
|
608
|
-
this.debug(`UpdateState: Device is deactivated ${devId} ${JSON.stringify(obj)}`);
|
|
697
|
+
if (this.debugActive) this.debug(`UpdateState: Device is deactivated ${devId} ${JSON.stringify(obj)}`);
|
|
609
698
|
}
|
|
610
699
|
} else {
|
|
611
|
-
this.debug(`UpdateState: missing device ${devId} ${JSON.stringify(obj)}`);
|
|
700
|
+
if (this.debugActive) this.debug(`UpdateState: missing device ${devId} ${JSON.stringify(obj)}`);
|
|
612
701
|
}
|
|
613
702
|
}
|
|
614
703
|
|
|
@@ -616,7 +705,7 @@ class StatesController extends EventEmitter {
|
|
|
616
705
|
// never set a null or undefined value
|
|
617
706
|
if (value === null || value === undefined) return;
|
|
618
707
|
if (!type) {
|
|
619
|
-
this.debug('SetState_typed called without type');
|
|
708
|
+
if (this.debugActive) this.debug('SetState_typed called without type');
|
|
620
709
|
// identify datatype, recursively call this function with set datatype
|
|
621
710
|
this.adapter.getObject(id, (err, obj) => {
|
|
622
711
|
if (obj && obj.common) {
|
|
@@ -628,7 +717,7 @@ class StatesController extends EventEmitter {
|
|
|
628
717
|
return;
|
|
629
718
|
}
|
|
630
719
|
if (typeof value !== type) {
|
|
631
|
-
this.debug(`SetState_typed : converting ${JSON.stringify(value)} for ${id} from ${typeof value} to ${type}`);
|
|
720
|
+
if (this.debugActive) this.debug(`SetState_typed : converting ${JSON.stringify(value)} for ${id} from ${typeof value} to ${type}`);
|
|
632
721
|
switch (type) {
|
|
633
722
|
case 'number':
|
|
634
723
|
value = parseFloat(value);
|
|
@@ -685,7 +774,7 @@ class StatesController extends EventEmitter {
|
|
|
685
774
|
async updateDev(dev_id, dev_name, model, callback) {
|
|
686
775
|
|
|
687
776
|
const __dev_name = this.verifyDeviceName(dev_id, model, (dev_name ? dev_name : model));
|
|
688
|
-
this.debug(`UpdateDev called with ${dev_id}, ${dev_name}, ${model}, ${__dev_name}`);
|
|
777
|
+
if (this.debugActive) this.debug(`UpdateDev called with ${dev_id}, ${dev_name}, ${model}, ${__dev_name}`);
|
|
689
778
|
const id = '' + dev_id;
|
|
690
779
|
const modelDesc = statesMapping.findModel(model);
|
|
691
780
|
const modelIcon = (model == 'group' ? await this.getDefaultGroupIcon(dev_id) : modelDesc && modelDesc.icon ? modelDesc.icon : 'img/unknown.png');
|
|
@@ -807,12 +896,17 @@ class StatesController extends EventEmitter {
|
|
|
807
896
|
}
|
|
808
897
|
|
|
809
898
|
CleanupRequired(set) {
|
|
810
|
-
|
|
811
|
-
|
|
899
|
+
try {
|
|
900
|
+
if (typeof set === 'boolean') this.cleanupRequired = set;
|
|
901
|
+
return this.cleanupRequired;
|
|
902
|
+
}
|
|
903
|
+
catch (error) {
|
|
904
|
+
if (this.debugActive) this.debug(`Error setting cleanup required: ${error && error.message ? error.message : 'no message available'}`);
|
|
905
|
+
}
|
|
812
906
|
}
|
|
813
907
|
|
|
814
908
|
async syncDevStates(dev, model) {
|
|
815
|
-
this.debug('synchronizing device states for ' + dev.ieeeAddr + ' (' + model + ')');
|
|
909
|
+
if (this.debugActive) this.debug('synchronizing device states for ' + dev.ieeeAddr + ' (' + model + ')');
|
|
816
910
|
const devId = dev.ieeeAddr.substr(2);
|
|
817
911
|
// devId - iobroker device id
|
|
818
912
|
const devStates = await this.getDevStates(dev.ieeeAddr, model);
|
|
@@ -890,11 +984,11 @@ class StatesController extends EventEmitter {
|
|
|
890
984
|
|
|
891
985
|
const message = `message received '${JSON.stringify(payload)}' from device ${devId} type '${model}'`;
|
|
892
986
|
if (has_elevated_debug) this.emit('device_debug', { ID:debugId, data: { deviceID: devId, flag:'01', IO:true }, message:message});
|
|
893
|
-
else this.debug(message);
|
|
987
|
+
else if (this.debugActive) this.debug(message);
|
|
894
988
|
if (!devStates) {
|
|
895
989
|
const message = `no device states for device ${devId} type '${model}'`;
|
|
896
990
|
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);
|
|
991
|
+
else if (this.debugActive) this.debug(message);
|
|
898
992
|
return;
|
|
899
993
|
}
|
|
900
994
|
// find states for payload
|
|
@@ -922,7 +1016,7 @@ class StatesController extends EventEmitter {
|
|
|
922
1016
|
|
|
923
1017
|
const message = `value generated '${JSON.stringify(value)}' from device ${devId} for '${statedesc.name}'`;
|
|
924
1018
|
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);
|
|
1019
|
+
else if (this.debugActive) this.debug(message);
|
|
926
1020
|
|
|
927
1021
|
const common = {
|
|
928
1022
|
name: statedesc.name,
|
|
@@ -968,16 +1062,16 @@ class StatesController extends EventEmitter {
|
|
|
968
1062
|
} catch (e) {
|
|
969
1063
|
const message = `unable to enumerate states of ${devId} for payload ${JSON.stringify(payload)}, ${(e ? e.name : 'undefined')} (${(e ? e.message : '')}).`;
|
|
970
1064
|
if (has_elevated_debug) this.emit('device_debug', { ID:debugId, data:{ error:'ESTATE', IO:true }, message:message});
|
|
971
|
-
else this.debug(message);
|
|
1065
|
+
else if (this.debugActive) this.debug(message);
|
|
972
1066
|
}
|
|
973
1067
|
const message = `No value published for device ${devId}`;
|
|
974
1068
|
if (!has_published && has_elevated_debug) this.emit('device_debug', { ID:debugId, data:{ error:'NOVAL', IO:true }, message:message});
|
|
975
|
-
else this.debug(message);
|
|
1069
|
+
else if (this.debugActive) this.debug(message);
|
|
976
1070
|
}
|
|
977
1071
|
else {
|
|
978
1072
|
const message = `ELEVATED IE05 - NOSTATE: No states matching the payload ${JSON.stringify(payload)} for device ${devId}`;
|
|
979
1073
|
if (has_elevated_debug) this.emit('device_debug', { ID:debugId, data:{ error:'NOSTATE', IO:true }, message});
|
|
980
|
-
else this.debug(message);
|
|
1074
|
+
else if (this.debugActive) this.debug(message);
|
|
981
1075
|
}
|
|
982
1076
|
}
|
|
983
1077
|
catch (error) {
|
|
@@ -1012,7 +1106,7 @@ class StatesController extends EventEmitter {
|
|
|
1012
1106
|
|
|
1013
1107
|
|
|
1014
1108
|
async onZigbeeEvent(type, entity, message) {
|
|
1015
|
-
this.debug(`Type ${type} device ${safeJsonStringify(entity)} incoming event: ${safeJsonStringify(message)}`);
|
|
1109
|
+
if (this.debugActive) this.debug(`Type ${type} device ${safeJsonStringify(entity)} incoming event: ${safeJsonStringify(message)}`);
|
|
1016
1110
|
|
|
1017
1111
|
const device = entity.device;
|
|
1018
1112
|
const mappedModel = entity.mapped;
|
|
@@ -1113,7 +1207,7 @@ class StatesController extends EventEmitter {
|
|
|
1113
1207
|
if (type !== 'readResponse') {
|
|
1114
1208
|
const message = `No converter available for '${mappedModel.model}' '${devId}' with cluster '${cluster}' and type '${type}'`;
|
|
1115
1209
|
if (has_elevated_debug) this.emit('device_debug', { ID:debugId, data: { error:'NOCONV', IO:true }, message:message});
|
|
1116
|
-
else this.debug(message);
|
|
1210
|
+
else if (this.debugActive) this.debug(message);
|
|
1117
1211
|
}
|
|
1118
1212
|
return;
|
|
1119
1213
|
}
|
package/lib/utils.js
CHANGED
|
@@ -1,7 +1,5 @@
|
|
|
1
1
|
'use strict';
|
|
2
2
|
|
|
3
|
-
const zigbeeHerdsmanConverters = require('zigbee-herdsman-converters');
|
|
4
|
-
|
|
5
3
|
/**
|
|
6
4
|
* Converts a bulb level of range [0...254] to an adapter level of range [0...100]
|
|
7
5
|
* @param {number} the bulb level of range [0...254]
|
|
@@ -125,18 +123,13 @@ function sanitizeImageParameter(parameter) {
|
|
|
125
123
|
}
|
|
126
124
|
|
|
127
125
|
function getDeviceIcon(definition) {
|
|
128
|
-
|
|
126
|
+
const icon = definition.icon;
|
|
129
127
|
if (icon) {
|
|
130
|
-
|
|
131
|
-
}
|
|
132
|
-
// if (!icon) {
|
|
133
|
-
// icon = `https://www.zigbee2mqtt.io/images/devices/${sanitizeImageParameter(definition.model)}.jpg`;
|
|
134
|
-
// }
|
|
135
|
-
if (!icon) {
|
|
136
|
-
icon = `https://www.zigbee2mqtt.io/images/devices/${sanitizeImageParameter(definition.model)}.png`;
|
|
128
|
+
return icon;
|
|
137
129
|
}
|
|
138
|
-
return
|
|
130
|
+
return `https://www.zigbee2mqtt.io/images/devices/${sanitizeImageParameter(definition.model)}.png`;
|
|
139
131
|
}
|
|
132
|
+
|
|
140
133
|
function getModelRegEx( model) {
|
|
141
134
|
const stripModel = (model) ? model.replace(/\0.*$/g, '').trim() : '';
|
|
142
135
|
return stripModel;
|
|
@@ -149,6 +142,40 @@ function getEntityInfo(entity) {
|
|
|
149
142
|
return `getEntityInfo: Illegal Entity ${JSON.stringify(entity)}`;
|
|
150
143
|
}
|
|
151
144
|
|
|
145
|
+
|
|
146
|
+
function byteArrayToString(data) {
|
|
147
|
+
if (data) {
|
|
148
|
+
return data.map(function (x) {
|
|
149
|
+
x = x + 0x100; // twos complement
|
|
150
|
+
x = x.toString(16); // to hex
|
|
151
|
+
x = ('00'+x).substr(-2); // zero-pad to 8-digits
|
|
152
|
+
return x
|
|
153
|
+
}).join('');
|
|
154
|
+
}
|
|
155
|
+
else return '';
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
function getNetAddress(address) {
|
|
159
|
+
const TcpData = address.match(/[tT][cC][pP]:\/\/(.+)/);
|
|
160
|
+
if (TcpData) {
|
|
161
|
+
const hostarr = TcpData[1].split(':');
|
|
162
|
+
return { strAddress :`tcp://${hostarr.length > 1 ? hostarr[0]+':'+hostarr[1] : hostarr[0]}`, host:hostarr[0], port:(hostarr.length > 1 ? hostarr[1] : undefined) };
|
|
163
|
+
}
|
|
164
|
+
return {};
|
|
165
|
+
}
|
|
166
|
+
|
|
167
|
+
function reverseByteString(source) {
|
|
168
|
+
if (source && typeof source == 'string') {
|
|
169
|
+
const rv = [];
|
|
170
|
+
for (let i=0;i<source.length;i+=2)
|
|
171
|
+
rv.push(source.slice(i,i+2))
|
|
172
|
+
return rv.reverse().join('');
|
|
173
|
+
}
|
|
174
|
+
return '';
|
|
175
|
+
}
|
|
176
|
+
|
|
177
|
+
|
|
178
|
+
|
|
152
179
|
exports.secondsToMilliseconds = seconds => seconds * 1000;
|
|
153
180
|
exports.bulbLevelToAdapterLevel = bulbLevelToAdapterLevel;
|
|
154
181
|
exports.adapterLevelToBulbLevel = adapterLevelToBulbLevel;
|
|
@@ -168,3 +195,6 @@ exports.isXiaomiDevice = device =>
|
|
|
168
195
|
exports.isIkeaTradfriDevice = device => ikeaTradfriManufacturerID.includes(device.manufacturerID);
|
|
169
196
|
exports.getDeviceIcon = getDeviceIcon;
|
|
170
197
|
exports.getEntityInfo = getEntityInfo;
|
|
198
|
+
exports.getNetAddress = getNetAddress;
|
|
199
|
+
exports.byteArrayToString = byteArrayToString;
|
|
200
|
+
exports.reverseByteString = reverseByteString;
|
package/lib/zbDeviceConfigure.js
CHANGED
|
@@ -180,15 +180,20 @@ class DeviceConfigure extends BaseExtension {
|
|
|
180
180
|
if (this.configureOnMessageAttempts.hasOwnProperty(device.ieeeAddr)) {
|
|
181
181
|
const com = this.configureOnMessageAttempts[device.ieeeAddr];
|
|
182
182
|
com.count--;
|
|
183
|
+
com.attempts++;
|
|
183
184
|
com.timestamp = Date.now();
|
|
184
|
-
|
|
185
|
-
|
|
185
|
+
if ( com.count < 0) {
|
|
186
|
+
delete this.configureOnMessageAttempts[device.ieeeAddr];
|
|
187
|
+
this.info(`Configure on message abandoned for ${device.ieeeAddr} ${mappedDevice.model} after failing ${com.attempts} times.`)
|
|
188
|
+
}
|
|
189
|
+
else this.info(`Timeout trying to configure ${device.ieeeAddr} ${mappedDevice.model} (${com.count}).`)
|
|
186
190
|
}
|
|
187
191
|
else {
|
|
188
192
|
this.info(`Timeout trying to configure ${device.ieeeAddr} ${mappedDevice.model} (starting CoM).`)
|
|
189
193
|
this.configureOnMessageAttempts[device.ieeeAddr] = {
|
|
190
194
|
count: 5,
|
|
191
195
|
timestamp: 0,
|
|
196
|
+
attempts: 0,
|
|
192
197
|
};
|
|
193
198
|
}
|
|
194
199
|
return `Configuration timed out ${device.ieeeAddr} ${device.modelID}. The device did not repond in time to the configuration request. Another attempt will be made when the device is awake.`;
|
|
@@ -203,6 +208,8 @@ class DeviceConfigure extends BaseExtension {
|
|
|
203
208
|
}
|
|
204
209
|
}
|
|
205
210
|
return 'no return value specified';
|
|
211
|
+
|
|
212
|
+
|
|
206
213
|
}
|
|
207
214
|
|
|
208
215
|
async stop() {
|