iobroker.zigbee 2.0.1 → 2.0.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +77 -36
- package/admin/admin.js +211 -19
- package/admin/img/philips_hue_lom001.png +0 -0
- package/admin/tab_m.html +13 -8
- package/docs/de/readme.md +21 -28
- 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 +30 -51
- package/lib/DeviceDebug.js +80 -0
- package/lib/commands.js +24 -3
- package/lib/developer.js +0 -0
- package/lib/devices.js +14 -4
- package/lib/exposes.js +46 -46
- package/lib/groups.js +6 -8
- package/lib/localConfig.js +11 -6
- package/lib/ota.js +11 -10
- package/lib/statescontroller.js +314 -101
- package/lib/utils.js +4 -2
- package/lib/zbDeviceAvailability.js +3 -9
- package/lib/zbDeviceConfigure.js +68 -34
- package/lib/zigbeecontroller.js +50 -14
- package/main.js +168 -203
- package/package.json +5 -5
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,9 @@ 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
|
+
|
|
82
86
|
|
|
83
87
|
this.plugins = [
|
|
84
88
|
new SerialListPlugin(this),
|
|
@@ -115,6 +119,19 @@ class Zigbee extends utils.Adapter {
|
|
|
115
119
|
}
|
|
116
120
|
}
|
|
117
121
|
|
|
122
|
+
warn(message) {
|
|
123
|
+
this.log.warn(message);
|
|
124
|
+
}
|
|
125
|
+
debug(message) {
|
|
126
|
+
this.log.debug(message);
|
|
127
|
+
}
|
|
128
|
+
error(message) {
|
|
129
|
+
this.log.error(message);
|
|
130
|
+
}
|
|
131
|
+
info(message) {
|
|
132
|
+
this.log.info(message);
|
|
133
|
+
}
|
|
134
|
+
|
|
118
135
|
sendError(error, message) {
|
|
119
136
|
try {
|
|
120
137
|
if (this.supportsFeature && this.supportsFeature('PLUGINS')) {
|
|
@@ -210,12 +227,15 @@ class Zigbee extends utils.Adapter {
|
|
|
210
227
|
this.zbController.on('new', this.newDevice.bind(this));
|
|
211
228
|
this.zbController.on('leave', this.leaveDevice.bind(this));
|
|
212
229
|
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));
|
|
230
|
+
this.zbController.on('event', this.stController.onZigbeeEvent.bind(this.stController));
|
|
231
|
+
this.zbController.on('msg', this.stController.onZigbeeEvent.bind(this.stController));
|
|
215
232
|
this.zbController.on('publish', this.publishToState.bind(this));
|
|
216
233
|
this.zbController.configure(zigbeeOptions);
|
|
217
234
|
await this.callPluginMethod('configure', [zigbeeOptions]);
|
|
218
235
|
|
|
236
|
+
// elevated debug handling
|
|
237
|
+
this.deviceDebug.start(this.stController, this.zbController);
|
|
238
|
+
|
|
219
239
|
this.reconnectCounter = 1;
|
|
220
240
|
this.doConnect();
|
|
221
241
|
}
|
|
@@ -304,6 +324,9 @@ class Zigbee extends utils.Adapter {
|
|
|
304
324
|
//fs.writeFileSync(mN+'.tmp3', modifiedCode)
|
|
305
325
|
converterLoaded &= this.SandboxRequire(sandbox,[...modifiedCode.matchAll(/import\s+\{(.+)\}\s+from\s+(\S+);/gm)]);
|
|
306
326
|
modifiedCode = modifiedCode.replace(/import\s+\{.+\}\s+from\s+\S+;/gm, '');
|
|
327
|
+
|
|
328
|
+
converterLoaded &= this.SandboxRequire(sandbox,[...modifiedCode.matchAll(/import\s+(.+)\s+from\s+(\S+);/gm)]);
|
|
329
|
+
modifiedCode = modifiedCode.replace(/import\s+.+\s+from\s+\S+;/gm, '');
|
|
307
330
|
//fs.writeFileSync(mN+'.tmp4', modifiedCode)
|
|
308
331
|
converterLoaded &= this.SandboxRequire(sandbox,[...modifiedCode.matchAll(/const\s+\{(.+)\}\s+=\s+require\((.+)\)/gm)]);
|
|
309
332
|
modifiedCode = modifiedCode.replace(/const\s+\{.+\}\s+=\s+require\(.+\)/gm, '');
|
|
@@ -312,7 +335,12 @@ class Zigbee extends utils.Adapter {
|
|
|
312
335
|
modifiedCode = modifiedCode.replace(/const\s+\S+\s+=\s+require\(.+\)/gm, '');
|
|
313
336
|
//fs.writeFileSync(mN+'.tmp6', modifiedCode)
|
|
314
337
|
|
|
315
|
-
|
|
338
|
+
for(const component of modifiedCode.matchAll(/const (.+):(.+)=/gm)) {
|
|
339
|
+
modifiedCode = modifiedCode.replace(component[0], `const ${component[1]} = `);
|
|
340
|
+
}
|
|
341
|
+
modifiedCode = modifiedCode.replace(/export .+;/gm, '');
|
|
342
|
+
|
|
343
|
+
fs.writeFileSync(mN+'.tmp', modifiedCode)
|
|
316
344
|
|
|
317
345
|
if (converterLoaded) {
|
|
318
346
|
try {
|
|
@@ -345,10 +373,27 @@ class Zigbee extends utils.Adapter {
|
|
|
345
373
|
const toAdd = {...definition};
|
|
346
374
|
delete toAdd['homeassistant'];
|
|
347
375
|
try {
|
|
348
|
-
zigbeeHerdsmanConverters.
|
|
376
|
+
if (zigbeeHerdsmanConverters.hasOwnProperty('addExternalDefinition')) {
|
|
377
|
+
zigbeeHerdsmanConverters.addExternalDefinition(toAdd);
|
|
378
|
+
this.log.info('added external converter using addExternalDefinition')
|
|
379
|
+
}
|
|
380
|
+
else if (zigbeeHerdsmanConverters.hasOwnProperty('addDefinition')) {
|
|
381
|
+
zigbeeHerdsmanConverters.addDefinition(toAdd);
|
|
382
|
+
this.log.info('added external converter using addDefinition')
|
|
383
|
+
}
|
|
384
|
+
|
|
385
|
+
/*
|
|
386
|
+
for (const zigbeeModel of toAdd.zigbeeModel)
|
|
387
|
+
{
|
|
388
|
+
try {
|
|
389
|
+
zigbeeHerdsmanConverters.addToExternalDefinitionsLookup(zigbeeModel, toAdd.toAdd);
|
|
390
|
+
} catch (e) {
|
|
391
|
+
this.log.error(`unable to apply external converter ${JSON.stringify(toAdd)} for device ${zigbeeModel}: ${e && e.message ? e.message : 'no error message available'}`);
|
|
392
|
+
}
|
|
393
|
+
}
|
|
394
|
+
*/
|
|
349
395
|
} catch (e) {
|
|
350
|
-
this.log.error(`unable to apply external converter ${JSON.stringify(toAdd)}`);
|
|
351
|
-
this.log.error(`${e}`);
|
|
396
|
+
this.log.error(`unable to apply external converter for ${JSON.stringify(toAdd.model)}: ${e && e.message ? e.message : 'no error message available'}`);
|
|
352
397
|
}
|
|
353
398
|
}
|
|
354
399
|
}
|
|
@@ -359,15 +404,15 @@ class Zigbee extends utils.Adapter {
|
|
|
359
404
|
const DebugIdentify = require('./debugidentify');
|
|
360
405
|
debugversion = DebugIdentify.ReportIdentifier();
|
|
361
406
|
} catch {
|
|
362
|
-
debugversion = '
|
|
407
|
+
debugversion = 'npm ...';
|
|
363
408
|
}
|
|
364
409
|
|
|
365
410
|
// installed version
|
|
366
411
|
let gitVers = '';
|
|
367
412
|
try {
|
|
368
|
-
this.log.info(`Starting
|
|
413
|
+
this.log.info(`Starting Adapter ${debugversion}`);
|
|
369
414
|
|
|
370
|
-
|
|
415
|
+
this.getForeignObject(`system.adapter.${this.namespace}`,async (err, obj) => {
|
|
371
416
|
if (!err && obj && obj.common.installedFrom && obj.common.installedFrom.includes('://')) {
|
|
372
417
|
const instFrom = obj.common.installedFrom;
|
|
373
418
|
gitVers = gitVers + instFrom.replace('tarball', 'commit');
|
|
@@ -375,9 +420,9 @@ class Zigbee extends utils.Adapter {
|
|
|
375
420
|
gitVers = obj.common.installedFrom;
|
|
376
421
|
}
|
|
377
422
|
this.log.info(`Installed Version: ${gitVers} (Converters ${zigbeeHerdsmanConvertersPackage.version} Herdsman ${zigbeeHerdsmanPackage.version})`);
|
|
423
|
+
await this.zbController.start();
|
|
378
424
|
});
|
|
379
425
|
|
|
380
|
-
await this.zbController.start();
|
|
381
426
|
} catch (error) {
|
|
382
427
|
this.setState('info.connection', false, true);
|
|
383
428
|
this.log.error(`Failed to start Zigbee`);
|
|
@@ -434,7 +479,7 @@ class Zigbee extends utils.Adapter {
|
|
|
434
479
|
const configExtPanId = this.config.extPanID ? `0x${this.config.extPanID.toLowerCase()}` : '0xdddddddddddddddd';
|
|
435
480
|
let networkExtPanId = (await this.zbController.herdsman.getNetworkParameters()).extendedPanID;
|
|
436
481
|
let needChange = false;
|
|
437
|
-
this.log.
|
|
482
|
+
this.log.info(`Config value ${configExtPanId} : Network value ${networkExtPanId}`);
|
|
438
483
|
const adapterType = this.config.adapterType || 'zstack';
|
|
439
484
|
if (adapterType === 'zstack') {
|
|
440
485
|
if (configExtPanId !== networkExtPanId) {
|
|
@@ -480,9 +525,8 @@ class Zigbee extends utils.Adapter {
|
|
|
480
525
|
}
|
|
481
526
|
|
|
482
527
|
await this.setState('info.connection', true, true);
|
|
483
|
-
|
|
484
|
-
const devicesFromDB = this.zbController.getClientIterator(false);
|
|
485
528
|
this.stController.CleanupRequired(false);
|
|
529
|
+
const devicesFromDB = this.zbController.getClientIterator(false);
|
|
486
530
|
for (const device of devicesFromDB) {
|
|
487
531
|
const entity = await this.zbController.resolveEntity(device);
|
|
488
532
|
if (entity) {
|
|
@@ -490,6 +534,7 @@ class Zigbee extends utils.Adapter {
|
|
|
490
534
|
this.stController.updateDev(device.ieeeAddr.substr(2), model, model, () =>
|
|
491
535
|
this.stController.syncDevStates(device, model));
|
|
492
536
|
}
|
|
537
|
+
else (this.log.warn('resolveEntity returned no entity'));
|
|
493
538
|
}
|
|
494
539
|
await this.callPluginMethod('start', [this.zbController, this.stController]);
|
|
495
540
|
}
|
|
@@ -527,154 +572,6 @@ class Zigbee extends utils.Adapter {
|
|
|
527
572
|
});
|
|
528
573
|
}
|
|
529
574
|
|
|
530
|
-
async onZigbeeEvent(type, entity, message) {
|
|
531
|
-
this.log.debug(`Type ${type} device ${safeJsonStringify(entity)} incoming event: ${safeJsonStringify(message)}`);
|
|
532
|
-
|
|
533
|
-
const device = entity.device;
|
|
534
|
-
const mappedModel = entity.mapped;
|
|
535
|
-
const model = entity.mapped ? entity.mapped.model : entity.device.modelID;
|
|
536
|
-
const cluster = message.cluster;
|
|
537
|
-
const devId = device.ieeeAddr.substr(2);
|
|
538
|
-
const meta = {device};
|
|
539
|
-
|
|
540
|
-
const has_elevated_debug = this.stController.checkDebugDevice(devId);
|
|
541
|
-
|
|
542
|
-
if (has_elevated_debug) {
|
|
543
|
-
const shortMessage = {};
|
|
544
|
-
for(const propertyName in message) {
|
|
545
|
-
shortMessage[propertyName] = message[propertyName];
|
|
546
|
-
}
|
|
547
|
-
shortMessage.device = device.ieeeAddr;
|
|
548
|
-
shortMessage.meta = undefined;
|
|
549
|
-
shortMessage.endpoint = (message.endpoint.ID ? message.endpoint.ID: -1);
|
|
550
|
-
this.log.warn(`ELEVATED I00: Zigbee Event of Type ${type} from device ${safeJsonStringify(device.ieeeAddr)}, incoming event: ${safeJsonStringify(shortMessage)}`);
|
|
551
|
-
}
|
|
552
|
-
// this assigment give possibility to use iobroker logger in code of the converters, via meta.logger
|
|
553
|
-
meta.logger = this.log;
|
|
554
|
-
|
|
555
|
-
await this.checkIfModelUpdate(entity);
|
|
556
|
-
|
|
557
|
-
let _voltage = 0;
|
|
558
|
-
let _temperature = 0;
|
|
559
|
-
let _humidity = 0;
|
|
560
|
-
|
|
561
|
-
let isMessure = false;
|
|
562
|
-
let isBattKey = false;
|
|
563
|
-
|
|
564
|
-
if (mappedModel && mappedModel.meta && mappedModel.meta.battery) {
|
|
565
|
-
const isVoltage = mappedModel.meta.battery.hasOwnProperty('voltageToPercentage');
|
|
566
|
-
|
|
567
|
-
if (isVoltage) {
|
|
568
|
-
const keys = Object.keys(message.data);
|
|
569
|
-
|
|
570
|
-
for (const key of keys) {
|
|
571
|
-
const value = message.data[key];
|
|
572
|
-
|
|
573
|
-
if (value && value[1]) {
|
|
574
|
-
if (key == 65282 && value[1][1]) {
|
|
575
|
-
_voltage = value[1][1].elmVal;
|
|
576
|
-
isBattKey = true;
|
|
577
|
-
break;
|
|
578
|
-
}
|
|
579
|
-
if (key == 65281) {
|
|
580
|
-
_voltage = value[1];
|
|
581
|
-
isBattKey = true;
|
|
582
|
-
_temperature = value[100];
|
|
583
|
-
_temperature = _temperature /100;
|
|
584
|
-
_humidity = value[101];
|
|
585
|
-
_humidity = _humidity / 100;
|
|
586
|
-
isMessure = true;
|
|
587
|
-
break;
|
|
588
|
-
}
|
|
589
|
-
}
|
|
590
|
-
}
|
|
591
|
-
}
|
|
592
|
-
}
|
|
593
|
-
|
|
594
|
-
// always publish link_quality and battery
|
|
595
|
-
if (message.linkquality) { // send battery with
|
|
596
|
-
this.publishToState(devId, model, {linkquality: message.linkquality});
|
|
597
|
-
if (isBattKey) {
|
|
598
|
-
this.publishToState(devId, model, {voltage: _voltage});
|
|
599
|
-
const battProz = zigbeeHerdsmanConvertersUtils.batteryVoltageToPercentage(_voltage,entity.mapped.meta.battery.voltageToPercentage);
|
|
600
|
-
this.publishToState(devId, model, {battery: battProz});
|
|
601
|
-
}
|
|
602
|
-
if (isMessure) {
|
|
603
|
-
this.publishToState(devId, model, {temperature: _temperature});
|
|
604
|
-
this.publishToState(devId, model, {humidity: _humidity});
|
|
605
|
-
}
|
|
606
|
-
}
|
|
607
|
-
|
|
608
|
-
// publish raw event to "from_zigbee"
|
|
609
|
-
// some cleanup
|
|
610
|
-
const msgForState = Object.assign({}, message);
|
|
611
|
-
delete msgForState['device'];
|
|
612
|
-
delete msgForState['endpoint'];
|
|
613
|
-
|
|
614
|
-
msgForState['endpoint_id'] = message.endpoint.ID;
|
|
615
|
-
this.publishToState(devId, model, {msg_from_zigbee: safeJsonStringify(msgForState)});
|
|
616
|
-
|
|
617
|
-
if (!entity.mapped) {
|
|
618
|
-
return;
|
|
619
|
-
}
|
|
620
|
-
|
|
621
|
-
let converters = mappedModel.fromZigbee.filter(c => c && c.cluster === cluster && (
|
|
622
|
-
Array.isArray(c.type) ? c.type.includes(type) : c.type === type));
|
|
623
|
-
|
|
624
|
-
|
|
625
|
-
if (!converters.length && type === 'readResponse') {
|
|
626
|
-
converters = mappedModel.fromZigbee.filter(c => c.cluster === cluster && (
|
|
627
|
-
Array.isArray(c.type) ? c.type.includes('attributeReport') : c.type === 'attributeReport'));
|
|
628
|
-
}
|
|
629
|
-
|
|
630
|
-
if (!converters.length) {
|
|
631
|
-
if (type !== 'readResponse') {
|
|
632
|
-
this.log.debug(`No converter available for '${mappedModel.model}' '${devId}' with cluster '${cluster}' and type '${type}'`);
|
|
633
|
-
if (has_elevated_debug)
|
|
634
|
-
this.log.warn(`ELEVATED IE00: No converter available for '${mappedModel.model}' '${devId}' with cluster '${cluster}' and type '${type}'`);
|
|
635
|
-
}
|
|
636
|
-
return;
|
|
637
|
-
}
|
|
638
|
-
|
|
639
|
-
meta.state = { state: '' }; // for tuya
|
|
640
|
-
|
|
641
|
-
this.processConverters(converters, devId, model, mappedModel, message, meta)
|
|
642
|
-
.catch((error) => {
|
|
643
|
-
// 'Error: Expected one of: 0, 1, got: 'undefined''
|
|
644
|
-
if (cluster !== '64529') {
|
|
645
|
-
this.log.error(`Error while processing converters DEVICE_ID: '${devId}' cluster '${cluster}' type '${type}'`);
|
|
646
|
-
}
|
|
647
|
-
});
|
|
648
|
-
}
|
|
649
|
-
|
|
650
|
-
async processConverters(converters, devId, model, mappedModel, message, meta) {
|
|
651
|
-
for (const converter of converters) {
|
|
652
|
-
const publish = (payload) => {
|
|
653
|
-
this.log.debug(`Publish ${safeJsonStringify(payload)} to ${safeJsonStringify(devId)}`);
|
|
654
|
-
if (typeof payload === 'object') {
|
|
655
|
-
this.publishToState(devId, model, payload);
|
|
656
|
-
}
|
|
657
|
-
};
|
|
658
|
-
|
|
659
|
-
const options = await new Promise((resolve, reject) => {
|
|
660
|
-
this.stController.collectOptions(devId, model, (options) => {
|
|
661
|
-
resolve(options);
|
|
662
|
-
});
|
|
663
|
-
});
|
|
664
|
-
|
|
665
|
-
const payload = await new Promise((resolve, reject) => {
|
|
666
|
-
const payloadConv = converter.convert(mappedModel, message, publish, options, meta);
|
|
667
|
-
if (typeof payloadConv === 'object') {
|
|
668
|
-
resolve(payloadConv);
|
|
669
|
-
}
|
|
670
|
-
});
|
|
671
|
-
|
|
672
|
-
publish(payload);
|
|
673
|
-
}
|
|
674
|
-
}
|
|
675
|
-
|
|
676
|
-
|
|
677
|
-
|
|
678
575
|
publishToState(devId, model, payload) {
|
|
679
576
|
this.stController.publishToState(devId, model, payload);
|
|
680
577
|
}
|
|
@@ -695,15 +592,18 @@ class Zigbee extends utils.Adapter {
|
|
|
695
592
|
});
|
|
696
593
|
}
|
|
697
594
|
|
|
698
|
-
async publishFromState(deviceId, model, stateModel, stateList, options) {
|
|
595
|
+
async publishFromState(deviceId, model, stateModel, stateList, options, debugID) {
|
|
699
596
|
let isGroup = false;
|
|
700
597
|
const has_elevated_debug = this.stController.checkDebugDevice(deviceId)
|
|
701
598
|
|
|
702
599
|
if (has_elevated_debug)
|
|
703
600
|
{
|
|
704
601
|
const stateNames = [];
|
|
705
|
-
|
|
706
|
-
|
|
602
|
+
for (const state of stateList) {
|
|
603
|
+
stateNames.push(state.stateDesc.id);
|
|
604
|
+
}
|
|
605
|
+
const message = `Publishing to ${deviceId} of model ${model} with ${stateNames.join(', ')}`;
|
|
606
|
+
this.emit('device_debug', { ID:debugID, data: { ID: deviceId, flag: '03', IO:false }, message: message});
|
|
707
607
|
}
|
|
708
608
|
else
|
|
709
609
|
this.log.debug(`publishFromState : ${deviceId} ${model} ${safeJsonStringify(stateList)}`);
|
|
@@ -718,7 +618,10 @@ class Zigbee extends utils.Adapter {
|
|
|
718
618
|
|
|
719
619
|
if (!mappedModel) {
|
|
720
620
|
this.log.debug(`No mapped model for ${model}`);
|
|
721
|
-
if (has_elevated_debug)
|
|
621
|
+
if (has_elevated_debug) {
|
|
622
|
+
const message=`No mapped model ${deviceId} (model ${model})`;
|
|
623
|
+
this.emit('device_debug', { ID:debugID, data: { error: 'NOMODEL' , IO:false }, message: message});
|
|
624
|
+
}
|
|
722
625
|
return;
|
|
723
626
|
}
|
|
724
627
|
|
|
@@ -735,13 +638,20 @@ class Zigbee extends utils.Adapter {
|
|
|
735
638
|
if (stateDesc.id === 'send_payload') {
|
|
736
639
|
try {
|
|
737
640
|
const json_value = JSON.parse(value);
|
|
738
|
-
const payload = {device: deviceId.replace('0x', ''), payload: json_value};
|
|
641
|
+
const payload = {device: deviceId.replace('0x', ''), payload: json_value, model:model, stateModel:stateModel};
|
|
642
|
+
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 }});
|
|
643
|
+
|
|
739
644
|
const result = await this.sendPayload(payload);
|
|
740
645
|
if (result.hasOwnProperty('success') && result.success) {
|
|
741
646
|
this.acknowledgeState(deviceId, model, stateDesc, value);
|
|
742
647
|
}
|
|
648
|
+
else {
|
|
649
|
+
this.error('Error in SendPayload: '+result.error.message);
|
|
650
|
+
}
|
|
743
651
|
} catch (error) {
|
|
744
|
-
|
|
652
|
+
const message = `send_payload: ${value} does not parse as JSON Object : ${error.message}`;
|
|
653
|
+
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});
|
|
654
|
+
else this.error(message);
|
|
745
655
|
return;
|
|
746
656
|
}
|
|
747
657
|
return;
|
|
@@ -749,8 +659,10 @@ class Zigbee extends utils.Adapter {
|
|
|
749
659
|
|
|
750
660
|
if (stateDesc.isOption || stateDesc.compositeState) {
|
|
751
661
|
// acknowledge state with given value
|
|
752
|
-
if (has_elevated_debug)
|
|
662
|
+
if (has_elevated_debug) {
|
|
753
663
|
this.log.warn('ELEVATED OC: changed state: ' + JSON.stringify(changedState));
|
|
664
|
+
this.emit('device_debug', { ID:debugID, data: { flag: 'cc', states:[{id:stateDesc.id, value:value, payload:'none (OC State)'}] , IO:false }});
|
|
665
|
+
}
|
|
754
666
|
else
|
|
755
667
|
this.log.debug('changed composite state: ' + JSON.stringify(changedState));
|
|
756
668
|
|
|
@@ -770,8 +682,11 @@ class Zigbee extends utils.Adapter {
|
|
|
770
682
|
}
|
|
771
683
|
if (mappedModel) {
|
|
772
684
|
this.query_device_block.push(deviceId);
|
|
773
|
-
if (has_elevated_debug)
|
|
774
|
-
|
|
685
|
+
if (has_elevated_debug) {
|
|
686
|
+
const message = `Device query for '${entity.device.ieeeAddr}/${entity.device.endpoints[0].ID}' triggered`;
|
|
687
|
+
//this.log.warn(`ELEVATED O06: ${message}`);
|
|
688
|
+
this.emit('device_debug', { ID:debugID, data: { flag: 'qs' ,states:[{id:stateDesc.id, value:value, payload:'none for device query'}], IO:false }, message:message});
|
|
689
|
+
}
|
|
775
690
|
else
|
|
776
691
|
this.log.debug(`Device query for '${entity.device.ieeeAddr}' started`);
|
|
777
692
|
for (const converter of mappedModel.toZigbee) {
|
|
@@ -781,7 +696,9 @@ class Zigbee extends utils.Adapter {
|
|
|
781
696
|
await converter.convertGet(entity.device.endpoints[0], ckey, {});
|
|
782
697
|
} catch (error) {
|
|
783
698
|
if (has_elevated_debug) {
|
|
784
|
-
|
|
699
|
+
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'}`;
|
|
700
|
+
this.log.warn(`ELEVATED OE02.1 ${message}`);
|
|
701
|
+
this.emit('device_debug', { ID:debugID, data: { error: 'NOTREAD' , IO:false }, message:message });
|
|
785
702
|
}
|
|
786
703
|
else
|
|
787
704
|
this.log.info(`failed to read state ${JSON.stringify(ckey)} of ${entity.device.ieeeAddr}/${entity.device.endpoints[0].ID} after device query`);
|
|
@@ -789,8 +706,11 @@ class Zigbee extends utils.Adapter {
|
|
|
789
706
|
}
|
|
790
707
|
}
|
|
791
708
|
}
|
|
792
|
-
if (has_elevated_debug)
|
|
793
|
-
|
|
709
|
+
if (has_elevated_debug) {
|
|
710
|
+
const message = `ELEVATED O07: Device query for '${entity.device.ieeeAddr}/${entity.device.endpoints[0].ID}' complete`;
|
|
711
|
+
//this.log.warn(`ELEVATED O07: ${message}`);
|
|
712
|
+
this.emit('device_debug', { ID:debugID, data: { flag: 'qe' , IO:false }, message:message});
|
|
713
|
+
}
|
|
794
714
|
else
|
|
795
715
|
this.log.info(`Device query for '${entity.device.ieeeAddr}' done`);
|
|
796
716
|
const idToRemove = deviceId;
|
|
@@ -814,38 +734,51 @@ class Zigbee extends utils.Adapter {
|
|
|
814
734
|
this.log.debug(`Type of toZigbee is '${typeof c}', Contains key ${(c.hasOwnProperty('key')?JSON.stringify(c.key):'false ')}`)
|
|
815
735
|
if (!c.hasOwnProperty('key'))
|
|
816
736
|
{
|
|
817
|
-
if (
|
|
737
|
+
if (converter === undefined)
|
|
818
738
|
{
|
|
819
739
|
converter = c;
|
|
820
|
-
if (has_elevated_debug)
|
|
821
|
-
|
|
740
|
+
if (has_elevated_debug) {
|
|
741
|
+
const message = `Setting converter to keyless converter for ${deviceId} of type ${model}`;
|
|
742
|
+
this.emit('device_debug', { ID:debugID, data: { flag: `s4.${msg_counter}` , IO:false }, message:message});
|
|
743
|
+
}
|
|
822
744
|
else
|
|
823
|
-
this.log.debug(`Setting converter to keyless converter for ${deviceId} of type ${model}`)
|
|
745
|
+
this.log.debug(`Setting converter to keyless converter for ${deviceId} of type ${model}`);
|
|
824
746
|
msg_counter++;
|
|
825
747
|
}
|
|
826
748
|
else
|
|
827
749
|
{
|
|
828
750
|
if (has_elevated_debug)
|
|
829
|
-
|
|
751
|
+
{
|
|
752
|
+
const message = `ignoring keyless converter for ${deviceId} of type ${model}`;
|
|
753
|
+
this.emit('device_debug', { ID:debugID, data: { flag: `i4.${msg_counter}` , IO:false} , message:message});
|
|
754
|
+
}
|
|
830
755
|
else
|
|
831
|
-
this.log.debug(`ignoring keyless converter for ${deviceId} of type ${model}`)
|
|
756
|
+
this.log.debug(`ignoring keyless converter for ${deviceId} of type ${model}`);
|
|
832
757
|
msg_counter++;
|
|
833
758
|
}
|
|
834
759
|
continue;
|
|
835
760
|
}
|
|
836
761
|
if (c.key.includes(stateDesc.prop) || c.key.includes(stateDesc.setattr) || c.key.includes(stateDesc.id))
|
|
837
762
|
{
|
|
838
|
-
|
|
839
|
-
|
|
763
|
+
const message = `${(converter===undefined?'Setting':'Overriding')}' converter to converter with key(s)'${JSON.stringify(c.key)}}`;
|
|
764
|
+
if (has_elevated_debug) {
|
|
765
|
+
this.emit('device_debugug', { ID:debugID, data: { flag: `${converter===undefined ? 's' : 'o'}4.${msg_counter}` , IO:false }, message:message});
|
|
766
|
+
|
|
767
|
+
}
|
|
840
768
|
else
|
|
841
|
-
this.log.debug(
|
|
769
|
+
this.log.debug(message);
|
|
842
770
|
converter = c;
|
|
843
771
|
msg_counter++;
|
|
844
772
|
}
|
|
845
773
|
}
|
|
846
774
|
if (converter === undefined) {
|
|
847
|
-
|
|
848
|
-
|
|
775
|
+
const message = `No converter available for '${model}' with key '${stateDesc.id}' `;
|
|
776
|
+
if (has_elevated_debug) {
|
|
777
|
+
this.emit('device_debug', { ID:debugID, data: { error: 'NOCONV',states:[{id:stateDesc.id, value:value, payload:'no converter'}] , IO:false }, message:message});
|
|
778
|
+
}
|
|
779
|
+
else {
|
|
780
|
+
this.log.warn(message);
|
|
781
|
+
}
|
|
849
782
|
return;
|
|
850
783
|
}
|
|
851
784
|
|
|
@@ -864,10 +797,12 @@ class Zigbee extends utils.Adapter {
|
|
|
864
797
|
|
|
865
798
|
const epName = stateDesc.epname !== undefined ? stateDesc.epname : (stateDesc.prop || stateDesc.id);
|
|
866
799
|
const key = stateDesc.setattr || stateDesc.prop || stateDesc.id;
|
|
867
|
-
|
|
868
|
-
|
|
800
|
+
const message = `convert ${key}, ${safeJsonStringify(preparedValue)}, ${safeJsonStringify(preparedOptions)} for device ${deviceId} with Endpoint ${epName}`;
|
|
801
|
+
if (has_elevated_debug) {
|
|
802
|
+
this.emit('device_debug', { ID:debugID, data: { flag: '04', payload: {key:key, ep: stateDesc.epname, value:preparedValue, options:preparedOptions}, IO:false }, message:message});
|
|
803
|
+
}
|
|
869
804
|
else
|
|
870
|
-
this.log.debug(
|
|
805
|
+
this.log.debug(message);
|
|
871
806
|
|
|
872
807
|
let target;
|
|
873
808
|
if (model === 'group') {
|
|
@@ -897,6 +832,10 @@ class Zigbee extends utils.Adapter {
|
|
|
897
832
|
meta.message.state = preparedValue;
|
|
898
833
|
}
|
|
899
834
|
}
|
|
835
|
+
if (has_elevated_debug) {
|
|
836
|
+
//this.log.warn('epname is ' + epName + ' or ' + stateDesc.epname);
|
|
837
|
+
this.emit('device_debug', { ID:debugID, data: { states:[{id:stateDesc.id, value:value, payload:preparedValue, ep:stateDesc.epname}] , IO:false }});
|
|
838
|
+
}
|
|
900
839
|
|
|
901
840
|
if (preparedOptions !== undefined) {
|
|
902
841
|
if (preparedOptions.hasOwnProperty('state')) {
|
|
@@ -906,33 +845,52 @@ class Zigbee extends utils.Adapter {
|
|
|
906
845
|
|
|
907
846
|
try {
|
|
908
847
|
const result = await converter.convertSet(target, key, preparedValue, meta);
|
|
909
|
-
|
|
910
|
-
|
|
848
|
+
const message = `convert result ${safeJsonStringify(result)} for device ${deviceId}`;
|
|
849
|
+
if (has_elevated_debug) {
|
|
850
|
+
this.emit('device_debug', { ID:debugID, data: { flag: 'SUCCESS' , IO:false }, message:message});
|
|
851
|
+
}
|
|
911
852
|
else
|
|
912
|
-
this.log.debug(
|
|
853
|
+
this.log.debug(message);
|
|
913
854
|
if (result !== undefined) {
|
|
914
|
-
if (stateModel && !isGroup) {
|
|
855
|
+
if (stateModel && !isGroup && !stateDesc.noack) {
|
|
915
856
|
this.acknowledgeState(deviceId, model, stateDesc, value);
|
|
916
857
|
}
|
|
917
858
|
// process sync state list
|
|
918
859
|
this.processSyncStatesList(deviceId, model, syncStateList);
|
|
919
860
|
}
|
|
920
|
-
else
|
|
921
|
-
if (has_elevated_debug)
|
|
922
|
-
|
|
923
|
-
|
|
861
|
+
else {
|
|
862
|
+
if (has_elevated_debug) {
|
|
863
|
+
const message = `Convert does not return a result result for ${key} with ${safeJsonStringify(preparedValue)} on device ${deviceId}.`;
|
|
864
|
+
this.emit('device_debug', { ID:debugID, data: { flag: '06' , IO:false }, message:message});
|
|
865
|
+
}
|
|
866
|
+
}
|
|
924
867
|
} catch (error) {
|
|
925
|
-
if (has_elevated_debug)
|
|
926
|
-
|
|
868
|
+
if (has_elevated_debug) {
|
|
869
|
+
const message = `caught error ${safeJsonStringify(error)} when setting value for device ${deviceId}.`;
|
|
870
|
+
this.emit('device_debug', { ID:debugID, data: { error: 'EXSET' , IO:false },message:message});
|
|
871
|
+
}
|
|
927
872
|
this.filterError(`Error ${error.code} on send command to ${deviceId}.` +
|
|
928
873
|
` Error: ${error.stack}`, `Send command to ${deviceId} failed with`, error);
|
|
929
874
|
}
|
|
930
875
|
});
|
|
931
876
|
} catch (err) {
|
|
932
|
-
|
|
877
|
+
const message = `No entity for ${deviceId} : ${err && err.message ? err.message : 'no error message'}`;
|
|
878
|
+
this.emit('device_debug', { ID:debugID, data: { error: 'EXPUB' , IO:false }, message:message});
|
|
933
879
|
}
|
|
934
880
|
}
|
|
935
881
|
|
|
882
|
+
|
|
883
|
+
extractEP(key, endpoints) {
|
|
884
|
+
try {
|
|
885
|
+
if (endpoints) for (const ep of Object.keys(endpoints)) {
|
|
886
|
+
if (key.endsWith('_'+ep)) return { setattr: key.replace('_'+ep, ''), epname:ep }
|
|
887
|
+
}
|
|
888
|
+
}
|
|
889
|
+
catch {
|
|
890
|
+
return {};
|
|
891
|
+
}
|
|
892
|
+
return {};
|
|
893
|
+
}
|
|
936
894
|
// This function is introduced to explicitly allow user level scripts to send Commands
|
|
937
895
|
// directly to the zigbee device. It utilizes the zigbee-herdsman-converters to generate
|
|
938
896
|
// the exact zigbee message to be sent and can be used to set device options which are
|
|
@@ -946,7 +904,6 @@ class Zigbee extends utils.Adapter {
|
|
|
946
904
|
// endpoint: optional: the endpoint to send the data to, if supported.
|
|
947
905
|
//
|
|
948
906
|
async sendPayload(payload) {
|
|
949
|
-
this.log.debug(`publishToDevice called with ${safeJsonStringify(payload)}`);
|
|
950
907
|
let payloadObj = {};
|
|
951
908
|
if (typeof payload === 'string') {
|
|
952
909
|
try {
|
|
@@ -961,7 +918,7 @@ class Zigbee extends utils.Adapter {
|
|
|
961
918
|
}
|
|
962
919
|
} else if (typeof payload === 'object') {
|
|
963
920
|
payloadObj = payload;
|
|
964
|
-
}
|
|
921
|
+
} else return { success: false, error: 'illegal type of payload: ' + typeof payload};
|
|
965
922
|
|
|
966
923
|
if (payloadObj.hasOwnProperty('device') && payloadObj.hasOwnProperty('payload')) {
|
|
967
924
|
try {
|
|
@@ -986,16 +943,24 @@ class Zigbee extends utils.Adapter {
|
|
|
986
943
|
this.sendError(`Illegal payload type for ${safeJsonStringify(payloadObj.device)}`);
|
|
987
944
|
return {success: false, error: `Illegal payload type for ${safeJsonStringify(payloadObj.device)}`};
|
|
988
945
|
}
|
|
946
|
+
const endpoints = mappedModel && mappedModel.endpoint ? mappedModel.endpoint(entity.device) : null;
|
|
989
947
|
for (const key in payloadObj.payload) {
|
|
990
948
|
if (payloadObj.payload[key] != undefined) {
|
|
991
949
|
const datatype = typeof payloadObj.payload[key];
|
|
950
|
+
const epobj = this.extractEP(key, endpoints);
|
|
951
|
+
if (payloadObj.endpoint) {
|
|
952
|
+
epobj.epname = payloadObj.endpoint;
|
|
953
|
+
delete epobj.setattr;
|
|
954
|
+
}
|
|
992
955
|
stateList.push({
|
|
993
956
|
stateDesc: {
|
|
994
957
|
id: key,
|
|
995
958
|
prop: key,
|
|
996
959
|
role: 'state',
|
|
997
960
|
type: datatype,
|
|
998
|
-
|
|
961
|
+
noack:true,
|
|
962
|
+
epname: epobj.epname,
|
|
963
|
+
setattr: epobj.setattr,
|
|
999
964
|
},
|
|
1000
965
|
value: payloadObj.payload[key],
|
|
1001
966
|
index: 0,
|
|
@@ -1004,8 +969,7 @@ class Zigbee extends utils.Adapter {
|
|
|
1004
969
|
}
|
|
1005
970
|
}
|
|
1006
971
|
try {
|
|
1007
|
-
this.
|
|
1008
|
-
await this.publishFromState(`0x${payload.device}`, '', undefined, stateList, payload.options);
|
|
972
|
+
await this.publishFromState(`0x${payload.device}`, payload.model, payload.stateModel, stateList, payload.options, Date.now());
|
|
1009
973
|
return {success: true};
|
|
1010
974
|
} catch (error) {
|
|
1011
975
|
this.log.error(`Error ${error.code} on send command to ${payload.device}.` + ` Error: ${error.stack} ` + `Send command to ${payload.device} failed with ` + error);
|
|
@@ -1023,6 +987,7 @@ class Zigbee extends utils.Adapter {
|
|
|
1023
987
|
|
|
1024
988
|
newDevice(entity) {
|
|
1025
989
|
this.log.debug(`New device event: ${safeJsonStringify(entity)}`);
|
|
990
|
+
this.stController.AddModelFromHerdsman(entity.device, entity.mapped.model)
|
|
1026
991
|
const dev = entity.device;
|
|
1027
992
|
if (dev) {
|
|
1028
993
|
this.getObject(dev.ieeeAddr.substr(2), (err, obj) => {
|