iobroker.zigbee 2.0.4 → 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 +90 -57
- package/admin/admin.js +497 -120
- package/admin/img/philips_hue_lom001.png +0 -0
- package/admin/index_m.html +168 -124
- package/admin/tab_m.html +20 -11
- 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/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 +31 -65
- package/lib/DeviceDebug.js +5 -2
- package/lib/commands.js +182 -31
- package/lib/developer.js +0 -0
- package/lib/devices.js +2 -2
- package/lib/exposes.js +10 -27
- package/lib/groups.js +6 -8
- package/lib/localConfig.js +4 -5
- package/lib/ota.js +6 -6
- package/lib/seriallist.js +9 -2
- package/lib/statescontroller.js +397 -128
- package/lib/utils.js +41 -11
- package/lib/zbDeviceAvailability.js +2 -2
- package/lib/zbDeviceConfigure.js +99 -58
- package/lib/zigbeecontroller.js +152 -128
- package/main.js +251 -264
- package/package.json +10 -10
- 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/main.js
CHANGED
|
@@ -36,6 +36,7 @@ const zigbeeHerdsmanPackage = require('zigbee-herdsman/package.json')
|
|
|
36
36
|
const vm = require('vm');
|
|
37
37
|
const util = require('util');
|
|
38
38
|
const dmZigbee = require('./lib/devicemgmt.js');
|
|
39
|
+
const DeviceDebug = require('./lib/DeviceDebug');
|
|
39
40
|
|
|
40
41
|
const createByteArray = function (hexString) {
|
|
41
42
|
const bytes = [];
|
|
@@ -79,6 +80,10 @@ class Zigbee extends utils.Adapter {
|
|
|
79
80
|
this.stController.on('changed', this.publishFromState.bind(this));
|
|
80
81
|
|
|
81
82
|
this.deviceManagement = new dmZigbee(this);
|
|
83
|
+
this.deviceDebug = new DeviceDebug(this),
|
|
84
|
+
this.deviceDebug.on('log', this.onLog.bind(this));
|
|
85
|
+
this.debugActive = true;
|
|
86
|
+
|
|
82
87
|
|
|
83
88
|
this.plugins = [
|
|
84
89
|
new SerialListPlugin(this),
|
|
@@ -151,7 +156,7 @@ class Zigbee extends utils.Adapter {
|
|
|
151
156
|
em = em || error.stack.match(/failed \((.+?)\)/);
|
|
152
157
|
this.log.error(`${message} no error code (${(em ? em[1] : 'undefined')})`);
|
|
153
158
|
this.sendError(error, `${message} no error code`);
|
|
154
|
-
this.log.debug(`Stack trace for ${em}: ${error.stack}`);
|
|
159
|
+
if (this.debugActive) this.log.debug(`Stack trace for ${em}: ${error.stack}`);
|
|
155
160
|
return;
|
|
156
161
|
}
|
|
157
162
|
|
|
@@ -167,7 +172,7 @@ class Zigbee extends utils.Adapter {
|
|
|
167
172
|
this.log.info(`${message}: Code ${error.code} (${ecode.message})`);
|
|
168
173
|
break;
|
|
169
174
|
case E_DEBUG:
|
|
170
|
-
this.log.debug(`${message}: Code ${error.code} (${ecode.message})`);
|
|
175
|
+
if (this.debugActive) this.log.debug(`${message}: Code ${error.code} (${ecode.message})`);
|
|
171
176
|
break;
|
|
172
177
|
case E_WARN:
|
|
173
178
|
this.log.warn(`${message}: Code ${error.code} (${ecode.message})`);
|
|
@@ -185,10 +190,14 @@ class Zigbee extends utils.Adapter {
|
|
|
185
190
|
|
|
186
191
|
debugLog(data, ...args) {
|
|
187
192
|
const message = (args) ? util.format(data, ...args) : data;
|
|
188
|
-
this.log.debug(message.slice(message.indexOf('zigbee-herdsman')));
|
|
193
|
+
if (this.debugActive) this.log.debug(message.slice(message.indexOf('zigbee-herdsman')));
|
|
189
194
|
}
|
|
190
195
|
|
|
191
196
|
async onReady() {
|
|
197
|
+
|
|
198
|
+
const dbActive = await this.getForeignState(`system.adapter.${this.namespace}.logLevel`);
|
|
199
|
+
this.debugActive = (dbActive && dbActive.val === 'debug');
|
|
200
|
+
this.log.info('Adapter ready - starting subsystems. Adapter is running in '+dbActive.val+ ' mode.');
|
|
192
201
|
if (this.config.debugHerdsman) {
|
|
193
202
|
debug.log = this.debugLog.bind(this);
|
|
194
203
|
debug.enable('zigbee-herdsman*');
|
|
@@ -200,6 +209,7 @@ class Zigbee extends utils.Adapter {
|
|
|
200
209
|
this.stController.getExposes();
|
|
201
210
|
|
|
202
211
|
this.subscribeStates('*');
|
|
212
|
+
this.subscribeForeignStates(`system.adapter.${this.namespace}.logLevel`)
|
|
203
213
|
// set connection false before connect to zigbee
|
|
204
214
|
this.setState('info.connection', false, true);
|
|
205
215
|
const zigbeeOptions = this.getZigbeeOptions();
|
|
@@ -210,14 +220,26 @@ class Zigbee extends utils.Adapter {
|
|
|
210
220
|
this.zbController.on('new', this.newDevice.bind(this));
|
|
211
221
|
this.zbController.on('leave', this.leaveDevice.bind(this));
|
|
212
222
|
this.zbController.on('pairing', this.onPairing.bind(this));
|
|
213
|
-
this.zbController.on('event', this.onZigbeeEvent.bind(this));
|
|
214
|
-
this.zbController.on('msg', this.onZigbeeEvent.bind(this));
|
|
223
|
+
this.zbController.on('event', this.stController.onZigbeeEvent.bind(this.stController));
|
|
224
|
+
this.zbController.on('msg', this.stController.onZigbeeEvent.bind(this.stController));
|
|
215
225
|
this.zbController.on('publish', this.publishToState.bind(this));
|
|
216
226
|
this.zbController.configure(zigbeeOptions);
|
|
227
|
+
this.zbController.debugActive = this.debugActive;
|
|
228
|
+
this.stController.debugActive = this.debugActive;
|
|
217
229
|
await this.callPluginMethod('configure', [zigbeeOptions]);
|
|
218
230
|
|
|
231
|
+
// elevated debug handling
|
|
232
|
+
this.deviceDebug.start(this.stController, this.zbController);
|
|
233
|
+
|
|
219
234
|
this.reconnectCounter = 1;
|
|
220
|
-
this.doConnect();
|
|
235
|
+
if (this.config.autostart) this.doConnect();
|
|
236
|
+
}
|
|
237
|
+
updateDebugLevel(state) {
|
|
238
|
+
const dbActive = state === 'debug';
|
|
239
|
+
this.debugActive = dbActive;
|
|
240
|
+
this.stController.debugActive = dbActive;
|
|
241
|
+
this.zbController.debugActive = dbActive;
|
|
242
|
+
this.log.info('Change of log level while running to ' + state);
|
|
221
243
|
}
|
|
222
244
|
|
|
223
245
|
sandboxAdd(sandbox, item, module) {
|
|
@@ -242,18 +264,18 @@ class Zigbee extends utils.Adapter {
|
|
|
242
264
|
|
|
243
265
|
let zhcm1 = modulePath.match(/^zigbee-herdsman-converters\//);
|
|
244
266
|
if (zhcm1) {
|
|
245
|
-
const i2 = modulePath.replace(/^zigbee-herdsman-converters\//, `../zigbee-herdsman-converters/`);
|
|
246
267
|
try {
|
|
268
|
+
const i2 = modulePath.replace(/^zigbee-herdsman-converters\//, `../${sandbox.zhclibBase}/`);
|
|
247
269
|
this.sandboxAdd(sandbox, item[1], i2);
|
|
248
270
|
}
|
|
249
271
|
catch (error) {
|
|
250
|
-
this.log.error(`Sandbox error: ${(error && error.message ? error.message : 'no error message given')}`)
|
|
272
|
+
this.log.error(`Sandbox error: ${(error && error.message ? error.message : 'no error message given')}`)
|
|
251
273
|
}
|
|
252
274
|
continue;
|
|
253
275
|
}
|
|
254
276
|
zhcm1 = modulePath.match(/^..\//);
|
|
255
277
|
if (zhcm1) {
|
|
256
|
-
const i2 = modulePath.replace(/^..\//,
|
|
278
|
+
const i2 = modulePath.replace(/^..\//, `../${sandbox.zhclibBase}/`);
|
|
257
279
|
try {
|
|
258
280
|
this.sandboxAdd(sandbox, item[1], i2);
|
|
259
281
|
}
|
|
@@ -283,11 +305,14 @@ class Zigbee extends utils.Adapter {
|
|
|
283
305
|
const extfiles = this.config.external.split(';');
|
|
284
306
|
for (const moduleName of extfiles) {
|
|
285
307
|
if (!moduleName) continue;
|
|
308
|
+
const ZHCP = zigbeeHerdsmanConvertersPackage;
|
|
286
309
|
const sandbox = {
|
|
287
310
|
require,
|
|
288
311
|
module: {},
|
|
312
|
+
zhclibBase : path.join('zigbee-herdsman-converters',(ZHCP && ZHCP.exports && ZHCP.exports['.'] ? path.dirname(ZHCP.exports['.']) : ''))
|
|
289
313
|
};
|
|
290
|
-
|
|
314
|
+
|
|
315
|
+
const mN = (fs.existsSync(moduleName) ? moduleName : this.expandFileName(moduleName));
|
|
291
316
|
if (!fs.existsSync(mN)) {
|
|
292
317
|
this.log.warn(`External converter not loaded - neither ${moduleName} nor ${mN} exist.`);
|
|
293
318
|
}
|
|
@@ -313,13 +338,18 @@ class Zigbee extends utils.Adapter {
|
|
|
313
338
|
//fs.writeFileSync(mN+'.tmp5', modifiedCode)
|
|
314
339
|
converterLoaded &= this.SandboxRequire(sandbox,[...modifiedCode.matchAll(/const\s+(\S+)\s+=\s+require\((.+)\)/gm)]);
|
|
315
340
|
modifiedCode = modifiedCode.replace(/const\s+\S+\s+=\s+require\(.+\)/gm, '');
|
|
316
|
-
//
|
|
341
|
+
//mfs.writeFileSync(mN+'.tmp', modifiedCode)
|
|
317
342
|
|
|
318
343
|
for(const component of modifiedCode.matchAll(/const (.+):(.+)=/gm)) {
|
|
319
344
|
modifiedCode = modifiedCode.replace(component[0], `const ${component[1]} = `);
|
|
320
345
|
}
|
|
321
346
|
modifiedCode = modifiedCode.replace(/export .+;/gm, '');
|
|
322
347
|
|
|
348
|
+
if (modifiedCode.indexOf('module.exports') < 0) {
|
|
349
|
+
converterLoaded = false;
|
|
350
|
+
this.log.error(`converter does not export any converter array, please add 'module.exports' statement to ${mN}`);
|
|
351
|
+
}
|
|
352
|
+
|
|
323
353
|
fs.writeFileSync(mN+'.tmp', modifiedCode)
|
|
324
354
|
|
|
325
355
|
if (converterLoaded) {
|
|
@@ -353,32 +383,47 @@ class Zigbee extends utils.Adapter {
|
|
|
353
383
|
const toAdd = {...definition};
|
|
354
384
|
delete toAdd['homeassistant'];
|
|
355
385
|
try {
|
|
386
|
+
const t = Date.now();
|
|
356
387
|
if (zigbeeHerdsmanConverters.hasOwnProperty('addExternalDefinition')) {
|
|
357
388
|
zigbeeHerdsmanConverters.addExternalDefinition(toAdd);
|
|
358
|
-
this.log.info(
|
|
389
|
+
this.log.info(`added external converter using addExternalDefinition (${Date.now()-t} ms)`)
|
|
359
390
|
}
|
|
360
391
|
else if (zigbeeHerdsmanConverters.hasOwnProperty('addDefinition')) {
|
|
361
392
|
zigbeeHerdsmanConverters.addDefinition(toAdd);
|
|
362
|
-
this.log.info(
|
|
393
|
+
this.log.info(`added external converter using addDefinition (${Date.now()-t} ms)`);
|
|
363
394
|
}
|
|
364
|
-
|
|
365
|
-
/*
|
|
366
|
-
for (const zigbeeModel of toAdd.zigbeeModel)
|
|
367
|
-
{
|
|
368
|
-
try {
|
|
369
|
-
zigbeeHerdsmanConverters.addToExternalDefinitionsLookup(zigbeeModel, toAdd.toAdd);
|
|
370
|
-
} catch (e) {
|
|
371
|
-
this.log.error(`unable to apply external converter ${JSON.stringify(toAdd)} for device ${zigbeeModel}: ${e && e.message ? e.message : 'no error message available'}`);
|
|
372
|
-
}
|
|
373
|
-
}
|
|
374
|
-
*/
|
|
375
395
|
} catch (e) {
|
|
376
396
|
this.log.error(`unable to apply external converter for ${JSON.stringify(toAdd.model)}: ${e && e.message ? e.message : 'no error message available'}`);
|
|
377
397
|
}
|
|
378
398
|
}
|
|
379
399
|
}
|
|
380
400
|
|
|
381
|
-
async
|
|
401
|
+
async testConnect(from, command, message, callback) {
|
|
402
|
+
const response = {};
|
|
403
|
+
if (message.start) {
|
|
404
|
+
try {
|
|
405
|
+
this.logToPairing(`overriding zigbee options with:`);
|
|
406
|
+
for (const k of Object.keys(message.zigbeeOptions)) {
|
|
407
|
+
this.logToPairing(`${k} : ${message.zigbeeOptions[k]}`)
|
|
408
|
+
}
|
|
409
|
+
this.zbController.configure(this.getZigbeeOptions(message.zigbeeOptions));
|
|
410
|
+
response.status = await this.doConnect(true);
|
|
411
|
+
this.sendTo(from, command, response, callback);
|
|
412
|
+
}
|
|
413
|
+
catch (error) {
|
|
414
|
+
this.sendTo(from, command, { status:false }, callback);
|
|
415
|
+
}
|
|
416
|
+
}
|
|
417
|
+
else try {
|
|
418
|
+
await this.zbController.stopHerdsman();
|
|
419
|
+
//this.logToPairing('herdsman stopped !');
|
|
420
|
+
this.sendTo(from, command, { status:true }, callback);
|
|
421
|
+
} catch (error) {
|
|
422
|
+
this.sendTo(from, command, { status:false }, callback);
|
|
423
|
+
}
|
|
424
|
+
}
|
|
425
|
+
|
|
426
|
+
async doConnect(noReconnect) {
|
|
382
427
|
let debugversion = '';
|
|
383
428
|
try {
|
|
384
429
|
const DebugIdentify = require('./debugidentify');
|
|
@@ -390,33 +435,44 @@ class Zigbee extends utils.Adapter {
|
|
|
390
435
|
// installed version
|
|
391
436
|
let gitVers = '';
|
|
392
437
|
try {
|
|
438
|
+
if (noReconnect) this.logToPairing(`Starting Adapter ${debugversion}`);
|
|
393
439
|
this.log.info(`Starting Adapter ${debugversion}`);
|
|
394
440
|
|
|
395
441
|
this.getForeignObject(`system.adapter.${this.namespace}`,async (err, obj) => {
|
|
396
|
-
|
|
397
|
-
|
|
398
|
-
|
|
399
|
-
|
|
400
|
-
|
|
442
|
+
try {
|
|
443
|
+
if (!err && obj && obj.common.installedFrom && obj.common.installedFrom.includes('://')) {
|
|
444
|
+
const instFrom = obj.common.installedFrom;
|
|
445
|
+
gitVers = gitVers + instFrom.replace('tarball', 'commit');
|
|
446
|
+
} else {
|
|
447
|
+
gitVers = obj.common.installedFrom;
|
|
448
|
+
}
|
|
449
|
+
if (noReconnect) this.logToPairing(`Installed Version: ${gitVers} (Converters ${zigbeeHerdsmanConvertersPackage.version} Herdsman ${zigbeeHerdsmanPackage.version})`);
|
|
450
|
+
this.log.info(`Installed Version: ${gitVers} (Converters ${zigbeeHerdsmanConvertersPackage.version} Herdsman ${zigbeeHerdsmanPackage.version})`);
|
|
451
|
+
await this.zbController.start(noReconnect);
|
|
452
|
+
} catch (error) {
|
|
453
|
+
this.logToPairing(error && error.message ? error.message : error);
|
|
454
|
+
this.log.error(error && error.message ? error.message : error);
|
|
401
455
|
}
|
|
402
|
-
|
|
403
|
-
await this.zbController.start();
|
|
456
|
+
return false;
|
|
404
457
|
});
|
|
405
|
-
|
|
406
458
|
} catch (error) {
|
|
407
459
|
this.setState('info.connection', false, true);
|
|
408
|
-
this.
|
|
409
|
-
|
|
460
|
+
this.logToPairing(`Failed to start Zigbee: ${error && error.message ? error.message : 'no message given'}`)
|
|
461
|
+
this.log.error(`Failed to start Zigbee: ${error && error.message ? error.message : 'no message given'}`);
|
|
462
|
+
/* if (error.stack) {
|
|
410
463
|
this.log.error(error.stack);
|
|
411
464
|
} else {
|
|
412
465
|
this.log.error(error);
|
|
413
466
|
}
|
|
467
|
+
*/
|
|
414
468
|
this.sendError(error, `Failed to start Zigbee`);
|
|
469
|
+
if (noReconnect) return false;
|
|
415
470
|
|
|
416
471
|
if (this.reconnectCounter > 0) {
|
|
417
472
|
this.tryToReconnect();
|
|
418
473
|
}
|
|
419
474
|
}
|
|
475
|
+
return true;
|
|
420
476
|
}
|
|
421
477
|
|
|
422
478
|
UploadRequired(status) {
|
|
@@ -479,14 +535,16 @@ class Zigbee extends utils.Adapter {
|
|
|
479
535
|
]
|
|
480
536
|
);
|
|
481
537
|
const nwExtPanId = `0x${result.payload.value.reverse().toString('hex')}`;
|
|
482
|
-
this.log.debug(`Config value ${configExtPanId} : nw value ${nwExtPanId}`);
|
|
538
|
+
if (this.debugActive) this.log.debug(`Config value ${configExtPanId} : nw value ${nwExtPanId}`);
|
|
539
|
+
this.logToPairing(`Config value ${configExtPanId} : nw value ${nwExtPanId}`)
|
|
483
540
|
if (configExtPanId !== nwExtPanId) {
|
|
484
541
|
networkExtPanId = nwExtPanId;
|
|
485
542
|
needChange = true;
|
|
486
543
|
}
|
|
487
544
|
} catch (e) {
|
|
488
|
-
|
|
489
|
-
this.
|
|
545
|
+
const msg = `Unable to apply ExtPanID changes: ${e}`;
|
|
546
|
+
this.log.error(msg);
|
|
547
|
+
this.logToPairing(msg)
|
|
490
548
|
needChange = false;
|
|
491
549
|
}
|
|
492
550
|
} else {
|
|
@@ -495,11 +553,11 @@ class Zigbee extends utils.Adapter {
|
|
|
495
553
|
}
|
|
496
554
|
if (needChange) {
|
|
497
555
|
// need change config value and mark that fix is applied
|
|
498
|
-
this.log.debug(`Fix extPanId value to ${networkExtPanId}. And restart adapter.`);
|
|
556
|
+
if (this.debugActive) this.log.debug(`Fix extPanId value to ${networkExtPanId}. And restart adapter.`);
|
|
499
557
|
this.updateConfig({extPanID: networkExtPanId.substr(2), extPanIdFix: true});
|
|
500
558
|
} else {
|
|
501
559
|
// only mark that fix is applied
|
|
502
|
-
this.log.debug(`Fix without changes. And restart adapter.`);
|
|
560
|
+
if (this.debugActive) this.log.debug(`Fix without changes. And restart adapter.`);
|
|
503
561
|
this.updateConfig({extPanIdFix: true});
|
|
504
562
|
}
|
|
505
563
|
}
|
|
@@ -510,7 +568,6 @@ class Zigbee extends utils.Adapter {
|
|
|
510
568
|
for (const device of devicesFromDB) {
|
|
511
569
|
const entity = await this.zbController.resolveEntity(device);
|
|
512
570
|
if (entity) {
|
|
513
|
-
// this.log.warn('sync dev states for ' + (entity.mapped ? entity.mapped.model : entity.device.modelID));
|
|
514
571
|
const model = entity.mapped ? entity.mapped.model : entity.device.modelID;
|
|
515
572
|
this.stController.updateDev(device.ieeeAddr.substr(2), model, model, () =>
|
|
516
573
|
this.stController.syncDevStates(device, model));
|
|
@@ -553,154 +610,6 @@ class Zigbee extends utils.Adapter {
|
|
|
553
610
|
});
|
|
554
611
|
}
|
|
555
612
|
|
|
556
|
-
async onZigbeeEvent(type, entity, message) {
|
|
557
|
-
this.log.debug(`Type ${type} device ${safeJsonStringify(entity)} incoming event: ${safeJsonStringify(message)}`);
|
|
558
|
-
|
|
559
|
-
const device = entity.device;
|
|
560
|
-
const mappedModel = entity.mapped;
|
|
561
|
-
const model = entity.mapped ? entity.mapped.model : entity.device.modelID;
|
|
562
|
-
const cluster = message.cluster;
|
|
563
|
-
const devId = device.ieeeAddr.substr(2);
|
|
564
|
-
const meta = {device};
|
|
565
|
-
|
|
566
|
-
const has_elevated_debug = this.stController.checkDebugDevice(devId);
|
|
567
|
-
|
|
568
|
-
if (has_elevated_debug) {
|
|
569
|
-
const shortMessage = {};
|
|
570
|
-
for(const propertyName in message) {
|
|
571
|
-
shortMessage[propertyName] = message[propertyName];
|
|
572
|
-
}
|
|
573
|
-
shortMessage.device = device.ieeeAddr;
|
|
574
|
-
shortMessage.meta = undefined;
|
|
575
|
-
shortMessage.endpoint = (message.endpoint.ID ? message.endpoint.ID: -1);
|
|
576
|
-
this.log.warn(`ELEVATED I00: Zigbee Event of Type ${type} from device ${safeJsonStringify(device.ieeeAddr)}, incoming event: ${safeJsonStringify(shortMessage)}`);
|
|
577
|
-
}
|
|
578
|
-
// this assigment give possibility to use iobroker logger in code of the converters, via meta.logger
|
|
579
|
-
meta.logger = this.log;
|
|
580
|
-
|
|
581
|
-
await this.checkIfModelUpdate(entity);
|
|
582
|
-
|
|
583
|
-
let _voltage = 0;
|
|
584
|
-
let _temperature = 0;
|
|
585
|
-
let _humidity = 0;
|
|
586
|
-
|
|
587
|
-
let isMessure = false;
|
|
588
|
-
let isBattKey = false;
|
|
589
|
-
|
|
590
|
-
if (mappedModel && mappedModel.meta && mappedModel.meta.battery) {
|
|
591
|
-
const isVoltage = mappedModel.meta.battery.hasOwnProperty('voltageToPercentage');
|
|
592
|
-
|
|
593
|
-
if (isVoltage) {
|
|
594
|
-
const keys = Object.keys(message.data);
|
|
595
|
-
|
|
596
|
-
for (const key of keys) {
|
|
597
|
-
const value = message.data[key];
|
|
598
|
-
|
|
599
|
-
if (value && value[1]) {
|
|
600
|
-
if (key == 65282 && value[1][1]) {
|
|
601
|
-
_voltage = value[1][1].elmVal;
|
|
602
|
-
isBattKey = true;
|
|
603
|
-
break;
|
|
604
|
-
}
|
|
605
|
-
if (key == 65281) {
|
|
606
|
-
_voltage = value[1];
|
|
607
|
-
isBattKey = true;
|
|
608
|
-
_temperature = value[100];
|
|
609
|
-
_temperature = _temperature /100;
|
|
610
|
-
_humidity = value[101];
|
|
611
|
-
_humidity = _humidity / 100;
|
|
612
|
-
isMessure = true;
|
|
613
|
-
break;
|
|
614
|
-
}
|
|
615
|
-
}
|
|
616
|
-
}
|
|
617
|
-
}
|
|
618
|
-
}
|
|
619
|
-
|
|
620
|
-
// always publish link_quality and battery
|
|
621
|
-
if (message.linkquality) { // send battery with
|
|
622
|
-
this.publishToState(devId, model, {linkquality: message.linkquality});
|
|
623
|
-
if (isBattKey) {
|
|
624
|
-
this.publishToState(devId, model, {voltage: _voltage});
|
|
625
|
-
const battProz = zigbeeHerdsmanConvertersUtils.batteryVoltageToPercentage(_voltage,entity.mapped.meta.battery.voltageToPercentage);
|
|
626
|
-
this.publishToState(devId, model, {battery: battProz});
|
|
627
|
-
}
|
|
628
|
-
if (isMessure) {
|
|
629
|
-
this.publishToState(devId, model, {temperature: _temperature});
|
|
630
|
-
this.publishToState(devId, model, {humidity: _humidity});
|
|
631
|
-
}
|
|
632
|
-
}
|
|
633
|
-
|
|
634
|
-
// publish raw event to "from_zigbee"
|
|
635
|
-
// some cleanup
|
|
636
|
-
const msgForState = Object.assign({}, message);
|
|
637
|
-
delete msgForState['device'];
|
|
638
|
-
delete msgForState['endpoint'];
|
|
639
|
-
|
|
640
|
-
msgForState['endpoint_id'] = message.endpoint.ID;
|
|
641
|
-
this.publishToState(devId, model, {msg_from_zigbee: safeJsonStringify(msgForState)});
|
|
642
|
-
|
|
643
|
-
if (!entity.mapped) {
|
|
644
|
-
return;
|
|
645
|
-
}
|
|
646
|
-
|
|
647
|
-
let converters = mappedModel.fromZigbee.filter(c => c && c.cluster === cluster && (
|
|
648
|
-
Array.isArray(c.type) ? c.type.includes(type) : c.type === type));
|
|
649
|
-
|
|
650
|
-
|
|
651
|
-
if (!converters.length && type === 'readResponse') {
|
|
652
|
-
converters = mappedModel.fromZigbee.filter(c => c.cluster === cluster && (
|
|
653
|
-
Array.isArray(c.type) ? c.type.includes('attributeReport') : c.type === 'attributeReport'));
|
|
654
|
-
}
|
|
655
|
-
|
|
656
|
-
if (!converters.length) {
|
|
657
|
-
if (type !== 'readResponse') {
|
|
658
|
-
this.log.debug(`No converter available for '${mappedModel.model}' '${devId}' with cluster '${cluster}' and type '${type}'`);
|
|
659
|
-
if (has_elevated_debug)
|
|
660
|
-
this.log.warn(`ELEVATED IE00: No converter available for '${mappedModel.model}' '${devId}' with cluster '${cluster}' and type '${type}'`);
|
|
661
|
-
}
|
|
662
|
-
return;
|
|
663
|
-
}
|
|
664
|
-
|
|
665
|
-
meta.state = { state: '' }; // for tuya
|
|
666
|
-
|
|
667
|
-
this.processConverters(converters, devId, model, mappedModel, message, meta)
|
|
668
|
-
.catch((error) => {
|
|
669
|
-
// 'Error: Expected one of: 0, 1, got: 'undefined''
|
|
670
|
-
if (cluster !== '64529') {
|
|
671
|
-
this.log.error(`Error while processing converters DEVICE_ID: '${devId}' cluster '${cluster}' type '${type}'`);
|
|
672
|
-
}
|
|
673
|
-
});
|
|
674
|
-
}
|
|
675
|
-
|
|
676
|
-
async processConverters(converters, devId, model, mappedModel, message, meta) {
|
|
677
|
-
for (const converter of converters) {
|
|
678
|
-
const publish = (payload) => {
|
|
679
|
-
this.log.debug(`Publish ${safeJsonStringify(payload)} to ${safeJsonStringify(devId)}`);
|
|
680
|
-
if (typeof payload === 'object') {
|
|
681
|
-
this.publishToState(devId, model, payload);
|
|
682
|
-
}
|
|
683
|
-
};
|
|
684
|
-
|
|
685
|
-
const options = await new Promise((resolve, reject) => {
|
|
686
|
-
this.stController.collectOptions(devId, model, (options) => {
|
|
687
|
-
resolve(options);
|
|
688
|
-
});
|
|
689
|
-
});
|
|
690
|
-
|
|
691
|
-
const payload = await new Promise((resolve, reject) => {
|
|
692
|
-
const payloadConv = converter.convert(mappedModel, message, publish, options, meta);
|
|
693
|
-
if (typeof payloadConv === 'object') {
|
|
694
|
-
resolve(payloadConv);
|
|
695
|
-
}
|
|
696
|
-
});
|
|
697
|
-
|
|
698
|
-
publish(payload);
|
|
699
|
-
}
|
|
700
|
-
}
|
|
701
|
-
|
|
702
|
-
|
|
703
|
-
|
|
704
613
|
publishToState(devId, model, payload) {
|
|
705
614
|
this.stController.publishToState(devId, model, payload);
|
|
706
615
|
}
|
|
@@ -721,30 +630,36 @@ class Zigbee extends utils.Adapter {
|
|
|
721
630
|
});
|
|
722
631
|
}
|
|
723
632
|
|
|
724
|
-
async publishFromState(deviceId, model, stateModel, stateList, options) {
|
|
633
|
+
async publishFromState(deviceId, model, stateModel, stateList, options, debugID) {
|
|
725
634
|
let isGroup = false;
|
|
726
635
|
const has_elevated_debug = this.stController.checkDebugDevice(deviceId)
|
|
727
636
|
|
|
728
637
|
if (has_elevated_debug)
|
|
729
638
|
{
|
|
730
639
|
const stateNames = [];
|
|
731
|
-
|
|
732
|
-
|
|
640
|
+
for (const state of stateList) {
|
|
641
|
+
stateNames.push(state.stateDesc.id);
|
|
642
|
+
}
|
|
643
|
+
const message = `Publishing to ${deviceId} of model ${model} with ${stateNames.join(', ')}`;
|
|
644
|
+
this.emit('device_debug', { ID:debugID, data: { ID: deviceId, flag: '03', IO:false }, message: message});
|
|
733
645
|
}
|
|
734
646
|
else
|
|
735
|
-
this.log.debug(`publishFromState : ${deviceId} ${model} ${safeJsonStringify(stateList)}`);
|
|
647
|
+
if (this.debugActive) this.log.debug(`publishFromState : ${deviceId} ${model} ${safeJsonStringify(stateList)}`);
|
|
736
648
|
if (model === 'group') {
|
|
737
649
|
isGroup = true;
|
|
738
650
|
deviceId = parseInt(deviceId);
|
|
739
651
|
}
|
|
740
652
|
try {
|
|
741
653
|
const entity = await this.zbController.resolveEntity(deviceId);
|
|
742
|
-
this.log.debug(`entity: ${deviceId} ${model} ${safeJsonStringify(entity)}`);
|
|
654
|
+
if (this.debugActive) this.log.debug(`entity: ${deviceId} ${model} ${safeJsonStringify(entity)}`);
|
|
743
655
|
const mappedModel = entity ? entity.mapped : undefined;
|
|
744
656
|
|
|
745
657
|
if (!mappedModel) {
|
|
746
|
-
this.log.debug(`No mapped model for ${model}`);
|
|
747
|
-
if (has_elevated_debug)
|
|
658
|
+
if (this.debugActive) this.log.debug(`No mapped model for ${model}`);
|
|
659
|
+
if (has_elevated_debug) {
|
|
660
|
+
const message=`No mapped model ${deviceId} (model ${model})`;
|
|
661
|
+
this.emit('device_debug', { ID:debugID, data: { error: 'NOMODEL' , IO:false }, message: message});
|
|
662
|
+
}
|
|
748
663
|
return;
|
|
749
664
|
}
|
|
750
665
|
|
|
@@ -761,13 +676,20 @@ class Zigbee extends utils.Adapter {
|
|
|
761
676
|
if (stateDesc.id === 'send_payload') {
|
|
762
677
|
try {
|
|
763
678
|
const json_value = JSON.parse(value);
|
|
764
|
-
const payload = {device: deviceId.replace('0x', ''), payload: json_value};
|
|
679
|
+
const payload = {device: deviceId.replace('0x', ''), payload: json_value, model:model, stateModel:stateModel};
|
|
680
|
+
if (has_elevated_debug) this.emit('device_debug', { ID:debugID, data: { flag: '04' ,payload:value ,states:[{id:stateDesc.id, value:json_value, payload:'none'}], IO:false }});
|
|
681
|
+
|
|
765
682
|
const result = await this.sendPayload(payload);
|
|
766
683
|
if (result.hasOwnProperty('success') && result.success) {
|
|
767
684
|
this.acknowledgeState(deviceId, model, stateDesc, value);
|
|
768
685
|
}
|
|
686
|
+
else {
|
|
687
|
+
this.log.error('Error in SendPayload: '+result.error.message);
|
|
688
|
+
}
|
|
769
689
|
} catch (error) {
|
|
770
|
-
|
|
690
|
+
const message = `send_payload: ${value} does not parse as JSON Object : ${error.message}`;
|
|
691
|
+
if (has_elevated_debug) this.emit('device_debug', { ID:debugID, data: { error: 'EXSEND' ,states:[{id:stateDesc.id, value:value, payload:error.message}], IO:false }, message:message});
|
|
692
|
+
else this.log.error(message);
|
|
771
693
|
return;
|
|
772
694
|
}
|
|
773
695
|
return;
|
|
@@ -775,31 +697,31 @@ class Zigbee extends utils.Adapter {
|
|
|
775
697
|
|
|
776
698
|
if (stateDesc.isOption || stateDesc.compositeState) {
|
|
777
699
|
// acknowledge state with given value
|
|
778
|
-
if (has_elevated_debug)
|
|
779
|
-
|
|
700
|
+
if (has_elevated_debug) {
|
|
701
|
+
const message = 'changed state: ' + JSON.stringify(changedState);
|
|
702
|
+
this.emit('device_debug', { ID:debugID, data: { flag: 'cc', states:[{id:stateDesc.id, value:value, payload:'none (OC State)'}] , IO:false }, message:message});
|
|
703
|
+
}
|
|
780
704
|
else
|
|
781
|
-
this.log.debug('changed composite state: ' + JSON.stringify(changedState));
|
|
705
|
+
if (this.debugActive) this.log.debug('changed composite state: ' + JSON.stringify(changedState));
|
|
782
706
|
|
|
783
707
|
this.acknowledgeState(deviceId, model, stateDesc, value);
|
|
784
708
|
if (stateDesc.compositeState && stateDesc.compositeTimeout) {
|
|
785
709
|
this.stController.triggerComposite(deviceId, model, stateDesc, changedState.source.includes('.admin.'));
|
|
786
710
|
}
|
|
787
|
-
// process sync state list
|
|
788
|
-
//this.processSyncStatesList(deviceId, modelId, syncStateList);
|
|
789
|
-
// if this is the device query state => trigger the device query
|
|
790
|
-
|
|
791
711
|
// on activation of the 'device_query' state trigger hardware query where possible
|
|
792
712
|
if (stateDesc.id === 'device_query') {
|
|
793
713
|
if (this.query_device_block.indexOf(deviceId) > -1) {
|
|
794
|
-
this.log.
|
|
714
|
+
this.log.info(`Device query for '${entity.device.ieeeAddr}' blocked`);
|
|
795
715
|
return;
|
|
796
716
|
}
|
|
797
717
|
if (mappedModel) {
|
|
798
718
|
this.query_device_block.push(deviceId);
|
|
799
|
-
if (has_elevated_debug)
|
|
800
|
-
|
|
719
|
+
if (has_elevated_debug) {
|
|
720
|
+
const message = `Device query for '${entity.device.ieeeAddr}/${entity.device.endpoints[0].ID}' triggered`;
|
|
721
|
+
this.emit('device_debug', { ID:debugID, data: { flag: 'qs' ,states:[{id:stateDesc.id, value:value, payload:'none for device query'}], IO:false }, message:message});
|
|
722
|
+
}
|
|
801
723
|
else
|
|
802
|
-
this.log.debug(`Device query for '${entity.device.ieeeAddr}' started`);
|
|
724
|
+
if (this.debugActive) this.log.debug(`Device query for '${entity.device.ieeeAddr}' started`);
|
|
803
725
|
for (const converter of mappedModel.toZigbee) {
|
|
804
726
|
if (converter.hasOwnProperty('convertGet')) {
|
|
805
727
|
for (const ckey of converter.key) {
|
|
@@ -807,7 +729,9 @@ class Zigbee extends utils.Adapter {
|
|
|
807
729
|
await converter.convertGet(entity.device.endpoints[0], ckey, {});
|
|
808
730
|
} catch (error) {
|
|
809
731
|
if (has_elevated_debug) {
|
|
810
|
-
|
|
732
|
+
const message = `Failed to read state '${JSON.stringify(ckey)}'of '${entity.device.ieeeAddr}/${entity.device.endpoints[0].ID}' from query with '${error && error.message ? error.message : 'no error message'}`;
|
|
733
|
+
this.log.warn(`ELEVATED OE02.1 ${message}`);
|
|
734
|
+
this.emit('device_debug', { ID:debugID, data: { error: 'NOTREAD' , IO:false }, message:message });
|
|
811
735
|
}
|
|
812
736
|
else
|
|
813
737
|
this.log.info(`failed to read state ${JSON.stringify(ckey)} of ${entity.device.ieeeAddr}/${entity.device.endpoints[0].ID} after device query`);
|
|
@@ -815,8 +739,10 @@ class Zigbee extends utils.Adapter {
|
|
|
815
739
|
}
|
|
816
740
|
}
|
|
817
741
|
}
|
|
818
|
-
if (has_elevated_debug)
|
|
819
|
-
|
|
742
|
+
if (has_elevated_debug) {
|
|
743
|
+
const message = `ELEVATED O07: Device query for '${entity.device.ieeeAddr}/${entity.device.endpoints[0].ID}' complete`;
|
|
744
|
+
this.emit('device_debug', { ID:debugID, data: { flag: 'qe' , IO:false }, message:message});
|
|
745
|
+
}
|
|
820
746
|
else
|
|
821
747
|
this.log.info(`Device query for '${entity.device.ieeeAddr}' done`);
|
|
822
748
|
const idToRemove = deviceId;
|
|
@@ -837,41 +763,54 @@ class Zigbee extends utils.Adapter {
|
|
|
837
763
|
for (const c of mappedModel.toZigbee) {
|
|
838
764
|
|
|
839
765
|
if (!c.hasOwnProperty('convertSet')) continue;
|
|
840
|
-
this.log.debug(`Type of toZigbee is '${typeof c}', Contains key ${(c.hasOwnProperty('key')?JSON.stringify(c.key):'false ')}`)
|
|
766
|
+
if (this.debugActive) this.log.debug(`Type of toZigbee is '${typeof c}', Contains key ${(c.hasOwnProperty('key')?JSON.stringify(c.key):'false ')}`)
|
|
841
767
|
if (!c.hasOwnProperty('key'))
|
|
842
768
|
{
|
|
843
|
-
if (
|
|
769
|
+
if (converter === undefined)
|
|
844
770
|
{
|
|
845
771
|
converter = c;
|
|
846
|
-
if (has_elevated_debug)
|
|
847
|
-
|
|
772
|
+
if (has_elevated_debug) {
|
|
773
|
+
const message = `Setting converter to keyless converter for ${deviceId} of type ${model}`;
|
|
774
|
+
this.emit('device_debug', { ID:debugID, data: { flag: `s4.${msg_counter}` , IO:false }, message:message});
|
|
775
|
+
}
|
|
848
776
|
else
|
|
849
|
-
this.log.debug(`Setting converter to keyless converter for ${deviceId} of type ${model}`)
|
|
777
|
+
if (this.debugActive) this.log.debug(`Setting converter to keyless converter for ${deviceId} of type ${model}`);
|
|
850
778
|
msg_counter++;
|
|
851
779
|
}
|
|
852
780
|
else
|
|
853
781
|
{
|
|
854
782
|
if (has_elevated_debug)
|
|
855
|
-
|
|
783
|
+
{
|
|
784
|
+
const message = `ignoring keyless converter for ${deviceId} of type ${model}`;
|
|
785
|
+
this.emit('device_debug', { ID:debugID, data: { flag: `i4.${msg_counter}` , IO:false} , message:message});
|
|
786
|
+
}
|
|
856
787
|
else
|
|
857
|
-
this.log.debug(`ignoring keyless converter for ${deviceId} of type ${model}`)
|
|
788
|
+
if (this.debugActive) this.log.debug(`ignoring keyless converter for ${deviceId} of type ${model}`);
|
|
858
789
|
msg_counter++;
|
|
859
790
|
}
|
|
860
791
|
continue;
|
|
861
792
|
}
|
|
862
793
|
if (c.key.includes(stateDesc.prop) || c.key.includes(stateDesc.setattr) || c.key.includes(stateDesc.id))
|
|
863
794
|
{
|
|
864
|
-
|
|
865
|
-
|
|
795
|
+
const message = `${(converter===undefined?'Setting':'Overriding')}' converter to converter with key(s)'${JSON.stringify(c.key)}}`;
|
|
796
|
+
if (has_elevated_debug) {
|
|
797
|
+
this.emit('device_debugug', { ID:debugID, data: { flag: `${converter===undefined ? 's' : 'o'}4.${msg_counter}` , IO:false }, message:message});
|
|
798
|
+
|
|
799
|
+
}
|
|
866
800
|
else
|
|
867
|
-
this.log.debug(
|
|
801
|
+
if (this.debugActive) this.log.debug(message);
|
|
868
802
|
converter = c;
|
|
869
803
|
msg_counter++;
|
|
870
804
|
}
|
|
871
805
|
}
|
|
872
806
|
if (converter === undefined) {
|
|
873
|
-
|
|
874
|
-
|
|
807
|
+
const message = `No converter available for '${model}' with key '${stateDesc.id}' `;
|
|
808
|
+
if (has_elevated_debug) {
|
|
809
|
+
this.emit('device_debug', { ID:debugID, data: { error: 'NOCONV',states:[{id:stateDesc.id, value:value, payload:'no converter'}] , IO:false }, message:message});
|
|
810
|
+
}
|
|
811
|
+
else {
|
|
812
|
+
this.log.warn(message);
|
|
813
|
+
}
|
|
875
814
|
return;
|
|
876
815
|
}
|
|
877
816
|
|
|
@@ -890,10 +829,12 @@ class Zigbee extends utils.Adapter {
|
|
|
890
829
|
|
|
891
830
|
const epName = stateDesc.epname !== undefined ? stateDesc.epname : (stateDesc.prop || stateDesc.id);
|
|
892
831
|
const key = stateDesc.setattr || stateDesc.prop || stateDesc.id;
|
|
893
|
-
|
|
894
|
-
|
|
832
|
+
const message = `convert ${key}, ${safeJsonStringify(preparedValue)}, ${safeJsonStringify(preparedOptions)} for device ${deviceId} with Endpoint ${epName}`;
|
|
833
|
+
if (has_elevated_debug) {
|
|
834
|
+
this.emit('device_debug', { ID:debugID, data: { flag: '04', payload: {key:key, ep: stateDesc.epname, value:preparedValue, options:preparedOptions}, IO:false }, message:message});
|
|
835
|
+
}
|
|
895
836
|
else
|
|
896
|
-
this.log.debug(
|
|
837
|
+
if (this.debugActive) this.log.debug(message);
|
|
897
838
|
|
|
898
839
|
let target;
|
|
899
840
|
if (model === 'group') {
|
|
@@ -903,7 +844,7 @@ class Zigbee extends utils.Adapter {
|
|
|
903
844
|
target = target.endpoint;
|
|
904
845
|
}
|
|
905
846
|
|
|
906
|
-
this.log.debug(`target: ${safeJsonStringify(target)}`);
|
|
847
|
+
if (this.debugActive) this.log.debug(`target: ${safeJsonStringify(target)}`);
|
|
907
848
|
|
|
908
849
|
const meta = {
|
|
909
850
|
endpoint_name: epName,
|
|
@@ -923,6 +864,9 @@ class Zigbee extends utils.Adapter {
|
|
|
923
864
|
meta.message.state = preparedValue;
|
|
924
865
|
}
|
|
925
866
|
}
|
|
867
|
+
if (has_elevated_debug) {
|
|
868
|
+
this.emit('device_debug', { ID:debugID, data: { states:[{id:stateDesc.id, value:value, payload:preparedValue, ep:stateDesc.epname}] , IO:false }});
|
|
869
|
+
}
|
|
926
870
|
|
|
927
871
|
if (preparedOptions !== undefined) {
|
|
928
872
|
if (preparedOptions.hasOwnProperty('state')) {
|
|
@@ -932,33 +876,52 @@ class Zigbee extends utils.Adapter {
|
|
|
932
876
|
|
|
933
877
|
try {
|
|
934
878
|
const result = await converter.convertSet(target, key, preparedValue, meta);
|
|
935
|
-
|
|
936
|
-
|
|
879
|
+
const message = `convert result ${safeJsonStringify(result)} for device ${deviceId}`;
|
|
880
|
+
if (has_elevated_debug) {
|
|
881
|
+
this.emit('device_debug', { ID:debugID, data: { flag: 'SUCCESS' , IO:false }, message:message});
|
|
882
|
+
}
|
|
937
883
|
else
|
|
938
|
-
this.log.debug(
|
|
884
|
+
if (this.debugActive) this.log.debug(message);
|
|
939
885
|
if (result !== undefined) {
|
|
940
|
-
if (stateModel && !isGroup) {
|
|
886
|
+
if (stateModel && !isGroup && !stateDesc.noack) {
|
|
941
887
|
this.acknowledgeState(deviceId, model, stateDesc, value);
|
|
942
888
|
}
|
|
943
889
|
// process sync state list
|
|
944
890
|
this.processSyncStatesList(deviceId, model, syncStateList);
|
|
945
891
|
}
|
|
946
|
-
else
|
|
947
|
-
if (has_elevated_debug)
|
|
948
|
-
|
|
949
|
-
|
|
892
|
+
else {
|
|
893
|
+
if (has_elevated_debug) {
|
|
894
|
+
const message = `Convert does not return a result result for ${key} with ${safeJsonStringify(preparedValue)} on device ${deviceId}.`;
|
|
895
|
+
this.emit('device_debug', { ID:debugID, data: { flag: '06' , IO:false }, message:message});
|
|
896
|
+
}
|
|
897
|
+
}
|
|
950
898
|
} catch (error) {
|
|
951
|
-
if (has_elevated_debug)
|
|
952
|
-
|
|
899
|
+
if (has_elevated_debug) {
|
|
900
|
+
const message = `caught error ${safeJsonStringify(error)} when setting value for device ${deviceId}.`;
|
|
901
|
+
this.emit('device_debug', { ID:debugID, data: { error: 'EXSET' , IO:false },message:message});
|
|
902
|
+
}
|
|
953
903
|
this.filterError(`Error ${error.code} on send command to ${deviceId}.` +
|
|
954
904
|
` Error: ${error.stack}`, `Send command to ${deviceId} failed with`, error);
|
|
955
905
|
}
|
|
956
906
|
});
|
|
957
907
|
} catch (err) {
|
|
958
|
-
|
|
908
|
+
const message = `No entity for ${deviceId} : ${err && err.message ? err.message : 'no error message'}`;
|
|
909
|
+
this.emit('device_debug', { ID:debugID, data: { error: 'EXPUB' , IO:false }, message:message});
|
|
959
910
|
}
|
|
960
911
|
}
|
|
961
912
|
|
|
913
|
+
|
|
914
|
+
extractEP(key, endpoints) {
|
|
915
|
+
try {
|
|
916
|
+
if (endpoints) for (const ep of Object.keys(endpoints)) {
|
|
917
|
+
if (key.endsWith('_'+ep)) return { setattr: key.replace('_'+ep, ''), epname:ep }
|
|
918
|
+
}
|
|
919
|
+
}
|
|
920
|
+
catch {
|
|
921
|
+
return {};
|
|
922
|
+
}
|
|
923
|
+
return {};
|
|
924
|
+
}
|
|
962
925
|
// This function is introduced to explicitly allow user level scripts to send Commands
|
|
963
926
|
// directly to the zigbee device. It utilizes the zigbee-herdsman-converters to generate
|
|
964
927
|
// the exact zigbee message to be sent and can be used to set device options which are
|
|
@@ -972,7 +935,6 @@ class Zigbee extends utils.Adapter {
|
|
|
972
935
|
// endpoint: optional: the endpoint to send the data to, if supported.
|
|
973
936
|
//
|
|
974
937
|
async sendPayload(payload) {
|
|
975
|
-
this.log.debug(`publishToDevice called with ${safeJsonStringify(payload)}`);
|
|
976
938
|
let payloadObj = {};
|
|
977
939
|
if (typeof payload === 'string') {
|
|
978
940
|
try {
|
|
@@ -987,7 +949,7 @@ class Zigbee extends utils.Adapter {
|
|
|
987
949
|
}
|
|
988
950
|
} else if (typeof payload === 'object') {
|
|
989
951
|
payloadObj = payload;
|
|
990
|
-
}
|
|
952
|
+
} else return { success: false, error: 'illegal type of payload: ' + typeof payload};
|
|
991
953
|
|
|
992
954
|
if (payloadObj.hasOwnProperty('device') && payloadObj.hasOwnProperty('payload')) {
|
|
993
955
|
try {
|
|
@@ -1012,16 +974,24 @@ class Zigbee extends utils.Adapter {
|
|
|
1012
974
|
this.sendError(`Illegal payload type for ${safeJsonStringify(payloadObj.device)}`);
|
|
1013
975
|
return {success: false, error: `Illegal payload type for ${safeJsonStringify(payloadObj.device)}`};
|
|
1014
976
|
}
|
|
977
|
+
const endpoints = mappedModel && mappedModel.endpoint ? mappedModel.endpoint(entity.device) : null;
|
|
1015
978
|
for (const key in payloadObj.payload) {
|
|
1016
979
|
if (payloadObj.payload[key] != undefined) {
|
|
1017
980
|
const datatype = typeof payloadObj.payload[key];
|
|
981
|
+
const epobj = this.extractEP(key, endpoints);
|
|
982
|
+
if (payloadObj.endpoint) {
|
|
983
|
+
epobj.epname = payloadObj.endpoint;
|
|
984
|
+
delete epobj.setattr;
|
|
985
|
+
}
|
|
1018
986
|
stateList.push({
|
|
1019
987
|
stateDesc: {
|
|
1020
988
|
id: key,
|
|
1021
989
|
prop: key,
|
|
1022
990
|
role: 'state',
|
|
1023
991
|
type: datatype,
|
|
1024
|
-
|
|
992
|
+
noack:true,
|
|
993
|
+
epname: epobj.epname,
|
|
994
|
+
setattr: epobj.setattr,
|
|
1025
995
|
},
|
|
1026
996
|
value: payloadObj.payload[key],
|
|
1027
997
|
index: 0,
|
|
@@ -1030,8 +1000,7 @@ class Zigbee extends utils.Adapter {
|
|
|
1030
1000
|
}
|
|
1031
1001
|
}
|
|
1032
1002
|
try {
|
|
1033
|
-
this.
|
|
1034
|
-
await this.publishFromState(`0x${payload.device}`, '', undefined, stateList, payload.options);
|
|
1003
|
+
await this.publishFromState(`0x${payload.device}`, payload.model, payload.stateModel, stateList, payload.options, Date.now());
|
|
1035
1004
|
return {success: true};
|
|
1036
1005
|
} catch (error) {
|
|
1037
1006
|
this.log.error(`Error ${error.code} on send command to ${payload.device}.` + ` Error: ${error.stack} ` + `Send command to ${payload.device} failed with ` + error);
|
|
@@ -1048,28 +1017,39 @@ class Zigbee extends utils.Adapter {
|
|
|
1048
1017
|
|
|
1049
1018
|
|
|
1050
1019
|
newDevice(entity) {
|
|
1051
|
-
|
|
1052
|
-
this.
|
|
1020
|
+
|
|
1021
|
+
if (this.debugActive) this.log.debug(`New device event: ${safeJsonStringify(entity)}`);
|
|
1022
|
+
this.stController.AddModelFromHerdsman(entity.device, entity.mapped ? entity.mapped.model : entity.device.modelID)
|
|
1023
|
+
|
|
1053
1024
|
const dev = entity.device;
|
|
1025
|
+
const model = (entity.mapped) ? entity.mapped.model : dev.modelID;
|
|
1026
|
+
this.log.debug(`New device event: ${safeJsonStringify(entity)}`);
|
|
1027
|
+
if (!entity.mapped && !entity.device.interviewing) {
|
|
1028
|
+
const msg = `New device: '${dev.ieeeAddr}' does not have a known model. please provide an external converter for '${dev.modelID}'.`;
|
|
1029
|
+
this.log.warn(msg);
|
|
1030
|
+
this.logToPairing(msg, true);
|
|
1031
|
+
}
|
|
1032
|
+
this.stController.AddModelFromHerdsman(entity.device, model)
|
|
1054
1033
|
if (dev) {
|
|
1055
1034
|
this.getObject(dev.ieeeAddr.substr(2), (err, obj) => {
|
|
1056
1035
|
if (!obj) {
|
|
1057
1036
|
const model = (entity.mapped) ? entity.mapped.model : entity.device.modelID;
|
|
1058
|
-
this.log.debug(`new device ${dev.ieeeAddr} ${dev.networkAddress} ${model} `);
|
|
1037
|
+
if (this.debugActive) this.log.debug(`new device ${dev.ieeeAddr} ${dev.networkAddress} ${model} `);
|
|
1038
|
+
|
|
1059
1039
|
this.logToPairing(`New device joined '${dev.ieeeAddr}' model ${model}`, true);
|
|
1060
1040
|
this.stController.updateDev(dev.ieeeAddr.substr(2), model, model, () =>
|
|
1061
1041
|
this.stController.syncDevStates(dev, model));
|
|
1062
1042
|
}
|
|
1063
|
-
else this.log.debug(`Device ${safeJsonStringify(entity)} rejoined, no new device`);
|
|
1043
|
+
else if (this.debugActive) this.log.debug(`Device ${safeJsonStringify(entity)} rejoined, no new device`);
|
|
1064
1044
|
});
|
|
1065
1045
|
}
|
|
1066
1046
|
}
|
|
1067
1047
|
|
|
1068
1048
|
leaveDevice(ieeeAddr) {
|
|
1069
|
-
this.log.debug(`Leave device event: ${ieeeAddr}`);
|
|
1049
|
+
if (this.debugActive) this.log.debug(`Leave device event: ${ieeeAddr}`);
|
|
1070
1050
|
if (ieeeAddr) {
|
|
1071
1051
|
const devId = ieeeAddr.substr(2);
|
|
1072
|
-
this.log.debug(`Delete device ${devId} from iobroker.`);
|
|
1052
|
+
if (this.debugActive) this.log.debug(`Delete device ${devId} from iobroker.`);
|
|
1073
1053
|
this.stController.deleteObj(devId);
|
|
1074
1054
|
}
|
|
1075
1055
|
}
|
|
@@ -1120,10 +1100,10 @@ class Zigbee extends utils.Adapter {
|
|
|
1120
1100
|
}
|
|
1121
1101
|
}
|
|
1122
1102
|
|
|
1123
|
-
getZigbeeOptions() {
|
|
1103
|
+
getZigbeeOptions(_overrideOptions) {
|
|
1104
|
+
const override = (_overrideOptions ? _overrideOptions:{});
|
|
1124
1105
|
// file path for db
|
|
1125
|
-
|
|
1126
|
-
dbDir = dbDir.replace('.', '_');
|
|
1106
|
+
const dbDir = this.expandFileName('');
|
|
1127
1107
|
|
|
1128
1108
|
if (this.systemConfig && !fs.existsSync(dbDir)) {
|
|
1129
1109
|
try {
|
|
@@ -1133,21 +1113,21 @@ class Zigbee extends utils.Adapter {
|
|
|
1133
1113
|
this.sendError(`Cannot create directory ${dbDir}: ${e}`);
|
|
1134
1114
|
}
|
|
1135
1115
|
}
|
|
1136
|
-
const port = this.config.port;
|
|
1116
|
+
const port = override.port ? override.port : this.config.port;
|
|
1137
1117
|
if (!port) {
|
|
1138
1118
|
this.log.error('Serial port not selected! Go to settings page.');
|
|
1139
1119
|
this.sendError('Serial port not selected! Go to settings page.');
|
|
1140
1120
|
}
|
|
1141
|
-
const panID = parseInt(this.config.panID ? this.config.panID : 0x1a62);
|
|
1142
|
-
const channel = parseInt(this.config.channel ? this.config.channel : 11);
|
|
1143
|
-
const precfgkey = createByteArray(this.config.precfgkey ? this.config.precfgkey : '01030507090B0D0F00020406080A0C0D');
|
|
1144
|
-
const extPanId = createByteArray(this.config.extPanID ? this.config.extPanID : 'DDDDDDDDDDDDDDDD').reverse();
|
|
1145
|
-
const adapterType = this.config.adapterType || 'zstack';
|
|
1121
|
+
const panID = parseInt(override.panID ? override.panID : this.config.panID ? this.config.panID : 0x1a62);
|
|
1122
|
+
const channel = parseInt(override.channel ? override.channel : this.config.channel ? this.config.channel : 11);
|
|
1123
|
+
const precfgkey = createByteArray(override.precfgkey ? override.precfgkey : this.config.precfgkey ? this.config.precfgkey : '01030507090B0D0F00020406080A0C0D');
|
|
1124
|
+
const extPanId = createByteArray(override.extPanID ? override.extPanID : this.config.extPanID ? this.config.extPanID : 'DDDDDDDDDDDDDDDD').reverse();
|
|
1125
|
+
const adapterType = override.adapterType ? override.adapterType : this.config.adapterType || 'zstack';
|
|
1146
1126
|
// https://github.com/ioBroker/ioBroker.zigbee/issues/668
|
|
1147
1127
|
const extPanIdFix = this.config.extPanIdFix ? this.config.extPanIdFix : false;
|
|
1148
|
-
const baudRate = parseInt(this.config.baudRate ? this.config.baudRate : 115200);
|
|
1128
|
+
const baudRate = parseInt(override.baudRate ? override.baudRate : this.config.baudRate ? this.config.baudRate : 115200);
|
|
1149
1129
|
|
|
1150
|
-
const setRtscts = this.config.flowCTRL ? this.config.flowCTRL : false;
|
|
1130
|
+
const setRtscts = override.flowCTRL ? override.flowCTRL : this.config.flowCTRL ? this.config.flowCTRL : false;
|
|
1151
1131
|
|
|
1152
1132
|
return {
|
|
1153
1133
|
net: {
|
|
@@ -1162,6 +1142,7 @@ class Zigbee extends utils.Adapter {
|
|
|
1162
1142
|
rtscts: setRtscts,
|
|
1163
1143
|
adapter: adapterType,
|
|
1164
1144
|
},
|
|
1145
|
+
transmitpower: this.transmitPower,
|
|
1165
1146
|
dbDir: dbDir,
|
|
1166
1147
|
dbPath: 'shepherd.db',
|
|
1167
1148
|
backupPath: 'nvbackup.json',
|
|
@@ -1170,7 +1151,7 @@ class Zigbee extends utils.Adapter {
|
|
|
1170
1151
|
transmitPower: this.config.transmitPower,
|
|
1171
1152
|
disableBackup: this.config.disableBackup,
|
|
1172
1153
|
extPanIdFix: extPanIdFix,
|
|
1173
|
-
startWithInconsistent: this.config.startWithInconsistent || false,
|
|
1154
|
+
startWithInconsistent: override.startWithInconsistent ? override.startWithInconsistent: this.config.startWithInconsistent || false,
|
|
1174
1155
|
};
|
|
1175
1156
|
}
|
|
1176
1157
|
|
|
@@ -1194,7 +1175,12 @@ class Zigbee extends utils.Adapter {
|
|
|
1194
1175
|
}
|
|
1195
1176
|
|
|
1196
1177
|
expandFileName(fn) {
|
|
1197
|
-
return path.join(
|
|
1178
|
+
return path.join(this.getDataFolder(), fn);
|
|
1179
|
+
}
|
|
1180
|
+
|
|
1181
|
+
getDataFolder() {
|
|
1182
|
+
const datapath=this.namespace.replace('.','_');
|
|
1183
|
+
return path.join(utils.getAbsoluteInstanceDataDir(this).replace(this.namespace, datapath));
|
|
1198
1184
|
}
|
|
1199
1185
|
|
|
1200
1186
|
onLog(level, msg, data) {
|
|
@@ -1205,7 +1191,8 @@ class Zigbee extends utils.Adapter {
|
|
|
1205
1191
|
logger = this.log.error;
|
|
1206
1192
|
if (data)
|
|
1207
1193
|
data = data.toString();
|
|
1208
|
-
this.
|
|
1194
|
+
if (this.ErrorMessagesToPairing)
|
|
1195
|
+
this.logToPairing(`Error: ${msg}. ${data}`, true);
|
|
1209
1196
|
this.sendError(`Error: ${msg}. ${data}`);
|
|
1210
1197
|
break;
|
|
1211
1198
|
case 'debug':
|