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/lib/zbDeviceConfigure.js
CHANGED
|
@@ -3,20 +3,19 @@
|
|
|
3
3
|
const zigbeeHerdsmanConverters = require('zigbee-herdsman-converters');
|
|
4
4
|
const BaseExtension = require('./zbBaseExtension');
|
|
5
5
|
|
|
6
|
-
const forcedConfigureOnEachStart = [
|
|
7
|
-
zigbeeHerdsmanConverters.findByModel('V3-BTZB'),
|
|
8
|
-
zigbeeHerdsmanConverters.findByModel('014G2461'),
|
|
9
|
-
zigbeeHerdsmanConverters.findByModel('SPZB0001'),
|
|
10
|
-
zigbeeHerdsmanConverters.findByModel('ZK03840')
|
|
11
|
-
];
|
|
6
|
+
const forcedConfigureOnEachStart = ['V3-BTZB','014G2461','SPZB0001','ZK03840'];
|
|
12
7
|
|
|
13
8
|
class DeviceConfigure extends BaseExtension {
|
|
14
9
|
constructor(zigbee, options) {
|
|
15
10
|
super(zigbee, options);
|
|
16
11
|
|
|
12
|
+
this.delayedConfigure = {};
|
|
13
|
+
|
|
17
14
|
this.configuring = new Set();
|
|
18
15
|
this.attempts = {};
|
|
19
16
|
this.name = 'DeviceConfigure';
|
|
17
|
+
|
|
18
|
+
this.configureKeys = {};
|
|
20
19
|
}
|
|
21
20
|
|
|
22
21
|
setOptions(options) {
|
|
@@ -30,14 +29,42 @@ class DeviceConfigure extends BaseExtension {
|
|
|
30
29
|
if (!mappedDevice || !mappedDevice.configure) {
|
|
31
30
|
return false;
|
|
32
31
|
}
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
32
|
+
const delayedAttempts = this.checkDelayedConfigure(device, 0);
|
|
33
|
+
if (!this.configureKeys.hasOwnProperty(device.ieeeAddr)) this.configureKeys[device.ieeeAddr] = zigbeeHerdsmanConverters.getConfigureKey(mappedDevice);
|
|
34
|
+
if (delayedAttempts > 0 && !device.interviewing) return true;
|
|
35
|
+
if (device.meta.hasOwnProperty('configured') && device.meta.configured !== this.configureKeys[device.ieeeAddr]) return true;
|
|
36
|
+
return (device.interviewing !== true && this.checkDelayedConfigure(device)>0);
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
checkDelayedConfigure(device, num) {
|
|
40
|
+
if (!this.delayedConfigure.hasOwnProperty(device.ieeeAddr)) {
|
|
41
|
+
if (num && num > 0) {
|
|
42
|
+
// this.warn('adding dev ' + device.ieeeAddr + ' to delayedConfigure with ' + num + ' attempts');
|
|
43
|
+
this.delayedConfigure[device.ieeeAddr] = { maxAttempts:num };
|
|
44
|
+
return num;
|
|
45
|
+
}
|
|
46
|
+
return 0;
|
|
36
47
|
}
|
|
48
|
+
const dc = this.delayedConfigure[device.ieeeAddr];
|
|
49
|
+
// this.warn('checkDelayedConfigure for ' + device.ieeeAddr + ' with ' + JSON.stringify(dc));
|
|
50
|
+
dc.maxAttempts--;
|
|
51
|
+
if (dc.maxAttempts > 0) return dc.maxAttempts;
|
|
52
|
+
if (num && num > 0) {
|
|
53
|
+
dc.maxAttempts = num;
|
|
54
|
+
return num;
|
|
55
|
+
}
|
|
56
|
+
return 0;
|
|
57
|
+
}
|
|
37
58
|
|
|
38
|
-
|
|
59
|
+
delayedConfigureAttempt(device, status) {
|
|
60
|
+
if (status) {
|
|
61
|
+
delete this.delayedConfigure[device.ieeeAddr];
|
|
62
|
+
return 0;
|
|
63
|
+
}
|
|
64
|
+
return this.checkDelayedConfigure(device, 10);
|
|
39
65
|
}
|
|
40
66
|
|
|
67
|
+
|
|
41
68
|
async onZigbeeStarted() {
|
|
42
69
|
try {
|
|
43
70
|
this.coordinatorEndpoint = await this.zigbee.getDevicesByType('Coordinator')[0].endpoints[0];
|
|
@@ -58,7 +85,7 @@ class DeviceConfigure extends BaseExtension {
|
|
|
58
85
|
}
|
|
59
86
|
} catch (error) {
|
|
60
87
|
this.sendError(error);
|
|
61
|
-
this.error(`Failed to DeviceConfigure.onZigbeeStarted (${error.
|
|
88
|
+
this.error(`Failed to DeviceConfigure.onZigbeeStarted (${error && error.message ? error.message : 'no error message'})`);
|
|
62
89
|
}
|
|
63
90
|
}
|
|
64
91
|
|
|
@@ -66,14 +93,16 @@ class DeviceConfigure extends BaseExtension {
|
|
|
66
93
|
try {
|
|
67
94
|
const device = data.device;
|
|
68
95
|
if (this.shouldConfigure(device, mappedDevice)) {
|
|
96
|
+
this.debug('ShouldConfigure ' + device.ieeeAddr);
|
|
69
97
|
this.configure(device, mappedDevice);
|
|
70
98
|
}
|
|
71
99
|
} catch (error) {
|
|
72
100
|
this.sendError(error);
|
|
73
|
-
this.error(`Failed to DeviceConfigure.onZigbeeEvent (${error.
|
|
101
|
+
this.error(`Failed to DeviceConfigure.onZigbeeEvent (${error && error.message ? error.message : 'no error message'})`);
|
|
74
102
|
}
|
|
75
103
|
}
|
|
76
104
|
|
|
105
|
+
|
|
77
106
|
onDeviceRemove(device) {
|
|
78
107
|
try {
|
|
79
108
|
if (this.configuring.has(device.ieeeAddr)) {
|
|
@@ -85,7 +114,7 @@ class DeviceConfigure extends BaseExtension {
|
|
|
85
114
|
}
|
|
86
115
|
} catch (error) {
|
|
87
116
|
this.sendError(error);
|
|
88
|
-
this.error(`Failed to DeviceConfigure.onDeviceRemove (${error.
|
|
117
|
+
this.error(`Failed to DeviceConfigure.onDeviceRemove (${error && error.message ? error.message : 'no error message'})`);
|
|
89
118
|
}
|
|
90
119
|
}
|
|
91
120
|
|
|
@@ -100,23 +129,11 @@ class DeviceConfigure extends BaseExtension {
|
|
|
100
129
|
async configure(device, mappedDevice) {
|
|
101
130
|
try {
|
|
102
131
|
if (mappedDevice !== undefined && device !== undefined) {
|
|
103
|
-
if (this.configuring.has(device.ieeeAddr) || this.attempts[device.ieeeAddr] >= 5) {
|
|
104
|
-
return false;
|
|
105
|
-
}
|
|
106
|
-
|
|
107
|
-
this.configuring.add(device.ieeeAddr);
|
|
108
|
-
|
|
109
|
-
if (!this.attempts.hasOwnProperty(device.ieeeAddr)) {
|
|
110
|
-
this.attempts[device.ieeeAddr] = 0;
|
|
111
|
-
}
|
|
112
|
-
|
|
113
132
|
try {
|
|
114
|
-
await this.doConfigure(device, mappedDevice)
|
|
115
|
-
// this.configuring.delete(device.ieeeAddr);
|
|
133
|
+
await this.doConfigure(device, mappedDevice)
|
|
116
134
|
} catch (error) {
|
|
117
135
|
this.sendError(error);
|
|
118
|
-
this.warn(`DeviceConfigure failed ${device.ieeeAddr} ${device.modelID}
|
|
119
|
-
this.attempts[device.ieeeAddr]++;
|
|
136
|
+
this.warn(`DeviceConfigure failed ${device.ieeeAddr} ${device.modelID}`);
|
|
120
137
|
}
|
|
121
138
|
}
|
|
122
139
|
} catch (error) {
|
|
@@ -129,24 +146,41 @@ class DeviceConfigure extends BaseExtension {
|
|
|
129
146
|
const coordinatorEndpoint = await this.zigbee.getDevicesByType('Coordinator')[0].endpoints[0];
|
|
130
147
|
try {
|
|
131
148
|
if (mappedDevice) {
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
await mappedDevice.configure(device, coordinatorEndpoint, this);
|
|
135
|
-
|
|
149
|
+
if (mappedDevice.configure === undefined) return `No configure available for ${device.ieeeAddr} ${device.modelID}.`;
|
|
150
|
+
this.info(`Configuring ${device.ieeeAddr} ${device.modelID}`);
|
|
151
|
+
if (typeof mappedDevice.configure === 'function') await mappedDevice.configure(device, coordinatorEndpoint, this);
|
|
152
|
+
else {
|
|
153
|
+
const promises = [];
|
|
154
|
+
promises.push(...mappedDevice.configure);
|
|
155
|
+
await Promise.all(promises.map(callback => callback(device, coordinatorEndpoint, mappedDevice)))
|
|
156
|
+
}
|
|
136
157
|
device.meta.configured = zigbeeHerdsmanConverters.getConfigureKey(mappedDevice);
|
|
158
|
+
this.configureKeys[device.ieeeAddr] = device.meta.configured;
|
|
137
159
|
device.save();
|
|
138
160
|
this.info(`DeviceConfigure successful ${device.ieeeAddr} ${device.modelID}`);
|
|
161
|
+
this.delayedConfigureAttempt(device, true);
|
|
162
|
+
return '';
|
|
139
163
|
}
|
|
140
164
|
} catch (error) {
|
|
141
165
|
// https://github.com/Koenkk/zigbee2mqtt/issues/14857
|
|
142
166
|
if (error.stack.includes('UNSUPPORTED_ATTRIBUTE')) {
|
|
143
167
|
// do nothing
|
|
144
168
|
} else {
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
169
|
+
if (error && error.message && error.message.match(/(\d+)ms/gm)) {
|
|
170
|
+
// timeout message - we do want to start the configure chain
|
|
171
|
+
const num = this.delayedConfigureAttempt(device, false);
|
|
172
|
+
this.warn(`Timeout trying to configure ${device.ieeeAddr} ${device.modelID}: ${num} attempts remaining`)
|
|
173
|
+
return `Configuration timed out ${device.ieeeAddr} ${device.modelID}. The device did not repond in time to the configuration request. Another attempt will be made when the device is awake.`;
|
|
174
|
+
} else {
|
|
175
|
+
this.sendError(error);
|
|
176
|
+
this.warn(`${device.ieeeAddr} ${device.modelID} Failed to configure. --> ${error && error.message ? error.message : ' no error message given'} `);
|
|
177
|
+
return `${device.ieeeAddr} ${device.modelID} Failed to configure. --> ${error && error.message ? error.message : ' no error message given'} `
|
|
178
|
+
}
|
|
179
|
+
|
|
180
|
+
|
|
148
181
|
}
|
|
149
182
|
}
|
|
183
|
+
return 'no return value specified';
|
|
150
184
|
}
|
|
151
185
|
}
|
|
152
186
|
|
package/lib/zigbeecontroller.js
CHANGED
|
@@ -73,6 +73,17 @@ class ZigbeeController extends EventEmitter {
|
|
|
73
73
|
new DelayedActionExt(this, {}),
|
|
74
74
|
];
|
|
75
75
|
this.herdsmanTimeoutRegexp = new RegExp(/(\d+)ms/);
|
|
76
|
+
this.herdsmanLogSettings = {}
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
|
|
80
|
+
ByteArrayToString(data) {
|
|
81
|
+
return data.map(function (x) {
|
|
82
|
+
x = x + 0x100 + 1; // twos complement
|
|
83
|
+
x = x.toString(16); // to hex
|
|
84
|
+
x = ('00'+x).substr(-2); // zero-pad to 8-digits
|
|
85
|
+
return x
|
|
86
|
+
}).join('');
|
|
76
87
|
}
|
|
77
88
|
|
|
78
89
|
configure(options) {
|
|
@@ -110,8 +121,9 @@ class ZigbeeController extends EventEmitter {
|
|
|
110
121
|
}
|
|
111
122
|
this.disableLed = options.disableLed;
|
|
112
123
|
this.warnOnDeviceAnnouncement = options.warnOnDeviceAnnouncement;
|
|
113
|
-
|
|
114
|
-
this.
|
|
124
|
+
this.herdsmanLogSettings.panID = herdsmanSettings.network.panID;
|
|
125
|
+
this.herdsmanLogSettings.channel = herdsmanSettings.network.channelList[0];
|
|
126
|
+
this.herdsmanLogSettings.extendedPanID = this.ByteArrayToString(herdsmanSettings.network.extendedPanID);
|
|
115
127
|
this.herdsman = new ZigbeeHerdsman.Controller(herdsmanSettings, this.adapter.log);
|
|
116
128
|
this.callExtensionMethod('setOptions', [{
|
|
117
129
|
disableActivePing: options.disablePing,
|
|
@@ -136,11 +148,10 @@ class ZigbeeController extends EventEmitter {
|
|
|
136
148
|
this.herdsman.on('message', this.handleMessage.bind(this));
|
|
137
149
|
this.herdsman.on('permitJoinChanged', this.handlePermitJoinChanged.bind(this));
|
|
138
150
|
|
|
151
|
+
this.info('Starting Zigbee-Herdsman');
|
|
139
152
|
await this.herdsman.start();
|
|
140
|
-
|
|
141
|
-
this.debug('zigbee-herdsman started');
|
|
142
153
|
this.herdsmanStarted = true;
|
|
143
|
-
this.info(`Coordinator firmware version: ${JSON.stringify(await this.herdsman.getCoordinatorVersion())}`);
|
|
154
|
+
this.info(`Zigbee-Herdsman started successfully with Coordinator firmware version: ${JSON.stringify(await this.herdsman.getCoordinatorVersion())}`);
|
|
144
155
|
|
|
145
156
|
// debug info from herdsman getNetworkParameters
|
|
146
157
|
const debNetworkParam = JSON.parse(JSON.stringify(await this.herdsman.getNetworkParameters()));
|
|
@@ -152,14 +163,29 @@ class ZigbeeController extends EventEmitter {
|
|
|
152
163
|
extPanIDDebug += extendedPanIDDebug[i];
|
|
153
164
|
i--;
|
|
154
165
|
}
|
|
155
|
-
|
|
156
|
-
this.info(`Zigbee network parameters: panID=${debNetworkParam.panID} channel=${debNetworkParam.channel} extendedPanID=${extPanIDDebug}`);
|
|
166
|
+
this.debug(`Network parameters: panID=${debNetworkParam.panID} channel=${debNetworkParam.channel} extendedPanID=${extPanIDDebug}`);
|
|
157
167
|
} catch (e) {
|
|
168
|
+
try {
|
|
169
|
+
const debNetworkParam = JSON.parse(JSON.stringify(await this.herdsman.getNetworkParameters()));
|
|
170
|
+
const extendedPanIDDebug = typeof debNetworkParam.extendedPanID === 'string' ? debNetworkParam.extendedPanID.replace('0x', '') : debNetworkParam.extendedPanID;
|
|
171
|
+
|
|
172
|
+
let extPanIDDebug = '';
|
|
173
|
+
for (let i = extendedPanIDDebug.length - 1; i >= 0; i--) {
|
|
174
|
+
extPanIDDebug += extendedPanIDDebug[i - 1];
|
|
175
|
+
extPanIDDebug += extendedPanIDDebug[i];
|
|
176
|
+
i--;
|
|
177
|
+
}
|
|
178
|
+
this.warn(`Network parameters in Config : panID=${this.herdsmanLogSettings.panID} channel=${this.herdsmanLogSettings.channel} extendedPanID=${this.herdsmanLogSettings.extendedPanID}`)
|
|
179
|
+
this.warn(`Network parameters on Coordinator: panID=${debNetworkParam.panID} channel=${debNetworkParam.channel} extendedPanID=${extPanIDDebug}`);
|
|
180
|
+
}
|
|
181
|
+
catch (error) {
|
|
182
|
+
this.info(`Unable to obtain herdsman settings`)
|
|
183
|
+
}
|
|
158
184
|
try {
|
|
159
185
|
await this.herdsman.stop();
|
|
160
186
|
}
|
|
161
187
|
catch (error) {
|
|
162
|
-
this.warn(
|
|
188
|
+
this.warn('unable to stop zigbee-herdsman after failed startup');
|
|
163
189
|
}
|
|
164
190
|
this.sendError(e);
|
|
165
191
|
this.error(`Starting zigbee-herdsman problem : ${(e && e.message ? e.message : 'no error message')}`);
|
|
@@ -207,18 +233,20 @@ class ZigbeeController extends EventEmitter {
|
|
|
207
233
|
}
|
|
208
234
|
|
|
209
235
|
// Call extensions
|
|
210
|
-
this.callExtensionMethod('onZigbeeStarted', []);
|
|
211
236
|
|
|
212
237
|
const deviceIterator = this.getClientIterator();
|
|
213
238
|
let deviceCount = 0;
|
|
214
239
|
try {
|
|
215
240
|
for (const device of deviceIterator) {
|
|
216
241
|
deviceCount++;
|
|
242
|
+
// get the model description for the known devices
|
|
217
243
|
const entity = await this.resolveEntity(device);
|
|
218
244
|
if (!entity) {
|
|
219
245
|
this.warn('failed to resolve Entity for ' + device.ieeeAddr);
|
|
220
246
|
continue;
|
|
221
247
|
}
|
|
248
|
+
//await this.adapter.stController.AddModelFromHerdsman(device, entity.mapped.model);
|
|
249
|
+
|
|
222
250
|
this.adapter.getObject(device.ieeeAddr.substr(2), (err, obj) => {
|
|
223
251
|
if (obj && obj.common && obj.common.deactivated) {
|
|
224
252
|
this.callExtensionMethod('deregisterDevicePing', [device, entity]);
|
|
@@ -248,6 +276,7 @@ class ZigbeeController extends EventEmitter {
|
|
|
248
276
|
} else {
|
|
249
277
|
this.info(`Currently no devices.`);
|
|
250
278
|
}
|
|
279
|
+
this.callExtensionMethod('onZigbeeStarted', []);
|
|
251
280
|
}
|
|
252
281
|
catch (error) {
|
|
253
282
|
this.error('error iterating devices : '+ (error && error.message ? error.message: 'no reason given'));
|
|
@@ -718,7 +747,11 @@ class ZigbeeController extends EventEmitter {
|
|
|
718
747
|
this.debug('handleDeviceLeave', message);
|
|
719
748
|
const entity = await this.resolveEntity(message.device || message.ieeeAddr);
|
|
720
749
|
const friendlyName = entity ? entity.name : message.ieeeAddr;
|
|
721
|
-
this.
|
|
750
|
+
if (this.adapter.stController.checkDebugDevice(friendlyName)) {
|
|
751
|
+
this.emit('device_debug', {ID: Date.now(), data: {flag:'dl', states:[{id: '--', value:'--', payload:message}], IO:true},message:`Device '${friendlyName}' has left the network`});
|
|
752
|
+
}
|
|
753
|
+
else
|
|
754
|
+
this.info(`Device '${friendlyName}' left the network`);
|
|
722
755
|
this.emit('leave', message.ieeeAddr);
|
|
723
756
|
// Call extensions
|
|
724
757
|
this.callExtensionMethod(
|
|
@@ -735,7 +768,10 @@ class ZigbeeController extends EventEmitter {
|
|
|
735
768
|
this.debug('handleDeviceAnnounce', message);
|
|
736
769
|
const entity = await this.resolveEntity(message.device || message.ieeeAddr);
|
|
737
770
|
const friendlyName = entity.name;
|
|
738
|
-
if (this.
|
|
771
|
+
if (this.adapter.stController.checkDebugDevice(friendlyName)) {
|
|
772
|
+
this.emit('device_debug', {ID: Date.now(), data: {flag:'da', states:[{id: '--', value:'--', payload:message}] , IO:true} ,message:`Device '${friendlyName}' announced itself`});
|
|
773
|
+
}
|
|
774
|
+
else if (this.warnOnDeviceAnnouncement) {
|
|
739
775
|
this.warn(`Device '${friendlyName}' announced itself`);
|
|
740
776
|
} else {
|
|
741
777
|
this.info(`Device '${friendlyName}' announced itself`);
|
|
@@ -854,7 +890,7 @@ class ZigbeeController extends EventEmitter {
|
|
|
854
890
|
let resolved = await this.resolveEntity(device, 0);
|
|
855
891
|
if (!resolved) {
|
|
856
892
|
resolved = { name:'unresolved device', device:device }
|
|
857
|
-
this.
|
|
893
|
+
this.debug('resolve Entity failed for ' + device.ieeeAddr)
|
|
858
894
|
}
|
|
859
895
|
let result;
|
|
860
896
|
|
|
@@ -914,9 +950,9 @@ class ZigbeeController extends EventEmitter {
|
|
|
914
950
|
|
|
915
951
|
callback && callback({lqis, routing, errors});
|
|
916
952
|
if (errors.length) {
|
|
917
|
-
this.
|
|
953
|
+
this.debug(`Map Data collection complete with ${errors.length} issues:`);
|
|
918
954
|
for (const msg of errors)
|
|
919
|
-
this.
|
|
955
|
+
this.debug(msg);
|
|
920
956
|
}
|
|
921
957
|
else
|
|
922
958
|
this.info('Map data collection complete');
|