iobroker.zigbee 1.8.1 → 1.8.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/.eslintignore +2 -0
- package/.eslintrc.json +37 -0
- package/.github/FUNDING.yml +3 -0
- package/.github/auto-merge.yml +17 -0
- package/.github/dependabot.yml +24 -0
- package/.github/stale.yml +13 -0
- package/.github/workflows/codeql.yml +41 -0
- package/.github/workflows/dependabot-automerge.yml +22 -0
- package/.github/workflows/test-and-release.yml +149 -0
- package/.releaseconfig.json +3 -0
- package/.travis/wiki.sh +28 -0
- package/.travis.yml +41 -0
- package/README.md +31 -8
- package/admin/admin.js +466 -482
- package/admin/i18n/de/translations.json +2 -2
- package/admin/index_m.html +1 -1
- package/admin/tab_m.html +3 -44
- package/admin/words.js +2 -2
- package/gulpfile.js +464 -0
- package/io-package.json +18 -25
- package/lib/backup.js +2 -2
- package/lib/binding.js +37 -32
- package/lib/colors.js +158 -163
- package/lib/commands.js +90 -99
- package/lib/developer.js +12 -9
- package/lib/devices.js +179 -169
- package/lib/exclude.js +36 -30
- package/lib/exposes.js +139 -163
- package/lib/groups.js +83 -81
- package/lib/json.js +6 -5
- package/lib/networkmap.js +3 -2
- package/lib/ota.js +18 -34
- package/lib/rgb.js +72 -114
- package/lib/seriallist.js +20 -25
- package/lib/states.js +526 -511
- package/lib/statescontroller.js +183 -206
- package/lib/utils.js +23 -24
- package/lib/zbBaseExtension.js +4 -4
- package/lib/zbDelayedAction.js +13 -5
- package/lib/zbDeviceAvailability.js +65 -69
- package/lib/zbDeviceConfigure.js +21 -9
- package/lib/zbDeviceEvent.js +4 -3
- package/lib/zigbeecontroller.js +103 -109
- package/main.js +147 -163
- package/package.json +15 -29
- package/test/integration.js +5 -0
- package/test/mocha.custom.opts +2 -0
- package/test/mocha.setup.js +14 -0
- package/test/package.js +5 -0
- package/test/unit.js +5 -0
- package/docs/de/img/CC2531.png +0 -0
- package/docs/de/img/CC2538_CC2592_PA.PNG +0 -0
- package/docs/de/img/CC2591.png +0 -0
- package/docs/de/img/boards.jpg +0 -0
- package/docs/de/img/cc26x2r.PNG +0 -0
- package/docs/de/img/results.jpg +0 -0
- package/docs/de/img/sku_429478_2.png +0 -0
- package/docs/de/img/sku_429601_2.png +0 -0
- package/docs/de/readme.md +0 -27
- package/docs/en/img/CC2531.png +0 -0
- package/docs/en/img/CC2591.png +0 -0
- package/docs/en/img/deconz.png +0 -0
- package/docs/en/img/sku_429478_2.png +0 -0
- package/docs/en/img/sku_429601_2.png +0 -0
- package/docs/en/readme.md +0 -30
- package/docs/flashing_via_arduino_(en).md +0 -110
- package/docs/ru/img/CC2531.png +0 -0
- package/docs/ru/img/CC2591.png +0 -0
- package/docs/ru/img/sku_429478_2.png +0 -0
- package/docs/ru/img/sku_429601_2.png +0 -0
- package/docs/ru/readme.md +0 -28
- package/docs/tutorial/CC2530_20190425.zip +0 -0
- package/docs/tutorial/CC2530_CC2591_20190515.zip +0 -0
- package/docs/tutorial/CC2530_CC2592_20190515.zip +0 -0
- package/docs/tutorial/CC2531_20190425.zip +0 -0
- package/docs/tutorial/adm5_1.PNG +0 -0
- package/docs/tutorial/adm5_2.PNG +0 -0
- package/docs/tutorial/cat.PNG +0 -0
- package/docs/tutorial/groups-1.png +0 -0
- package/docs/tutorial/groups-2.png +0 -0
- package/docs/tutorial/inst.PNG +0 -0
- package/docs/tutorial/reflash-finish.PNG +0 -0
- package/docs/tutorial/reflash-step0.png +0 -0
- package/docs/tutorial/reflash-step1.PNG +0 -0
- package/docs/tutorial/reflash-step2.PNG +0 -0
- package/docs/tutorial/settings.png +0 -0
- package/docs/tutorial/tab-dev-1.png +0 -0
- package/docs/tutorial/zigbee.png +0 -0
- package/docs/tutorial/zigbee15.png +0 -0
package/main.js
CHANGED
|
@@ -40,20 +40,21 @@ const createByteArray = function (hexString) {
|
|
|
40
40
|
return bytes;
|
|
41
41
|
};
|
|
42
42
|
|
|
43
|
-
const E_INFO
|
|
44
|
-
const E_DEBUG
|
|
45
|
-
const E_WARN
|
|
46
|
-
const E_ERROR
|
|
43
|
+
const E_INFO=1;
|
|
44
|
+
const E_DEBUG=2;
|
|
45
|
+
const E_WARN=3;
|
|
46
|
+
const E_ERROR=4;
|
|
47
47
|
|
|
48
48
|
let _pairingMode = false;
|
|
49
49
|
|
|
50
50
|
const errorCodes = {
|
|
51
|
-
9999: {severity:
|
|
52
|
-
233: {severity:
|
|
53
|
-
205: {severity:
|
|
54
|
-
134: {severity:
|
|
51
|
+
9999: { severity:E_INFO, message:'No response'},
|
|
52
|
+
233: { severity:E_DEBUG, message:'MAC NO ACK'},
|
|
53
|
+
205: { severity:E_WARN, message:'No network route'},
|
|
54
|
+
134: { severity:E_WARN, message:'Unnsupported Attribute'},
|
|
55
55
|
};
|
|
56
56
|
|
|
57
|
+
|
|
57
58
|
class Zigbee extends utils.Adapter {
|
|
58
59
|
/**
|
|
59
60
|
* @param {Partial<ioBroker.AdapterOptions>} [options={}]
|
|
@@ -92,15 +93,14 @@ class Zigbee extends utils.Adapter {
|
|
|
92
93
|
case 'SendToDevice': {
|
|
93
94
|
let rv = {
|
|
94
95
|
success: false,
|
|
95
|
-
loc
|
|
96
|
+
loc:-1,
|
|
96
97
|
};
|
|
97
|
-
|
|
98
98
|
try {
|
|
99
|
-
rv = await this.
|
|
100
|
-
}
|
|
99
|
+
rv = await this.SendPayload(obj.message);
|
|
100
|
+
}
|
|
101
|
+
catch (e) {
|
|
101
102
|
rv.error = e;
|
|
102
103
|
}
|
|
103
|
-
|
|
104
104
|
this.sendTo(obj.from, obj.command, rv, obj.callback);
|
|
105
105
|
break;
|
|
106
106
|
}
|
|
@@ -115,15 +115,15 @@ class Zigbee extends utils.Adapter {
|
|
|
115
115
|
const Sentry = sentryInstance.getSentryObject();
|
|
116
116
|
if (Sentry) {
|
|
117
117
|
if (message) {
|
|
118
|
-
Sentry.configureScope(scope =>
|
|
118
|
+
Sentry.configureScope(scope => {
|
|
119
119
|
scope.addBreadcrumb({
|
|
120
|
-
type:
|
|
121
|
-
category:
|
|
120
|
+
type: "error", // predefined types
|
|
121
|
+
category: "error message",
|
|
122
122
|
level: Sentry.Severity.Error,
|
|
123
|
-
message
|
|
124
|
-
})
|
|
123
|
+
message: message
|
|
124
|
+
});
|
|
125
|
+
});
|
|
125
126
|
}
|
|
126
|
-
|
|
127
127
|
if (typeof error == 'string') {
|
|
128
128
|
Sentry.captureException(new Error(error));
|
|
129
129
|
} else {
|
|
@@ -135,31 +135,27 @@ class Zigbee extends utils.Adapter {
|
|
|
135
135
|
}
|
|
136
136
|
|
|
137
137
|
filterError(errormessage, message, error) {
|
|
138
|
-
if (error.code === undefined)
|
|
139
|
-
|
|
140
|
-
em =
|
|
141
|
-
|
|
138
|
+
if (error.code === undefined)
|
|
139
|
+
{
|
|
140
|
+
let em = error.stack.match(/failed \((.+?)\) at/);
|
|
141
|
+
if (!em) em = error.stack.match(/failed \((.+?)\)/);
|
|
142
|
+
this.log.error(`${message} no error code (${(em ? em[1]:'undefined')})`);
|
|
142
143
|
this.sendError(error, `${message} no error code`);
|
|
143
144
|
this.log.debug(`Stack trace for ${em}: ${error.stack}`);
|
|
144
145
|
return;
|
|
145
146
|
}
|
|
146
|
-
|
|
147
147
|
const ecode = errorCodes[error.code];
|
|
148
148
|
if (ecode === undefined) {
|
|
149
149
|
this.log.error(errormessage);
|
|
150
150
|
this.sendError(error, errormessage);
|
|
151
151
|
return;
|
|
152
152
|
}
|
|
153
|
-
|
|
154
153
|
switch (ecode.severity) {
|
|
155
|
-
case E_INFO:
|
|
156
|
-
this.log.info(`${message}: Code ${error.code} (${ecode.message})`);
|
|
154
|
+
case E_INFO: this.log.info(`${message}: Code ${error.code} (${ecode.message})`);
|
|
157
155
|
break;
|
|
158
|
-
case E_DEBUG:
|
|
159
|
-
this.log.debug(`${message}: Code ${error.code} (${ecode.message})`);
|
|
156
|
+
case E_DEBUG: this.log.debug(`${message}: Code ${error.code} (${ecode.message})`);
|
|
160
157
|
break;
|
|
161
|
-
case E_WARN:
|
|
162
|
-
this.log.warn(`${message}: Code ${error.code} (${ecode.message})`);
|
|
158
|
+
case E_WARN: this.log.warn(`${message}: Code ${error.code} (${ecode.message})`);
|
|
163
159
|
break;
|
|
164
160
|
case E_ERROR:
|
|
165
161
|
this.log.error(`${message}: Code ${error.code} (${ecode.message})`);
|
|
@@ -168,11 +164,10 @@ class Zigbee extends utils.Adapter {
|
|
|
168
164
|
default:
|
|
169
165
|
this.log.error(`${message}: Code ${error.code} (malformed error)`);
|
|
170
166
|
this.sendError(error, `${message}: Code ${error.code} (malformed error)`);
|
|
171
|
-
break;
|
|
172
167
|
}
|
|
173
168
|
}
|
|
174
169
|
|
|
175
|
-
debugLog(data, ...args) {
|
|
170
|
+
debugLog (data, ...args) {
|
|
176
171
|
const message = (args) ? util.format(data, ...args) : data;
|
|
177
172
|
this.log.debug(message.slice(message.indexOf('zigbee-herdsman')));
|
|
178
173
|
}
|
|
@@ -186,8 +181,9 @@ class Zigbee extends utils.Adapter {
|
|
|
186
181
|
// external converters
|
|
187
182
|
this.applyExternalConverters();
|
|
188
183
|
// get exclude list from object
|
|
189
|
-
this.getState('exclude.all', (err, state) =>
|
|
190
|
-
this.stController.getExcludeExposes(state)
|
|
184
|
+
this.getState('exclude.all', (err, state) => {
|
|
185
|
+
this.stController.getExcludeExposes(state);
|
|
186
|
+
});
|
|
191
187
|
|
|
192
188
|
this.subscribeStates('*');
|
|
193
189
|
// set connection false before connect to zigbee
|
|
@@ -216,9 +212,7 @@ class Zigbee extends utils.Adapter {
|
|
|
216
212
|
}
|
|
217
213
|
const extfiles = this.config.external.split(';');
|
|
218
214
|
for (const moduleName of extfiles) {
|
|
219
|
-
if (!moduleName)
|
|
220
|
-
continue;
|
|
221
|
-
}
|
|
215
|
+
if (!moduleName) continue;
|
|
222
216
|
this.log.info(`Apply converter from module: ${moduleName}`);
|
|
223
217
|
const sandbox = {
|
|
224
218
|
require,
|
|
@@ -237,7 +231,7 @@ class Zigbee extends utils.Adapter {
|
|
|
237
231
|
}
|
|
238
232
|
}
|
|
239
233
|
|
|
240
|
-
applyExternalConverters()
|
|
234
|
+
applyExternalConverters(){
|
|
241
235
|
for (const definition of this.getExternalDefinition()) {
|
|
242
236
|
const toAdd = {...definition};
|
|
243
237
|
delete toAdd['homeassistant'];
|
|
@@ -250,23 +244,24 @@ class Zigbee extends utils.Adapter {
|
|
|
250
244
|
try {
|
|
251
245
|
const DebugIdentify = require('./debugidentify');
|
|
252
246
|
debugversion = DebugIdentify.ReportIdentifier();
|
|
253
|
-
}
|
|
247
|
+
}
|
|
248
|
+
catch {
|
|
254
249
|
debugversion = ' npm ...';
|
|
255
250
|
}
|
|
256
251
|
|
|
257
252
|
// installed version
|
|
258
253
|
let gitVers = '';
|
|
259
254
|
try {
|
|
260
|
-
this.log.info(
|
|
255
|
+
this.log.info('Starting Zigbee ' + debugversion);
|
|
261
256
|
|
|
262
|
-
await this.getForeignObject(
|
|
257
|
+
await this.getForeignObject('system.adapter.' + this.namespace, (err, obj) => {
|
|
263
258
|
if (!err && obj && obj.common.installedFrom && obj.common.installedFrom.includes('://')) {
|
|
264
259
|
const instFrom = obj.common.installedFrom;
|
|
265
|
-
gitVers = gitVers + instFrom.replace('tarball',
|
|
260
|
+
gitVers = gitVers + instFrom.replace('tarball','commit');
|
|
266
261
|
} else {
|
|
267
262
|
gitVers = obj.common.installedFrom;
|
|
268
263
|
}
|
|
269
|
-
this.log.info(
|
|
264
|
+
this.log.info('Installed Version: ' + gitVers );
|
|
270
265
|
});
|
|
271
266
|
|
|
272
267
|
await this.zbController.start();
|
|
@@ -279,7 +274,6 @@ class Zigbee extends utils.Adapter {
|
|
|
279
274
|
this.log.error(error);
|
|
280
275
|
}
|
|
281
276
|
this.sendError(error, `Failed to start Zigbee`);
|
|
282
|
-
|
|
283
277
|
if (this.reconnectCounter > 0) {
|
|
284
278
|
this.tryToReconnect();
|
|
285
279
|
}
|
|
@@ -296,11 +290,11 @@ class Zigbee extends utils.Adapter {
|
|
|
296
290
|
}
|
|
297
291
|
|
|
298
292
|
tryToReconnect() {
|
|
299
|
-
this.reconnectTimer = setTimeout(()
|
|
300
|
-
if (this.config.port.
|
|
301
|
-
// Controller connect though
|
|
302
|
-
// Unlikely USB dongle, connection broken may only
|
|
303
|
-
//
|
|
293
|
+
this.reconnectTimer = setTimeout(()=>{
|
|
294
|
+
if (this.config.port.indexOf('tcp://') !== -1) {
|
|
295
|
+
// Controller connect though WiFi.
|
|
296
|
+
// Unlikely USB dongle, connection broken may only caused user unpluged the dongle,
|
|
297
|
+
// WiFi connected gateway is possible that device connection is broken caused by
|
|
304
298
|
// AP issue or Zigbee gateway power is turned off unexpectedly.
|
|
305
299
|
// So try to reconnect gateway every 10 seconds all the time.
|
|
306
300
|
this.log.info(`Try to reconnect.`);
|
|
@@ -309,7 +303,7 @@ class Zigbee extends utils.Adapter {
|
|
|
309
303
|
this.reconnectCounter -= 1;
|
|
310
304
|
}
|
|
311
305
|
this.doConnect();
|
|
312
|
-
}, 10
|
|
306
|
+
}, 10*1000); // every 10 seconds
|
|
313
307
|
}
|
|
314
308
|
|
|
315
309
|
async onZigbeeAdapterReady() {
|
|
@@ -318,13 +312,13 @@ class Zigbee extends utils.Adapter {
|
|
|
318
312
|
// https://github.com/ioBroker/ioBroker.zigbee/issues/668
|
|
319
313
|
const extPanIdFix = this.config.extPanIdFix ? this.config.extPanIdFix : false;
|
|
320
314
|
if (!extPanIdFix) {
|
|
321
|
-
const configExtPanId = this.config.extPanID ? '0x'
|
|
315
|
+
const configExtPanId = this.config.extPanID ? '0x'+this.config.extPanID.toLowerCase() : '0xdddddddddddddddd';
|
|
322
316
|
let networkExtPanId = (await this.zbController.herdsman.getNetworkParameters()).extendedPanID;
|
|
323
317
|
let needChange = false;
|
|
324
318
|
this.log.debug(`Config value ${configExtPanId} : Network value ${networkExtPanId}`);
|
|
325
319
|
const adapterType = this.config.adapterType || 'zstack';
|
|
326
320
|
if (adapterType === 'zstack') {
|
|
327
|
-
if (configExtPanId
|
|
321
|
+
if (configExtPanId != networkExtPanId) {
|
|
328
322
|
try {
|
|
329
323
|
// try to read from nvram
|
|
330
324
|
const result = await this.zbController.herdsman.adapter.znp.request(
|
|
@@ -340,9 +334,9 @@ class Zigbee extends utils.Adapter {
|
|
|
340
334
|
2, // ZnpCommandStatus.INVALID_PARAM
|
|
341
335
|
]
|
|
342
336
|
);
|
|
343
|
-
const nwExtPanId = '0x'
|
|
337
|
+
const nwExtPanId = '0x'+result.payload.value.reverse().toString('hex');
|
|
344
338
|
this.log.debug(`Config value ${configExtPanId} : nw value ${nwExtPanId}`);
|
|
345
|
-
if (configExtPanId
|
|
339
|
+
if (configExtPanId != nwExtPanId) {
|
|
346
340
|
networkExtPanId = nwExtPanId;
|
|
347
341
|
needChange = true;
|
|
348
342
|
}
|
|
@@ -372,36 +366,37 @@ class Zigbee extends utils.Adapter {
|
|
|
372
366
|
for (const device of devicesFromDB) {
|
|
373
367
|
const entity = await this.zbController.resolveEntity(device);
|
|
374
368
|
if (entity) {
|
|
375
|
-
const model = entity.mapped ? entity.mapped.model : entity.device.modelID;
|
|
376
|
-
this.stController.updateDev(device.ieeeAddr.substr(2), model, model, () =>
|
|
377
|
-
this.stController.syncDevStates(device, model)
|
|
369
|
+
const model = (entity.mapped) ? entity.mapped.model : entity.device.modelID;
|
|
370
|
+
this.stController.updateDev(device.ieeeAddr.substr(2), model, model, () => {
|
|
371
|
+
this.stController.syncDevStates(device, model);
|
|
372
|
+
});
|
|
378
373
|
}
|
|
379
374
|
}
|
|
380
375
|
await this.callPluginMethod('start', [this.zbController, this.stController]);
|
|
381
376
|
}
|
|
382
377
|
|
|
383
378
|
async checkIfModelUpdate(entity) {
|
|
384
|
-
const model = entity.mapped ? entity.mapped.model : entity.device.modelID,
|
|
379
|
+
const model = (entity.mapped) ? entity.mapped.model : entity.device.modelID,
|
|
385
380
|
device = entity.device,
|
|
386
381
|
devId = device.ieeeAddr.substr(2);
|
|
387
|
-
|
|
388
382
|
return new Promise((resolve) => {
|
|
389
383
|
this.getObject(devId, (err, obj) => {
|
|
390
|
-
if (obj && obj.common.type
|
|
384
|
+
if (obj && obj.common.type != model) {
|
|
391
385
|
// let's change model
|
|
392
386
|
this.getStatesOf(devId, (err, states) => {
|
|
393
387
|
if (!err && states) {
|
|
394
388
|
const chain = [];
|
|
395
|
-
states.forEach((state) =>
|
|
396
|
-
chain.push(this.deleteStateAsync(devId, null, state._id))
|
|
397
|
-
|
|
398
|
-
Promise.all(chain)
|
|
399
|
-
.
|
|
400
|
-
this.stController.
|
|
401
|
-
this.stController.
|
|
402
|
-
|
|
403
|
-
|
|
404
|
-
|
|
389
|
+
states.forEach((state) => {
|
|
390
|
+
chain.push(this.deleteStateAsync(devId, null, state._id));
|
|
391
|
+
});
|
|
392
|
+
Promise.all(chain).then(()=>{
|
|
393
|
+
this.stController.deleteDeviceStates(devId, () => {
|
|
394
|
+
this.stController.updateDev(devId, model, model, async () => {
|
|
395
|
+
await this.stController.syncDevStates(device, model);
|
|
396
|
+
resolve();
|
|
397
|
+
});
|
|
398
|
+
});
|
|
399
|
+
});
|
|
405
400
|
} else {
|
|
406
401
|
resolve();
|
|
407
402
|
}
|
|
@@ -413,16 +408,17 @@ class Zigbee extends utils.Adapter {
|
|
|
413
408
|
});
|
|
414
409
|
}
|
|
415
410
|
|
|
416
|
-
async onZigbeeEvent(type, entity, message) {
|
|
417
|
-
this.log.debug(`Type ${type} device ${safeJsonStringify(entity)} incoming event: ${safeJsonStringify(message)}`);
|
|
418
|
-
const device = entity.device;
|
|
419
|
-
const mappedModel = entity.mapped;
|
|
420
|
-
const model = (entity.mapped) ? entity.mapped.model : entity.device.modelID;
|
|
421
|
-
const cluster = message.cluster;
|
|
422
|
-
const devId = device.ieeeAddr.substr(2);
|
|
423
|
-
const meta = {device};
|
|
424
411
|
|
|
425
|
-
|
|
412
|
+
|
|
413
|
+
async onZigbeeEvent(type, entity, message){
|
|
414
|
+
this.log.debug(`Type ${type} device ${safeJsonStringify(entity)} incoming event: ${safeJsonStringify(message)}`);
|
|
415
|
+
const device = entity.device,
|
|
416
|
+
mappedModel = entity.mapped,
|
|
417
|
+
model = (entity.mapped) ? entity.mapped.model : entity.device.modelID,
|
|
418
|
+
cluster = message.cluster,
|
|
419
|
+
devId = device.ieeeAddr.substr(2),
|
|
420
|
+
meta = {device: device};
|
|
421
|
+
//this assigment give possibility to use iobroker logger in code of the converters, via meta.logger
|
|
426
422
|
meta.logger = this.log;
|
|
427
423
|
|
|
428
424
|
await this.checkIfModelUpdate(entity);
|
|
@@ -443,14 +439,12 @@ class Zigbee extends utils.Adapter {
|
|
|
443
439
|
}
|
|
444
440
|
let converters = mappedModel.fromZigbee.filter(c => c && c.cluster === cluster && (
|
|
445
441
|
(c.type instanceof Array) ? c.type.includes(type) : c.type === type));
|
|
446
|
-
|
|
447
442
|
if (!converters.length && type === 'readResponse') {
|
|
448
443
|
converters = mappedModel.fromZigbee.filter(c => c.cluster === cluster && (
|
|
449
444
|
(c.type instanceof Array) ? c.type.includes('attributeReport') : c.type === 'attributeReport'));
|
|
450
445
|
}
|
|
451
|
-
|
|
452
446
|
if (!converters.length) {
|
|
453
|
-
if (type
|
|
447
|
+
if (type != 'readResponse') {
|
|
454
448
|
this.log.debug(
|
|
455
449
|
`No converter available for '${mappedModel.model}' with cluster '${cluster}' and type '${type}'`
|
|
456
450
|
);
|
|
@@ -482,10 +476,10 @@ class Zigbee extends utils.Adapter {
|
|
|
482
476
|
|
|
483
477
|
acknowledgeState(deviceId, model, stateDesc, value) {
|
|
484
478
|
if (model === 'group') {
|
|
485
|
-
const stateId =
|
|
479
|
+
const stateId = this.namespace + '.group_' + deviceId + '.' + stateDesc.id;
|
|
486
480
|
this.setState(stateId, value, true);
|
|
487
481
|
} else {
|
|
488
|
-
const stateId =
|
|
482
|
+
const stateId = this.namespace + '.' + deviceId.replace('0x', '') + '.' + stateDesc.id;
|
|
489
483
|
this.setState(stateId, value, true);
|
|
490
484
|
}
|
|
491
485
|
}
|
|
@@ -496,9 +490,9 @@ class Zigbee extends utils.Adapter {
|
|
|
496
490
|
});
|
|
497
491
|
}
|
|
498
492
|
|
|
499
|
-
async publishFromState(deviceId, model, stateModel, stateList, options)
|
|
493
|
+
async publishFromState(deviceId, model, stateModel, stateList, options){
|
|
500
494
|
let isGroup = false;
|
|
501
|
-
if (model
|
|
495
|
+
if (model == 'group') {
|
|
502
496
|
isGroup = true;
|
|
503
497
|
deviceId = parseInt(deviceId);
|
|
504
498
|
}
|
|
@@ -509,17 +503,17 @@ class Zigbee extends utils.Adapter {
|
|
|
509
503
|
this.log.debug(`No mapped model for ${model}`);
|
|
510
504
|
return;
|
|
511
505
|
}
|
|
512
|
-
this.log.debug(
|
|
506
|
+
this.log.debug('Mapped Model: ' + JSON.stringify(mappedModel));
|
|
513
507
|
|
|
514
|
-
stateList.forEach(async
|
|
508
|
+
stateList.forEach(async(changedState) => {
|
|
515
509
|
const stateDesc = changedState.stateDesc;
|
|
516
510
|
const value = changedState.value;
|
|
517
511
|
|
|
518
|
-
if (stateDesc.id
|
|
512
|
+
if (stateDesc.id == 'send_payload') {
|
|
519
513
|
try {
|
|
520
514
|
const json_value = JSON.parse(value);
|
|
521
|
-
const payload = {device:
|
|
522
|
-
const result = await
|
|
515
|
+
const payload = { device:deviceId.replace('0x', ''), payload:json_value };
|
|
516
|
+
const result = await(this.SendPayload(payload));
|
|
523
517
|
if (result.hasOwnProperty('success') && result.success) {
|
|
524
518
|
this.acknowledgeState(deviceId, model, stateDesc, value);
|
|
525
519
|
}
|
|
@@ -538,7 +532,7 @@ class Zigbee extends utils.Adapter {
|
|
|
538
532
|
// if this is the device query state => trigger the device query
|
|
539
533
|
|
|
540
534
|
// on activation of the 'device_query' state trigger hardware query where possible
|
|
541
|
-
if (stateDesc.id
|
|
535
|
+
if (stateDesc.id == 'device_query') {
|
|
542
536
|
if (this.query_device_block.indexOf(deviceId) > -1) {
|
|
543
537
|
this.log.warn(`Device query for '${entity.device.ieeeAddr}' blocked`);
|
|
544
538
|
return;
|
|
@@ -560,18 +554,16 @@ class Zigbee extends utils.Adapter {
|
|
|
560
554
|
}
|
|
561
555
|
this.log.debug(`Device query for '${entity.device.ieeeAddr}' done`);
|
|
562
556
|
const idToRemove = deviceId;
|
|
563
|
-
setTimeout(()
|
|
557
|
+
setTimeout(()=>{
|
|
564
558
|
const idx = this.query_device_block.indexOf(idToRemove);
|
|
565
|
-
if (idx > -1)
|
|
566
|
-
this.query_device_block.splice(idx);
|
|
567
|
-
}
|
|
559
|
+
if (idx > -1) this.query_device_block.splice(idx);
|
|
568
560
|
}, 10000);
|
|
569
561
|
}
|
|
570
562
|
return;
|
|
571
563
|
}
|
|
572
564
|
return;
|
|
573
565
|
}
|
|
574
|
-
const converter = mappedModel.toZigbee.find(c => c && (c.key.includes(stateDesc.prop) || c.key.includes(stateDesc.setattr) || c.key.includes(stateDesc.id)));
|
|
566
|
+
const converter = mappedModel.toZigbee.find((c) => c && (c.key.includes(stateDesc.prop) || c.key.includes(stateDesc.setattr) || c.key.includes(stateDesc.id)));
|
|
575
567
|
if (!converter) {
|
|
576
568
|
this.log.error(`No converter available for '${model}' with key '${stateDesc.id}' `);
|
|
577
569
|
this.sendError(`No converter available for '${model}' with key '${stateDesc.id}' `);
|
|
@@ -582,7 +574,7 @@ class Zigbee extends utils.Adapter {
|
|
|
582
574
|
const preparedOptions = (stateDesc.setterOpt) ? stateDesc.setterOpt(value, options) : {};
|
|
583
575
|
let syncStateList = [];
|
|
584
576
|
if (stateModel && stateModel.syncStates) {
|
|
585
|
-
stateModel.syncStates.forEach(syncFunct => {
|
|
577
|
+
stateModel.syncStates.forEach((syncFunct) => {
|
|
586
578
|
const res = syncFunct(stateDesc, value, options);
|
|
587
579
|
if (res) {
|
|
588
580
|
syncStateList = syncStateList.concat(res);
|
|
@@ -601,14 +593,12 @@ class Zigbee extends utils.Adapter {
|
|
|
601
593
|
target = await this.zbController.resolveEntity(deviceId, epName);
|
|
602
594
|
target = target.endpoint;
|
|
603
595
|
}
|
|
604
|
-
|
|
605
596
|
this.log.debug(`target: ${safeJsonStringify(target)}`);
|
|
606
|
-
|
|
607
597
|
const meta = {
|
|
608
598
|
endpoint_name: epName,
|
|
609
599
|
options: preparedOptions,
|
|
610
600
|
device: entity.device,
|
|
611
|
-
mapped: model
|
|
601
|
+
mapped: (model == 'group') ? [] : mappedModel,
|
|
612
602
|
message: {[key]: preparedValue},
|
|
613
603
|
logger: this.log,
|
|
614
604
|
state: {},
|
|
@@ -620,24 +610,23 @@ class Zigbee extends utils.Adapter {
|
|
|
620
610
|
const result = await converter.convertSet(target, key, preparedValue, meta);
|
|
621
611
|
this.log.debug(`convert result ${safeJsonStringify(result)}`);
|
|
622
612
|
if (result !== undefined) {
|
|
623
|
-
if (stateModel && !isGroup)
|
|
613
|
+
if (stateModel && !isGroup)
|
|
624
614
|
this.acknowledgeState(deviceId, model, stateDesc, value);
|
|
625
|
-
}
|
|
626
615
|
// process sync state list
|
|
627
616
|
this.processSyncStatesList(deviceId, model, syncStateList);
|
|
628
|
-
|
|
629
617
|
if (isGroup) {
|
|
630
618
|
await this.callPluginMethod('queryGroupMemberState', [deviceId, stateDesc]);
|
|
631
619
|
this.acknowledgeState(deviceId, model, stateDesc, value);
|
|
632
620
|
}
|
|
633
621
|
}
|
|
634
|
-
} catch
|
|
635
|
-
this.filterError(`Error ${error.code} on send command to ${deviceId}
|
|
636
|
-
|
|
622
|
+
} catch(error) {
|
|
623
|
+
this.filterError(`Error ${error.code} on send command to ${deviceId}.`+
|
|
624
|
+
` Error: ${error.stack}`, `Send command to ${deviceId} failed with`, error);
|
|
637
625
|
}
|
|
638
626
|
});
|
|
639
627
|
}
|
|
640
|
-
|
|
628
|
+
//
|
|
629
|
+
//
|
|
641
630
|
// This function is introduced to explicitly allow user level scripts to send Commands
|
|
642
631
|
// directly to the zigbee device. It utilizes the zigbee-herdsman-converters to generate
|
|
643
632
|
// the exact zigbee message to be sent and can be used to set device options which are
|
|
@@ -650,79 +639,74 @@ class Zigbee extends utils.Adapter {
|
|
|
650
639
|
// payload: The data to send to the device as JSON object (key/Value pairs)
|
|
651
640
|
// endpoint: optional: the endpoint to send the data to, if supported.
|
|
652
641
|
//
|
|
653
|
-
async
|
|
642
|
+
async SendPayload(payload) {
|
|
654
643
|
this.log.debug(`publishToDevice called with ${safeJsonStringify(payload)}`);
|
|
655
|
-
let
|
|
644
|
+
let payload_obj = {};
|
|
656
645
|
if (typeof payload === 'string') {
|
|
657
646
|
try {
|
|
658
|
-
|
|
647
|
+
payload_obj = JSON.parse();
|
|
659
648
|
} catch (e) {
|
|
660
649
|
this.log.error(`Unable to parse ${safeJsonStringify(payload)}: ${safeJsonStringify(e)}`);
|
|
661
650
|
this.sendError(e, `Unable to parse ${safeJsonStringify(payload)}: ${safeJsonStringify(e)}`);
|
|
662
|
-
return {
|
|
663
|
-
success: false,
|
|
664
|
-
error: `Unable to parse ${safeJsonStringify(payload)}: ${safeJsonStringify(e)}`
|
|
665
|
-
};
|
|
651
|
+
return {success:false, error: `Unable to parse ${safeJsonStringify(payload)}: ${safeJsonStringify(e)}`};
|
|
666
652
|
}
|
|
667
653
|
} else if (typeof payload === 'object') {
|
|
668
|
-
|
|
654
|
+
payload_obj = payload;
|
|
669
655
|
}
|
|
670
|
-
|
|
671
|
-
|
|
656
|
+
if (payload_obj.hasOwnProperty('device') && payload_obj.hasOwnProperty('payload'))
|
|
657
|
+
{
|
|
672
658
|
try {
|
|
673
|
-
const isDevice =
|
|
659
|
+
const isDevice = payload.device.indexOf('group_') == -1;
|
|
674
660
|
const stateList = [];
|
|
675
|
-
const devID = isDevice ? `0x${payload.device}
|
|
661
|
+
const devID = (isDevice ? `0x${payload.device}`:parseInt(payload.device.replace('group_', '')));
|
|
676
662
|
|
|
677
663
|
const entity = await this.zbController.resolveEntity(devID);
|
|
678
664
|
if (!entity) {
|
|
679
|
-
this.log.error(`Device ${safeJsonStringify(
|
|
680
|
-
this.sendError(`Device ${safeJsonStringify(
|
|
681
|
-
return {success: false, error: `Device ${safeJsonStringify(
|
|
665
|
+
this.log.error(`Device ${safeJsonStringify(payload_obj.device)} not found`);
|
|
666
|
+
this.sendError(`Device ${safeJsonStringify(payload_obj.device)} not found`);
|
|
667
|
+
return {success: false, error: `Device ${safeJsonStringify(payload_obj.device)} not found`};
|
|
682
668
|
}
|
|
683
669
|
const mappedModel = entity.mapped;
|
|
684
670
|
if (!mappedModel) {
|
|
685
|
-
this.log.error(`No Model for Device ${safeJsonStringify(
|
|
686
|
-
this.sendError(`No Model for Device ${safeJsonStringify(
|
|
687
|
-
return {success: false, error: `No Model for Device ${safeJsonStringify(
|
|
671
|
+
this.log.error(`No Model for Device ${safeJsonStringify(payload_obj.device)}`);
|
|
672
|
+
this.sendError(`No Model for Device ${safeJsonStringify(payload_obj.device)}`);
|
|
673
|
+
return {success: false, error: `No Model for Device ${safeJsonStringify(payload_obj.device)}`};
|
|
688
674
|
}
|
|
689
|
-
if (typeof
|
|
690
|
-
this.log.error(`Illegal payload type for ${safeJsonStringify(
|
|
691
|
-
this.sendError(`Illegal payload type for ${safeJsonStringify(
|
|
692
|
-
return {success: false, error: `Illegal payload type for ${safeJsonStringify(
|
|
675
|
+
if (typeof payload_obj.payload !== 'object') {
|
|
676
|
+
this.log.error(`Illegal payload type for ${safeJsonStringify(payload_obj.device)}`);
|
|
677
|
+
this.sendError(`Illegal payload type for ${safeJsonStringify(payload_obj.device)}`);
|
|
678
|
+
return {success: false, error: `Illegal payload type for ${safeJsonStringify(payload_obj.device)}`};
|
|
693
679
|
}
|
|
694
|
-
for (const key in
|
|
695
|
-
if (
|
|
696
|
-
const datatype = typeof
|
|
697
|
-
stateList.push({
|
|
698
|
-
|
|
699
|
-
|
|
700
|
-
|
|
701
|
-
|
|
702
|
-
|
|
703
|
-
|
|
704
|
-
},
|
|
705
|
-
value: payloadObj.payload[key],
|
|
706
|
-
index: 0,
|
|
707
|
-
timeout: 0,
|
|
708
|
-
});
|
|
680
|
+
for (const key in payload_obj.payload) {
|
|
681
|
+
if (payload_obj.payload[key] != undefined) {
|
|
682
|
+
const datatype = typeof payload_obj.payload[key];
|
|
683
|
+
stateList.push({stateDesc: {
|
|
684
|
+
id:key,
|
|
685
|
+
prop:key,
|
|
686
|
+
role:'state',
|
|
687
|
+
type:datatype,
|
|
688
|
+
epname:payload_obj.endpoint,
|
|
689
|
+
}, value: payload_obj.payload[key], index:0, timeout:0});
|
|
709
690
|
}
|
|
710
691
|
}
|
|
711
692
|
try {
|
|
712
|
-
this.log.debug(`Calling publish to state for ${safeJsonStringify(
|
|
693
|
+
this.log.debug(`Calling publish to state for ${safeJsonStringify(payload_obj.device)} with ${safeJsonStringify(stateList)}`);
|
|
713
694
|
await this.publishFromState(`0x${payload.device}`, '', undefined, stateList, payload.options);
|
|
714
695
|
return {success: true};
|
|
715
|
-
} catch (error) {
|
|
716
|
-
this.filterError(`Error ${error.code} on send command to ${payload.device}.` +
|
|
717
|
-
` Error: ${error.stack}`, `Send command to ${payload.device} failed with`, error);
|
|
718
|
-
return {success: false, error};
|
|
719
696
|
}
|
|
720
|
-
|
|
721
|
-
|
|
697
|
+
catch (error)
|
|
698
|
+
{
|
|
699
|
+
this.filterError(`Error ${error.code} on send command to ${payload.device}.`+
|
|
700
|
+
` Error: ${error.stack}`, `Send command to ${payload.device} failed with`, error);
|
|
701
|
+
return {success:false, error: error};
|
|
702
|
+
}
|
|
703
|
+
}
|
|
704
|
+
catch (e) {
|
|
705
|
+
return {success:false, error: e};
|
|
722
706
|
}
|
|
723
|
-
}
|
|
724
707
|
|
|
725
|
-
|
|
708
|
+
}
|
|
709
|
+
return {success:false, error: 'missing parameter device or payload in message ' + JSON.stringify(payload)};
|
|
726
710
|
}
|
|
727
711
|
|
|
728
712
|
|
|
@@ -748,7 +732,7 @@ class Zigbee extends utils.Adapter {
|
|
|
748
732
|
this.log.debug(`Leave device event: ${ieeeAddr}`);
|
|
749
733
|
if (ieeeAddr) {
|
|
750
734
|
const devId = ieeeAddr.substr(2);
|
|
751
|
-
this.log.debug(
|
|
735
|
+
this.log.debug('Delete device ' + devId + ' from iobroker.');
|
|
752
736
|
this.stController.deleteDeviceStates(devId);
|
|
753
737
|
}
|
|
754
738
|
}
|
|
@@ -772,7 +756,7 @@ class Zigbee extends utils.Adapter {
|
|
|
772
756
|
}
|
|
773
757
|
}
|
|
774
758
|
}
|
|
775
|
-
|
|
759
|
+
|
|
776
760
|
/**
|
|
777
761
|
* @param {() => void} callback
|
|
778
762
|
*/
|
|
@@ -857,7 +841,7 @@ class Zigbee extends utils.Adapter {
|
|
|
857
841
|
}
|
|
858
842
|
if (data === 0) {
|
|
859
843
|
// set pairing mode off
|
|
860
|
-
this.setState('info.pairingMode', false,
|
|
844
|
+
this.setState('info.pairingMode', false,true);
|
|
861
845
|
_pairingMode = false;
|
|
862
846
|
}
|
|
863
847
|
if (data) {
|
|
@@ -872,7 +856,7 @@ class Zigbee extends utils.Adapter {
|
|
|
872
856
|
}
|
|
873
857
|
|
|
874
858
|
expandFileName(fn) {
|
|
875
|
-
|
|
859
|
+
return path.join(utils.getAbsoluteInstanceDataDir(this), fn);
|
|
876
860
|
}
|
|
877
861
|
|
|
878
862
|
onLog(level, msg, data) {
|
|
@@ -883,8 +867,8 @@ class Zigbee extends utils.Adapter {
|
|
|
883
867
|
logger = this.log.error;
|
|
884
868
|
if (data)
|
|
885
869
|
data = data.toString();
|
|
886
|
-
this.logToPairing(
|
|
887
|
-
this.sendError(
|
|
870
|
+
this.logToPairing('Error: ' + msg + '. ' + data, true);
|
|
871
|
+
this.sendError('Error: ' + msg + '. ' + data);
|
|
888
872
|
break;
|
|
889
873
|
case 'debug':
|
|
890
874
|
logger = this.log.debug;
|
|
@@ -898,9 +882,9 @@ class Zigbee extends utils.Adapter {
|
|
|
898
882
|
}
|
|
899
883
|
if (data) {
|
|
900
884
|
if (typeof data === 'string') {
|
|
901
|
-
logger(
|
|
885
|
+
logger(msg + '. ' + data);
|
|
902
886
|
} else {
|
|
903
|
-
logger(
|
|
887
|
+
logger(msg + '. ' + safeJsonStringify(data));
|
|
904
888
|
}
|
|
905
889
|
} else {
|
|
906
890
|
logger(msg);
|