iobroker.zigbee 1.5.5 → 1.6.6
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/.github/FUNDING.yml +1 -1
- package/.github/workflows/test-and-release.yml +10 -10
- package/LICENSE +1 -1
- package/README.md +40 -8
- package/admin/adapter-settings.js +39 -3
- package/admin/admin.js +23 -33
- package/admin/img/tuya_rb280.png +0 -0
- package/admin/index_m.html +84 -37
- package/admin/tab_m.html +95 -35
- package/admin/words.js +24 -24
- package/docs/tutorial/adm5_1.PNG +0 -0
- package/docs/tutorial/adm5_2.PNG +0 -0
- package/docs/tutorial/zigbee.png +0 -0
- package/io-package.json +27 -3
- package/lib/commands.js +2 -1
- package/lib/devices.js +10 -14
- package/lib/exposes.js +263 -104
- package/lib/states.js +86 -27
- package/lib/statescontroller.js +10 -2
- package/lib/utils.js +19 -0
- package/lib/zbBaseExtension.js +4 -0
- package/lib/zbDelayedAction.js +5 -0
- package/lib/zbDeviceAvailability.js +2 -0
- package/lib/zbDeviceConfigure.js +8 -5
- package/lib/zigbeecontroller.js +79 -31
- package/main.js +118 -43
- package/package.json +7 -6
package/lib/zigbeecontroller.js
CHANGED
|
@@ -37,8 +37,9 @@ class ZigbeeController extends EventEmitter {
|
|
|
37
37
|
join - join countdown (counter)
|
|
38
38
|
ready - connection successfull ()
|
|
39
39
|
*/
|
|
40
|
-
constructor() {
|
|
40
|
+
constructor(adapter) {
|
|
41
41
|
super();
|
|
42
|
+
this.adapter = adapter;
|
|
42
43
|
this._permitJoinTime = 0;
|
|
43
44
|
this.herdsman_started = false;
|
|
44
45
|
this.extensions = [
|
|
@@ -65,6 +66,9 @@ class ZigbeeController extends EventEmitter {
|
|
|
65
66
|
path: options.sp.port,
|
|
66
67
|
adapter: options.sp.adapter,
|
|
67
68
|
},
|
|
69
|
+
adapter: {
|
|
70
|
+
forceStartWithInconsistentAdapterConfiguration: options.startWithInconsistent
|
|
71
|
+
},
|
|
68
72
|
};
|
|
69
73
|
// https://github.com/ioBroker/ioBroker.zigbee/issues/668
|
|
70
74
|
if (!options.extPanIdFix) {
|
|
@@ -80,28 +84,46 @@ class ZigbeeController extends EventEmitter {
|
|
|
80
84
|
this.disableLed = options.disableLed;
|
|
81
85
|
|
|
82
86
|
this.debug(`Using zigbee-herdsman with settings: ${JSON.stringify(herdsmanSettings)}`);
|
|
83
|
-
this.herdsman = new ZigbeeHerdsman.Controller(herdsmanSettings);
|
|
87
|
+
this.herdsman = new ZigbeeHerdsman.Controller(herdsmanSettings, this.adapter.log);
|
|
84
88
|
this.callExtensionMethod('setOptions', [{ disableActivePing: options.disablePing, disableForcedPing: false, pingTimeout:300, pingCount:3 }] );
|
|
85
89
|
}
|
|
86
90
|
|
|
87
91
|
// Start controller
|
|
88
92
|
async start() {
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
93
|
+
try {
|
|
94
|
+
//this.debug(`Using zigbee-herdsman with settings2: ${JSON.stringify(this)}`);
|
|
95
|
+
this.debug(`Starting zigbee-herdsman...`);
|
|
96
|
+
await this.herdsman.start();
|
|
97
|
+
|
|
98
|
+
this.herdsman.on('adapterDisconnected', this.handleDisconnected.bind(this));
|
|
99
|
+
this.herdsman.on('deviceAnnounce', this.handleDeviceAnnounce.bind(this));
|
|
100
|
+
this.herdsman.on('deviceInterview', this.handleDeviceInterview.bind(this));
|
|
101
|
+
this.herdsman.on('deviceJoined', this.handleDeviceJoined.bind(this));
|
|
102
|
+
this.herdsman.on('deviceLeave', this.handleDeviceLeave.bind(this));
|
|
103
|
+
this.herdsman.on('message', this.handleMessage.bind(this));
|
|
104
|
+
|
|
105
|
+
this.debug('zigbee-herdsman started');
|
|
106
|
+
this.herdsman_started = true;
|
|
107
|
+
this.info(`Coordinator firmware version: ${JSON.stringify(await this.herdsman.getCoordinatorVersion())}`);
|
|
108
|
+
|
|
109
|
+
// debug info from herdsman getNetworkParameters
|
|
110
|
+
const debNetworkParam = JSON.parse(JSON.stringify(await this.herdsman.getNetworkParameters()));
|
|
111
|
+
const extendedPanIDDebug = (typeof debNetworkParam.extendedPanID == 'string') ? debNetworkParam.extendedPanID.replace('0x','') : debNetworkParam.extendedPanID;
|
|
112
|
+
|
|
113
|
+
let extPanIDDebug = '';
|
|
114
|
+
for (let i = extendedPanIDDebug.length - 1; i >= 0; i--) {
|
|
115
|
+
extPanIDDebug += extendedPanIDDebug[i-1];
|
|
116
|
+
extPanIDDebug += extendedPanIDDebug[i];
|
|
117
|
+
i--;
|
|
118
|
+
}
|
|
104
119
|
|
|
120
|
+
this.debug(`Zigbee network parameters: panID=${debNetworkParam.panID} channel=${debNetworkParam.channel} extendedPanID=${extPanIDDebug}`);
|
|
121
|
+
|
|
122
|
+
} catch (e) {
|
|
123
|
+
this.sendError(e);
|
|
124
|
+
this.error('Starting zigbee-herdsman problem : ' + JSON.stringify(e.message));
|
|
125
|
+
throw 'Error herdsman start';
|
|
126
|
+
}
|
|
105
127
|
// Check if we have to turn off the led
|
|
106
128
|
try {
|
|
107
129
|
if (this.disableLed) {
|
|
@@ -112,6 +134,7 @@ class ZigbeeController extends EventEmitter {
|
|
|
112
134
|
}
|
|
113
135
|
} catch (e) {
|
|
114
136
|
this.info('Unable to disable LED, unsupported function.');
|
|
137
|
+
this.sendError(e);
|
|
115
138
|
}
|
|
116
139
|
|
|
117
140
|
// only for CC1352P and CC26X2R1 transmit power
|
|
@@ -135,6 +158,7 @@ class ZigbeeController extends EventEmitter {
|
|
|
135
158
|
try {
|
|
136
159
|
await this.herdsman.setTransmitPower(this.transmitPower);
|
|
137
160
|
} catch (e) {
|
|
161
|
+
this.sendError(e);
|
|
138
162
|
this.info('Unable to set transmit power, unsupported function.');
|
|
139
163
|
}
|
|
140
164
|
|
|
@@ -188,6 +212,10 @@ class ZigbeeController extends EventEmitter {
|
|
|
188
212
|
this.emit('event', type, dev, message, data);
|
|
189
213
|
}
|
|
190
214
|
|
|
215
|
+
sendError(error, message) {
|
|
216
|
+
this.adapter.sendError(error, message);
|
|
217
|
+
}
|
|
218
|
+
|
|
191
219
|
callExtensionMethod(method, parameters) {
|
|
192
220
|
for (const extension of this.extensions) {
|
|
193
221
|
if (extension[method]) {
|
|
@@ -198,8 +226,8 @@ class ZigbeeController extends EventEmitter {
|
|
|
198
226
|
extension[method]();
|
|
199
227
|
}
|
|
200
228
|
} catch (error) {
|
|
229
|
+
this.sendError(error);
|
|
201
230
|
this.error(`Failed to call '${extension.constructor.name}' '${method}' (${error.stack})`);
|
|
202
|
-
// throw error;
|
|
203
231
|
}
|
|
204
232
|
}
|
|
205
233
|
}
|
|
@@ -221,8 +249,8 @@ class ZigbeeController extends EventEmitter {
|
|
|
221
249
|
async getGroups() {
|
|
222
250
|
try {
|
|
223
251
|
return this.herdsman.getGroups();
|
|
224
|
-
}
|
|
225
|
-
|
|
252
|
+
} catch (error) {
|
|
253
|
+
this.sendError(error);
|
|
226
254
|
return undefined;
|
|
227
255
|
}
|
|
228
256
|
}
|
|
@@ -232,6 +260,7 @@ class ZigbeeController extends EventEmitter {
|
|
|
232
260
|
try {
|
|
233
261
|
if (group) group.removeFromDatabase();
|
|
234
262
|
} catch (error) {
|
|
263
|
+
this.sendError(error);
|
|
235
264
|
this.error('error in removeGroupById: ' + error);
|
|
236
265
|
}
|
|
237
266
|
}
|
|
@@ -239,8 +268,8 @@ class ZigbeeController extends EventEmitter {
|
|
|
239
268
|
async getGroupByID(id) {
|
|
240
269
|
try {
|
|
241
270
|
return this.herdsman.getGroupByID(id);
|
|
242
|
-
}
|
|
243
|
-
|
|
271
|
+
} catch (error) {
|
|
272
|
+
this.sendError(error);
|
|
244
273
|
return undefined;
|
|
245
274
|
}
|
|
246
275
|
}
|
|
@@ -263,6 +292,7 @@ class ZigbeeController extends EventEmitter {
|
|
|
263
292
|
}
|
|
264
293
|
|
|
265
294
|
} catch (error) {
|
|
295
|
+
this.sendError(error);
|
|
266
296
|
if (error) this.error('getGroupMembersFromController: error is ' + JSON.stringify(error) + ' ' + JSON.stringify(new Error().stack));
|
|
267
297
|
else this.error('unidentifed error in getGroupMembersFromController');
|
|
268
298
|
}
|
|
@@ -344,7 +374,6 @@ class ZigbeeController extends EventEmitter {
|
|
|
344
374
|
}
|
|
345
375
|
}
|
|
346
376
|
|
|
347
|
-
|
|
348
377
|
async incMsgHandler(message){
|
|
349
378
|
this.debug('incoming msg', message);
|
|
350
379
|
const device = await this.herdsman.getDeviceByIeeeAddr(message.srcaddr);
|
|
@@ -371,6 +400,7 @@ class ZigbeeController extends EventEmitter {
|
|
|
371
400
|
await this.permitJoin(0);
|
|
372
401
|
await this.herdsman.stop();
|
|
373
402
|
} catch (error) {
|
|
403
|
+
this.sendError(error);
|
|
374
404
|
if (this.herdsman_started)
|
|
375
405
|
this.error(`Failed to stop zigbee (${error.stack})`);
|
|
376
406
|
else {
|
|
@@ -379,6 +409,15 @@ class ZigbeeController extends EventEmitter {
|
|
|
379
409
|
}
|
|
380
410
|
}
|
|
381
411
|
|
|
412
|
+
async handleDisconnected() {
|
|
413
|
+
this.herdsman_started = false;
|
|
414
|
+
this.emit('disconnect');
|
|
415
|
+
}
|
|
416
|
+
|
|
417
|
+
connected() {
|
|
418
|
+
return this.herdsman_started;
|
|
419
|
+
}
|
|
420
|
+
|
|
382
421
|
// Permit join
|
|
383
422
|
async permitJoin(permitTime, devid, failure) {
|
|
384
423
|
let permitDev;
|
|
@@ -416,9 +455,8 @@ class ZigbeeController extends EventEmitter {
|
|
|
416
455
|
await this.herdsman.permitJoin(false, permitDev);
|
|
417
456
|
}
|
|
418
457
|
}
|
|
419
|
-
}
|
|
420
|
-
|
|
421
|
-
{
|
|
458
|
+
} catch (e) {
|
|
459
|
+
this.sendError(e);
|
|
422
460
|
this.error(`Failed to open the network: ${e.stack}`);
|
|
423
461
|
}
|
|
424
462
|
}
|
|
@@ -431,6 +469,7 @@ class ZigbeeController extends EventEmitter {
|
|
|
431
469
|
try {
|
|
432
470
|
await this.herdsman.adapter.removeDevice(device.networkAddress, device.ieeeAddr);
|
|
433
471
|
} catch (error) {
|
|
472
|
+
this.sendError(error);
|
|
434
473
|
if (error)
|
|
435
474
|
this.debug(`Failed to remove device ${error.stack}`);
|
|
436
475
|
// skip error if force
|
|
@@ -443,6 +482,7 @@ class ZigbeeController extends EventEmitter {
|
|
|
443
482
|
try {
|
|
444
483
|
await device.removeFromDatabase();
|
|
445
484
|
} catch (error) {
|
|
485
|
+
this.sendError(error);
|
|
446
486
|
// skip error
|
|
447
487
|
if (error)
|
|
448
488
|
this.debug(`Failed to remove from DB ${error.stack}`);
|
|
@@ -455,6 +495,7 @@ class ZigbeeController extends EventEmitter {
|
|
|
455
495
|
);
|
|
456
496
|
}
|
|
457
497
|
} catch (error) {
|
|
498
|
+
this.sendError(error);
|
|
458
499
|
this.error(`Failed to remove ${error.stack}`);
|
|
459
500
|
if (callback) callback(`Failed to remove ${error.stack}`);
|
|
460
501
|
}
|
|
@@ -474,6 +515,7 @@ class ZigbeeController extends EventEmitter {
|
|
|
474
515
|
[message, entity],
|
|
475
516
|
);
|
|
476
517
|
} catch (error) {
|
|
518
|
+
this.sendError(error);
|
|
477
519
|
this.error(`Failed to handleDeviceLeave ${error.stack}`);
|
|
478
520
|
}
|
|
479
521
|
}
|
|
@@ -577,6 +619,7 @@ class ZigbeeController extends EventEmitter {
|
|
|
577
619
|
try {
|
|
578
620
|
result = await device.lqi();
|
|
579
621
|
} catch (error) {
|
|
622
|
+
this.sendError(error);
|
|
580
623
|
if (error)
|
|
581
624
|
this.debug(`Failed to execute LQI for '${resolved.name}'. ${safeJsonStringify(error.stack)}`);
|
|
582
625
|
|
|
@@ -612,6 +655,7 @@ class ZigbeeController extends EventEmitter {
|
|
|
612
655
|
try {
|
|
613
656
|
result = await device.routingTable();
|
|
614
657
|
} catch (error) {
|
|
658
|
+
this.sendError(error);
|
|
615
659
|
if (error) {
|
|
616
660
|
this.debug(`Failed to execute routing table for '${resolved.name}'. ${safeJsonStringify(error.stack)}`);
|
|
617
661
|
}
|
|
@@ -637,6 +681,7 @@ class ZigbeeController extends EventEmitter {
|
|
|
637
681
|
|
|
638
682
|
if (callback) callback({lqis: lqis, routing: routing});
|
|
639
683
|
} catch (error) {
|
|
684
|
+
this.sendError(error);
|
|
640
685
|
this.debug(`Failed to get map: ${safeJsonStringify(error.stack)}`);
|
|
641
686
|
}
|
|
642
687
|
}
|
|
@@ -666,7 +711,7 @@ class ZigbeeController extends EventEmitter {
|
|
|
666
711
|
|
|
667
712
|
if (type === 'foundation') {
|
|
668
713
|
cfg.disableDefaultResponse = true;
|
|
669
|
-
if (cmd === 'read') {
|
|
714
|
+
if (cmd === 'read' && !Array.isArray(zclData)) {
|
|
670
715
|
// needs to be iterateable (string[] | number [])
|
|
671
716
|
zclData[Symbol.iterator] = function* () {
|
|
672
717
|
let k;
|
|
@@ -696,8 +741,8 @@ class ZigbeeController extends EventEmitter {
|
|
|
696
741
|
this.debug(`entity: ${safeJsonStringify(entity)}`);
|
|
697
742
|
this.debug(`group: ${safeJsonStringify(group)}`);
|
|
698
743
|
await entity.endpoint.addToGroup(group.mapped);
|
|
699
|
-
}
|
|
700
|
-
|
|
744
|
+
} catch (error) {
|
|
745
|
+
this.sendError(error);
|
|
701
746
|
this.error(`Exception when trying to Add ${devId} to group ${groupId}`, error);
|
|
702
747
|
return { error:`Failed to add ${devId} to group ${groupId}: ${JSON.stringify(error)}` };
|
|
703
748
|
}
|
|
@@ -709,8 +754,8 @@ class ZigbeeController extends EventEmitter {
|
|
|
709
754
|
const entity = await this.resolveEntity(devId);
|
|
710
755
|
this.debug(`entity: ${safeJsonStringify(entity)}`);
|
|
711
756
|
await entity.endpoint.removeFromAllGroups();
|
|
712
|
-
}
|
|
713
|
-
|
|
757
|
+
} catch (error) {
|
|
758
|
+
this.sendError(error);
|
|
714
759
|
this.error(`Exception when trying remove ${devId} from all groups`, error);
|
|
715
760
|
return { error: `Failed to remove dev ${devId} from all groups: ${error}`};
|
|
716
761
|
}
|
|
@@ -724,6 +769,7 @@ class ZigbeeController extends EventEmitter {
|
|
|
724
769
|
this.debug(`Binding ${log}`);
|
|
725
770
|
ep.bind(cluster, target, (error) => {
|
|
726
771
|
if (error) {
|
|
772
|
+
this.sendError(error);
|
|
727
773
|
this.error(`Failed to bind ${log} - (${error})`);
|
|
728
774
|
} else {
|
|
729
775
|
this.debug(`Successfully bound ${log}`);
|
|
@@ -754,6 +800,7 @@ class ZigbeeController extends EventEmitter {
|
|
|
754
800
|
this.herdsman.reset(mode);
|
|
755
801
|
if (callback) callback();
|
|
756
802
|
} catch (error) {
|
|
803
|
+
this.sendError(error);
|
|
757
804
|
this.error(`Failed to reset ${error.stack}`);
|
|
758
805
|
if (callback) callback(error);
|
|
759
806
|
}
|
|
@@ -764,6 +811,7 @@ class ZigbeeController extends EventEmitter {
|
|
|
764
811
|
await this.herdsman.touchlinkFactoryResetFirst();
|
|
765
812
|
this.permitJoin(permitTime);
|
|
766
813
|
} catch (error) {
|
|
814
|
+
this.sendError(error);
|
|
767
815
|
this.error(`Failed to touchlinkReset ${error.stack}`);
|
|
768
816
|
}
|
|
769
817
|
}
|
package/main.js
CHANGED
|
@@ -15,7 +15,7 @@ const originalLogMethod = debug.log;
|
|
|
15
15
|
|
|
16
16
|
const safeJsonStringify = require('./lib/json');
|
|
17
17
|
const fs = require('fs');
|
|
18
|
-
const
|
|
18
|
+
const path = require('path');
|
|
19
19
|
const utils = require('@iobroker/adapter-core'); // Get common adapter utils
|
|
20
20
|
const SerialListPlugin = require('./lib/seriallist');
|
|
21
21
|
const CommandsPlugin = require('./lib/commands');
|
|
@@ -30,6 +30,7 @@ const StatesController = require('./lib/statescontroller');
|
|
|
30
30
|
const ExcludePlugin = require('./lib/exclude');
|
|
31
31
|
const zigbeeHerdsmanConverters = require('zigbee-herdsman-converters');
|
|
32
32
|
const vm = require('vm');
|
|
33
|
+
const util = require('util');
|
|
33
34
|
|
|
34
35
|
const createByteArray = function (hexString) {
|
|
35
36
|
const bytes = [];
|
|
@@ -105,18 +106,46 @@ class Zigbee extends utils.Adapter {
|
|
|
105
106
|
}
|
|
106
107
|
}
|
|
107
108
|
|
|
109
|
+
sendError(error, message) {
|
|
110
|
+
if (this.supportsFeature && this.supportsFeature('PLUGINS')) {
|
|
111
|
+
const sentryInstance = this.getPluginInstance('sentry');
|
|
112
|
+
if (sentryInstance) {
|
|
113
|
+
const Sentry = sentryInstance.getSentryObject();
|
|
114
|
+
if (Sentry) {
|
|
115
|
+
if (message) {
|
|
116
|
+
Sentry.configureScope(scope => {
|
|
117
|
+
scope.addBreadcrumb({
|
|
118
|
+
type: "error", // predefined types
|
|
119
|
+
category: "error message",
|
|
120
|
+
level: Sentry.Severity.Error,
|
|
121
|
+
message: message
|
|
122
|
+
});
|
|
123
|
+
});
|
|
124
|
+
}
|
|
125
|
+
if (typeof error == 'string') {
|
|
126
|
+
Sentry.captureException(new Error(error));
|
|
127
|
+
} else {
|
|
128
|
+
Sentry.captureException(error);
|
|
129
|
+
}
|
|
130
|
+
}
|
|
131
|
+
}
|
|
132
|
+
}
|
|
133
|
+
}
|
|
134
|
+
|
|
108
135
|
filterError(errormessage, message, error) {
|
|
109
136
|
if (error.code === undefined)
|
|
110
137
|
{
|
|
111
138
|
let em = error.stack.match(/failed \((.+?)\) at/);
|
|
112
139
|
if (!em) em = error.stack.match(/failed \((.+?)\)/);
|
|
113
140
|
this.log.error(`${message} no error code (${(em ? em[1]:'undefined')})`);
|
|
141
|
+
this.sendError(error, `${message} no error code`);
|
|
114
142
|
this.log.debug(`Stack trace for ${em}: ${error.stack}`);
|
|
115
143
|
return;
|
|
116
144
|
}
|
|
117
145
|
const ecode = errorCodes[error.code];
|
|
118
146
|
if (ecode === undefined) {
|
|
119
147
|
this.log.error(errormessage);
|
|
148
|
+
this.sendError(error, errormessage);
|
|
120
149
|
return;
|
|
121
150
|
}
|
|
122
151
|
switch (ecode.severity) {
|
|
@@ -126,14 +155,19 @@ class Zigbee extends utils.Adapter {
|
|
|
126
155
|
break;
|
|
127
156
|
case E_WARN: this.log.warn(`${message}: Code ${error.code} (${ecode.message})`);
|
|
128
157
|
break;
|
|
129
|
-
case E_ERROR:
|
|
158
|
+
case E_ERROR:
|
|
159
|
+
this.log.error(`${message}: Code ${error.code} (${ecode.message})`);
|
|
160
|
+
this.sendError(error, `${message}: Code ${error.code} (${ecode.message})`);
|
|
130
161
|
break;
|
|
131
|
-
default:
|
|
162
|
+
default:
|
|
163
|
+
this.log.error(`${message}: Code ${error.code} (malformed error)`);
|
|
164
|
+
this.sendError(error, `${message}: Code ${error.code} (malformed error)`);
|
|
132
165
|
}
|
|
133
166
|
}
|
|
134
167
|
|
|
135
|
-
debugLog (data) {
|
|
136
|
-
|
|
168
|
+
debugLog (data, ...args) {
|
|
169
|
+
const message = (args) ? util.format(data, ...args) : data;
|
|
170
|
+
this.log.debug(message.slice(message.indexOf('zigbee-herdsman')));
|
|
137
171
|
}
|
|
138
172
|
|
|
139
173
|
async onReady() {
|
|
@@ -153,7 +187,7 @@ class Zigbee extends utils.Adapter {
|
|
|
153
187
|
// set connection false before connect to zigbee
|
|
154
188
|
this.setState('info.connection', false, true);
|
|
155
189
|
const zigbeeOptions = this.getZigbeeOptions();
|
|
156
|
-
this.zbController = new ZigbeeController();
|
|
190
|
+
this.zbController = new ZigbeeController(this);
|
|
157
191
|
this.zbController.on('log', this.onLog.bind(this));
|
|
158
192
|
this.zbController.on('ready', this.onZigbeeAdapterReady.bind(this));
|
|
159
193
|
this.zbController.on('disconnect', this.onZigbeeAdapterDisconnected.bind(this));
|
|
@@ -237,6 +271,7 @@ class Zigbee extends utils.Adapter {
|
|
|
237
271
|
} else {
|
|
238
272
|
this.log.error(error);
|
|
239
273
|
}
|
|
274
|
+
this.sendError(error, `Failed to start Zigbee`);
|
|
240
275
|
if (this.reconnectCounter > 0) {
|
|
241
276
|
this.tryToReconnect();
|
|
242
277
|
}
|
|
@@ -246,6 +281,7 @@ class Zigbee extends utils.Adapter {
|
|
|
246
281
|
async onZigbeeAdapterDisconnected() {
|
|
247
282
|
this.reconnectCounter = 5;
|
|
248
283
|
this.log.error('Adapter disconnected, stopping');
|
|
284
|
+
this.sendError('Adapter disconnected, stopping');
|
|
249
285
|
this.setState('info.connection', false, true);
|
|
250
286
|
await this.callPluginMethod('stop');
|
|
251
287
|
this.tryToReconnect();
|
|
@@ -281,25 +317,31 @@ class Zigbee extends utils.Adapter {
|
|
|
281
317
|
const adapterType = this.config.adapterType || 'zstack';
|
|
282
318
|
if (adapterType === 'zstack') {
|
|
283
319
|
if (configExtPanId != networkExtPanId) {
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
320
|
+
try {
|
|
321
|
+
// try to read from nvram
|
|
322
|
+
const result = await this.zbController.herdsman.adapter.znp.request(
|
|
323
|
+
1, // Subsystem.SYS
|
|
324
|
+
'osalNvRead',
|
|
325
|
+
{
|
|
326
|
+
id: 45, // EXTENDED_PAN_ID
|
|
327
|
+
len: 0x08,
|
|
328
|
+
offset: 0x00,
|
|
329
|
+
},
|
|
330
|
+
null, [
|
|
331
|
+
0, // ZnpCommandStatus.SUCCESS
|
|
332
|
+
2, // ZnpCommandStatus.INVALID_PARAM
|
|
333
|
+
]
|
|
334
|
+
);
|
|
335
|
+
const nwExtPanId = '0x'+result.payload.value.reverse().toString('hex');
|
|
336
|
+
this.log.debug(`Config value ${configExtPanId} : nw value ${nwExtPanId}`);
|
|
337
|
+
if (configExtPanId != nwExtPanId) {
|
|
338
|
+
networkExtPanId = nwExtPanId;
|
|
339
|
+
needChange = true;
|
|
340
|
+
}
|
|
341
|
+
} catch (e) {
|
|
342
|
+
this.log.error(`Unable to apply ExtPanID changes: ${e}`);
|
|
343
|
+
this.sendError(e, `Unable to apply ExtPanID changes`);
|
|
344
|
+
needChange = false;
|
|
303
345
|
}
|
|
304
346
|
} else {
|
|
305
347
|
needChange = true;
|
|
@@ -368,9 +410,6 @@ class Zigbee extends utils.Adapter {
|
|
|
368
410
|
|
|
369
411
|
async onZigbeeEvent(type, entity, message){
|
|
370
412
|
this.log.debug(`Type ${type} device ${safeJsonStringify(entity)} incoming event: ${safeJsonStringify(message)}`);
|
|
371
|
-
if (!entity.mapped) {
|
|
372
|
-
return;
|
|
373
|
-
}
|
|
374
413
|
const device = entity.device,
|
|
375
414
|
mappedModel = entity.mapped,
|
|
376
415
|
model = (entity.mapped) ? entity.mapped.model : entity.device.modelID,
|
|
@@ -385,6 +424,17 @@ class Zigbee extends utils.Adapter {
|
|
|
385
424
|
if (message.linkquality) {
|
|
386
425
|
this.publishToState(devId, model, {linkquality: message.linkquality});
|
|
387
426
|
}
|
|
427
|
+
// publish raw event to "from_zigbee"
|
|
428
|
+
// some cleanup
|
|
429
|
+
const msgForState = Object.assign({}, message);
|
|
430
|
+
delete msgForState['device'];
|
|
431
|
+
delete msgForState['endpoint'];
|
|
432
|
+
msgForState['endpoint_id'] = message.endpoint.ID;
|
|
433
|
+
this.publishToState(devId, model, {msg_from_zigbee: safeJsonStringify(msgForState)});
|
|
434
|
+
|
|
435
|
+
if (!entity.mapped) {
|
|
436
|
+
return;
|
|
437
|
+
}
|
|
388
438
|
let converters = mappedModel.fromZigbee.filter(c => c && c.cluster === cluster && (
|
|
389
439
|
(c.type instanceof Array) ? c.type.includes(type) : c.type === type));
|
|
390
440
|
if (!converters.length && type === 'readResponse') {
|
|
@@ -447,6 +497,10 @@ class Zigbee extends utils.Adapter {
|
|
|
447
497
|
const entity = await this.zbController.resolveEntity(deviceId);
|
|
448
498
|
this.log.debug(`entity: ${safeJsonStringify(entity)}`);
|
|
449
499
|
const mappedModel = entity.mapped;
|
|
500
|
+
if (!mappedModel) {
|
|
501
|
+
this.log.debug(`No mapped model for ${model}`);
|
|
502
|
+
return;
|
|
503
|
+
}
|
|
450
504
|
this.log.debug('Mapped Model: ' + JSON.stringify(mappedModel));
|
|
451
505
|
|
|
452
506
|
stateList.forEach(async(changedState) => {
|
|
@@ -494,6 +548,7 @@ class Zigbee extends utils.Adapter {
|
|
|
494
548
|
const converter = mappedModel.toZigbee.find((c) => c && (c.key.includes(stateDesc.prop) || c.key.includes(stateDesc.setattr) || c.key.includes(stateDesc.id)));
|
|
495
549
|
if (!converter) {
|
|
496
550
|
this.log.error(`No converter available for '${model}' with key '${stateDesc.id}' `);
|
|
551
|
+
this.sendError(`No converter available for '${model}' with key '${stateDesc.id}' `);
|
|
497
552
|
return;
|
|
498
553
|
}
|
|
499
554
|
|
|
@@ -511,7 +566,7 @@ class Zigbee extends utils.Adapter {
|
|
|
511
566
|
|
|
512
567
|
const epName = stateDesc.epname !== undefined ? stateDesc.epname : (stateDesc.prop || stateDesc.id);
|
|
513
568
|
const key = stateDesc.setattr || stateDesc.prop || stateDesc.id;
|
|
514
|
-
this.log.debug(`convert ${key}, ${preparedValue}, ${safeJsonStringify(preparedOptions)}`);
|
|
569
|
+
this.log.debug(`convert ${key}, ${safeJsonStringify(preparedValue)}, ${safeJsonStringify(preparedOptions)}`);
|
|
515
570
|
|
|
516
571
|
let target;
|
|
517
572
|
if (model === 'group') {
|
|
@@ -536,13 +591,15 @@ class Zigbee extends utils.Adapter {
|
|
|
536
591
|
try {
|
|
537
592
|
const result = await converter.convertSet(target, key, preparedValue, meta);
|
|
538
593
|
this.log.debug(`convert result ${safeJsonStringify(result)}`);
|
|
539
|
-
if (
|
|
540
|
-
|
|
541
|
-
|
|
542
|
-
|
|
543
|
-
|
|
544
|
-
|
|
545
|
-
|
|
594
|
+
if (result !== undefined) {
|
|
595
|
+
if (stateModel && !isGroup)
|
|
596
|
+
this.acknowledgeState(deviceId, model, stateDesc, value);
|
|
597
|
+
// process sync state list
|
|
598
|
+
this.processSyncStatesList(deviceId, model, syncStateList);
|
|
599
|
+
if (isGroup) {
|
|
600
|
+
await this.callPluginMethod('queryGroupMemberState', [deviceId, stateDesc]);
|
|
601
|
+
this.acknowledgeState(deviceId, model, stateDesc, value);
|
|
602
|
+
}
|
|
546
603
|
}
|
|
547
604
|
} catch(error) {
|
|
548
605
|
this.filterError(`Error ${error.code} on send command to ${deviceId}.`+
|
|
@@ -574,6 +631,7 @@ class Zigbee extends utils.Adapter {
|
|
|
574
631
|
payload_obj = JSON.parse();
|
|
575
632
|
} catch (e) {
|
|
576
633
|
this.log.error(`Unable to parse ${safeJsonStringify(payload)}: ${safeJsonStringify(e)}`);
|
|
634
|
+
this.sendError(e, `Unable to parse ${safeJsonStringify(payload)}: ${safeJsonStringify(e)}`);
|
|
577
635
|
return {success:false, error: `Unable to parse ${safeJsonStringify(payload)}: ${safeJsonStringify(e)}`};
|
|
578
636
|
}
|
|
579
637
|
} else if (typeof payload === 'object') {
|
|
@@ -590,15 +648,18 @@ class Zigbee extends utils.Adapter {
|
|
|
590
648
|
const entity = await this.zbController.resolveEntity(devID);
|
|
591
649
|
if (!entity) {
|
|
592
650
|
this.log.error(`Device ${safeJsonStringify(payload_obj.device)} not found`);
|
|
651
|
+
this.sendError(`Device ${safeJsonStringify(payload_obj.device)} not found`);
|
|
593
652
|
return {success: false, error: `Device ${safeJsonStringify(payload_obj.device)} not found`};
|
|
594
653
|
}
|
|
595
654
|
const mappedModel = entity.mapped;
|
|
596
655
|
if (!mappedModel) {
|
|
597
656
|
this.log.error(`No Model for Device ${safeJsonStringify(payload_obj.device)}`);
|
|
657
|
+
this.sendError(`No Model for Device ${safeJsonStringify(payload_obj.device)}`);
|
|
598
658
|
return {success: false, error: `No Model for Device ${safeJsonStringify(payload_obj.device)}`};
|
|
599
659
|
}
|
|
600
660
|
if (typeof payload_obj.payload !== 'object') {
|
|
601
661
|
this.log.error(`Illegal payload type for ${safeJsonStringify(payload_obj.device)}`);
|
|
662
|
+
this.sendError(`Illegal payload type for ${safeJsonStringify(payload_obj.device)}`);
|
|
602
663
|
return {success: false, error: `Illegal payload type for ${safeJsonStringify(payload_obj.device)}`};
|
|
603
664
|
}
|
|
604
665
|
for (const key in payload_obj.payload) {
|
|
@@ -671,8 +732,10 @@ class Zigbee extends utils.Adapter {
|
|
|
671
732
|
await plugin[method]();
|
|
672
733
|
}
|
|
673
734
|
} catch (error) {
|
|
674
|
-
if (error && !error.hasOwnProperty('code'))
|
|
735
|
+
if (error && !error.hasOwnProperty('code')) {
|
|
675
736
|
this.log.error(`Failed to call '${plugin.constructor.name}' '${method}' (${error.stack})`);
|
|
737
|
+
this.sendError(error, `Failed to call '${plugin.constructor.name}' '${method}'`);
|
|
738
|
+
}
|
|
676
739
|
throw error;
|
|
677
740
|
}
|
|
678
741
|
}
|
|
@@ -697,19 +760,31 @@ class Zigbee extends utils.Adapter {
|
|
|
697
760
|
}
|
|
698
761
|
callback();
|
|
699
762
|
} catch (error) {
|
|
700
|
-
|
|
763
|
+
if (error) {
|
|
764
|
+
this.log.error(`Unload error (${error.stack})`);
|
|
765
|
+
}
|
|
766
|
+
this.sendError(error, `Unload error`);
|
|
701
767
|
callback();
|
|
702
768
|
}
|
|
703
769
|
}
|
|
704
770
|
|
|
705
771
|
getZigbeeOptions() {
|
|
706
772
|
// file path for db
|
|
707
|
-
|
|
708
|
-
|
|
709
|
-
|
|
773
|
+
let dbDir = path.join(utils.getAbsoluteInstanceDataDir(this), '');
|
|
774
|
+
dbDir = dbDir.replace('.', '_');
|
|
775
|
+
|
|
776
|
+
if (this.systemConfig && !fs.existsSync(dbDir)) {
|
|
777
|
+
try {
|
|
778
|
+
fs.mkdirSync(dbDir);
|
|
779
|
+
} catch (e) {
|
|
780
|
+
this.log.error(`Cannot create directory ${dbDir}: ${e}`);
|
|
781
|
+
this.sendError(`Cannot create directory ${dbDir}: ${e}`);
|
|
782
|
+
}
|
|
783
|
+
}
|
|
710
784
|
const port = this.config.port;
|
|
711
785
|
if (!port) {
|
|
712
786
|
this.log.error('Serial port not selected! Go to settings page.');
|
|
787
|
+
this.sendError('Serial port not selected! Go to settings page.');
|
|
713
788
|
}
|
|
714
789
|
const panID = parseInt(this.config.panID ? this.config.panID : 0x1a62);
|
|
715
790
|
const channel = parseInt(this.config.channel ? this.config.channel : 11);
|
|
@@ -739,6 +814,7 @@ class Zigbee extends utils.Adapter {
|
|
|
739
814
|
disablePing: this.config.disablePing,
|
|
740
815
|
transmitPower: this.config.transmitPower,
|
|
741
816
|
extPanIdFix: extPanIdFix,
|
|
817
|
+
startWithInconsistent: this.config.startWithInconsistent || false,
|
|
742
818
|
};
|
|
743
819
|
}
|
|
744
820
|
|
|
@@ -770,6 +846,7 @@ class Zigbee extends utils.Adapter {
|
|
|
770
846
|
if (data)
|
|
771
847
|
data = data.toString();
|
|
772
848
|
this.logToPairing('Error: ' + msg + '. ' + data, true);
|
|
849
|
+
this.sendError('Error: ' + msg + '. ' + data);
|
|
773
850
|
break;
|
|
774
851
|
case 'debug':
|
|
775
852
|
logger = this.log.debug;
|
|
@@ -792,8 +869,6 @@ class Zigbee extends utils.Adapter {
|
|
|
792
869
|
}
|
|
793
870
|
}
|
|
794
871
|
}
|
|
795
|
-
|
|
796
|
-
|
|
797
872
|
}
|
|
798
873
|
|
|
799
874
|
|