iobroker.zigbee 1.8.0 → 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 +32 -9
- 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 +19 -26
- package/lib/backup.js +2 -2
- package/lib/binding.js +24 -23
- package/lib/colors.js +14 -16
- package/lib/commands.js +82 -89
- package/lib/developer.js +7 -6
- package/lib/devices.js +153 -144
- package/lib/exclude.js +36 -30
- package/lib/exposes.js +111 -106
- package/lib/groups.js +54 -53
- package/lib/json.js +4 -3
- package/lib/networkmap.js +2 -2
- package/lib/ota.js +15 -23
- package/lib/rgb.js +44 -47
- package/lib/seriallist.js +16 -21
- package/lib/states.js +496 -482
- package/lib/statescontroller.js +164 -170
- package/lib/utils.js +21 -22
- package/lib/zbBaseExtension.js +4 -4
- package/lib/zbDelayedAction.js +13 -5
- package/lib/zbDeviceAvailability.js +44 -47
- package/lib/zbDeviceConfigure.js +19 -7
- package/lib/zbDeviceEvent.js +4 -3
- package/lib/zigbeecontroller.js +96 -88
- package/main.js +133 -149
- 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 =
|
|
138
|
+
if (error.code === undefined)
|
|
139
|
+
{
|
|
140
|
+
let em = error.stack.match(/failed \((.+?)\) at/);
|
|
141
|
+
if (!em) em = error.stack.match(/failed \((.+?)\)/);
|
|
141
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,7 +164,6 @@ 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
|
|
|
@@ -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'];
|
|
@@ -258,16 +252,16 @@ class Zigbee extends utils.Adapter {
|
|
|
258
252
|
// installed version
|
|
259
253
|
let gitVers = '';
|
|
260
254
|
try {
|
|
261
|
-
this.log.info(
|
|
255
|
+
this.log.info('Starting Zigbee ' + debugversion);
|
|
262
256
|
|
|
263
|
-
await this.getForeignObject(
|
|
257
|
+
await this.getForeignObject('system.adapter.' + this.namespace, (err, obj) => {
|
|
264
258
|
if (!err && obj && obj.common.installedFrom && obj.common.installedFrom.includes('://')) {
|
|
265
259
|
const instFrom = obj.common.installedFrom;
|
|
266
|
-
gitVers = gitVers + instFrom.replace('tarball',
|
|
260
|
+
gitVers = gitVers + instFrom.replace('tarball','commit');
|
|
267
261
|
} else {
|
|
268
262
|
gitVers = obj.common.installedFrom;
|
|
269
263
|
}
|
|
270
|
-
this.log.info(
|
|
264
|
+
this.log.info('Installed Version: ' + gitVers );
|
|
271
265
|
});
|
|
272
266
|
|
|
273
267
|
await this.zbController.start();
|
|
@@ -280,7 +274,6 @@ class Zigbee extends utils.Adapter {
|
|
|
280
274
|
this.log.error(error);
|
|
281
275
|
}
|
|
282
276
|
this.sendError(error, `Failed to start Zigbee`);
|
|
283
|
-
|
|
284
277
|
if (this.reconnectCounter > 0) {
|
|
285
278
|
this.tryToReconnect();
|
|
286
279
|
}
|
|
@@ -298,10 +291,10 @@ class Zigbee extends utils.Adapter {
|
|
|
298
291
|
|
|
299
292
|
tryToReconnect() {
|
|
300
293
|
this.reconnectTimer = setTimeout(()=>{
|
|
301
|
-
if (this.config.port.
|
|
302
|
-
// Controller connect though
|
|
303
|
-
// Unlikely USB dongle, connection broken may only
|
|
304
|
-
//
|
|
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
|
|
305
298
|
// AP issue or Zigbee gateway power is turned off unexpectedly.
|
|
306
299
|
// So try to reconnect gateway every 10 seconds all the time.
|
|
307
300
|
this.log.info(`Try to reconnect.`);
|
|
@@ -310,7 +303,7 @@ class Zigbee extends utils.Adapter {
|
|
|
310
303
|
this.reconnectCounter -= 1;
|
|
311
304
|
}
|
|
312
305
|
this.doConnect();
|
|
313
|
-
}, 10
|
|
306
|
+
}, 10*1000); // every 10 seconds
|
|
314
307
|
}
|
|
315
308
|
|
|
316
309
|
async onZigbeeAdapterReady() {
|
|
@@ -319,13 +312,13 @@ class Zigbee extends utils.Adapter {
|
|
|
319
312
|
// https://github.com/ioBroker/ioBroker.zigbee/issues/668
|
|
320
313
|
const extPanIdFix = this.config.extPanIdFix ? this.config.extPanIdFix : false;
|
|
321
314
|
if (!extPanIdFix) {
|
|
322
|
-
const configExtPanId = this.config.extPanID ? '0x'
|
|
315
|
+
const configExtPanId = this.config.extPanID ? '0x'+this.config.extPanID.toLowerCase() : '0xdddddddddddddddd';
|
|
323
316
|
let networkExtPanId = (await this.zbController.herdsman.getNetworkParameters()).extendedPanID;
|
|
324
317
|
let needChange = false;
|
|
325
318
|
this.log.debug(`Config value ${configExtPanId} : Network value ${networkExtPanId}`);
|
|
326
319
|
const adapterType = this.config.adapterType || 'zstack';
|
|
327
320
|
if (adapterType === 'zstack') {
|
|
328
|
-
if (configExtPanId
|
|
321
|
+
if (configExtPanId != networkExtPanId) {
|
|
329
322
|
try {
|
|
330
323
|
// try to read from nvram
|
|
331
324
|
const result = await this.zbController.herdsman.adapter.znp.request(
|
|
@@ -341,9 +334,9 @@ class Zigbee extends utils.Adapter {
|
|
|
341
334
|
2, // ZnpCommandStatus.INVALID_PARAM
|
|
342
335
|
]
|
|
343
336
|
);
|
|
344
|
-
const nwExtPanId = '0x'
|
|
337
|
+
const nwExtPanId = '0x'+result.payload.value.reverse().toString('hex');
|
|
345
338
|
this.log.debug(`Config value ${configExtPanId} : nw value ${nwExtPanId}`);
|
|
346
|
-
if (configExtPanId
|
|
339
|
+
if (configExtPanId != nwExtPanId) {
|
|
347
340
|
networkExtPanId = nwExtPanId;
|
|
348
341
|
needChange = true;
|
|
349
342
|
}
|
|
@@ -373,36 +366,37 @@ class Zigbee extends utils.Adapter {
|
|
|
373
366
|
for (const device of devicesFromDB) {
|
|
374
367
|
const entity = await this.zbController.resolveEntity(device);
|
|
375
368
|
if (entity) {
|
|
376
|
-
const model = entity.mapped ? entity.mapped.model : entity.device.modelID;
|
|
377
|
-
this.stController.updateDev(device.ieeeAddr.substr(2), model, model, () =>
|
|
378
|
-
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
|
+
});
|
|
379
373
|
}
|
|
380
374
|
}
|
|
381
375
|
await this.callPluginMethod('start', [this.zbController, this.stController]);
|
|
382
376
|
}
|
|
383
377
|
|
|
384
378
|
async checkIfModelUpdate(entity) {
|
|
385
|
-
const model = entity.mapped ? entity.mapped.model : entity.device.modelID,
|
|
379
|
+
const model = (entity.mapped) ? entity.mapped.model : entity.device.modelID,
|
|
386
380
|
device = entity.device,
|
|
387
381
|
devId = device.ieeeAddr.substr(2);
|
|
388
|
-
|
|
389
382
|
return new Promise((resolve) => {
|
|
390
383
|
this.getObject(devId, (err, obj) => {
|
|
391
|
-
if (obj && obj.common.type
|
|
384
|
+
if (obj && obj.common.type != model) {
|
|
392
385
|
// let's change model
|
|
393
386
|
this.getStatesOf(devId, (err, states) => {
|
|
394
387
|
if (!err && states) {
|
|
395
388
|
const chain = [];
|
|
396
|
-
states.forEach((state) =>
|
|
397
|
-
chain.push(this.deleteStateAsync(devId, null, state._id))
|
|
398
|
-
|
|
399
|
-
Promise.all(chain)
|
|
400
|
-
.
|
|
401
|
-
this.stController.
|
|
402
|
-
this.stController.
|
|
403
|
-
|
|
404
|
-
|
|
405
|
-
|
|
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
|
+
});
|
|
406
400
|
} else {
|
|
407
401
|
resolve();
|
|
408
402
|
}
|
|
@@ -418,14 +412,13 @@ class Zigbee extends utils.Adapter {
|
|
|
418
412
|
|
|
419
413
|
async onZigbeeEvent(type, entity, message){
|
|
420
414
|
this.log.debug(`Type ${type} device ${safeJsonStringify(entity)} incoming event: ${safeJsonStringify(message)}`);
|
|
421
|
-
const device = entity.device
|
|
422
|
-
|
|
423
|
-
|
|
424
|
-
|
|
425
|
-
|
|
426
|
-
|
|
427
|
-
|
|
428
|
-
// this assigment give possibility to use iobroker logger in code of the converters, via meta.logger
|
|
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
|
|
429
422
|
meta.logger = this.log;
|
|
430
423
|
|
|
431
424
|
await this.checkIfModelUpdate(entity);
|
|
@@ -446,14 +439,12 @@ class Zigbee extends utils.Adapter {
|
|
|
446
439
|
}
|
|
447
440
|
let converters = mappedModel.fromZigbee.filter(c => c && c.cluster === cluster && (
|
|
448
441
|
(c.type instanceof Array) ? c.type.includes(type) : c.type === type));
|
|
449
|
-
|
|
450
442
|
if (!converters.length && type === 'readResponse') {
|
|
451
443
|
converters = mappedModel.fromZigbee.filter(c => c.cluster === cluster && (
|
|
452
444
|
(c.type instanceof Array) ? c.type.includes('attributeReport') : c.type === 'attributeReport'));
|
|
453
445
|
}
|
|
454
|
-
|
|
455
446
|
if (!converters.length) {
|
|
456
|
-
if (type
|
|
447
|
+
if (type != 'readResponse') {
|
|
457
448
|
this.log.debug(
|
|
458
449
|
`No converter available for '${mappedModel.model}' with cluster '${cluster}' and type '${type}'`
|
|
459
450
|
);
|
|
@@ -485,10 +476,10 @@ class Zigbee extends utils.Adapter {
|
|
|
485
476
|
|
|
486
477
|
acknowledgeState(deviceId, model, stateDesc, value) {
|
|
487
478
|
if (model === 'group') {
|
|
488
|
-
const stateId =
|
|
479
|
+
const stateId = this.namespace + '.group_' + deviceId + '.' + stateDesc.id;
|
|
489
480
|
this.setState(stateId, value, true);
|
|
490
481
|
} else {
|
|
491
|
-
const stateId =
|
|
482
|
+
const stateId = this.namespace + '.' + deviceId.replace('0x', '') + '.' + stateDesc.id;
|
|
492
483
|
this.setState(stateId, value, true);
|
|
493
484
|
}
|
|
494
485
|
}
|
|
@@ -501,7 +492,7 @@ class Zigbee extends utils.Adapter {
|
|
|
501
492
|
|
|
502
493
|
async publishFromState(deviceId, model, stateModel, stateList, options){
|
|
503
494
|
let isGroup = false;
|
|
504
|
-
if (model
|
|
495
|
+
if (model == 'group') {
|
|
505
496
|
isGroup = true;
|
|
506
497
|
deviceId = parseInt(deviceId);
|
|
507
498
|
}
|
|
@@ -512,17 +503,17 @@ class Zigbee extends utils.Adapter {
|
|
|
512
503
|
this.log.debug(`No mapped model for ${model}`);
|
|
513
504
|
return;
|
|
514
505
|
}
|
|
515
|
-
this.log.debug(
|
|
506
|
+
this.log.debug('Mapped Model: ' + JSON.stringify(mappedModel));
|
|
516
507
|
|
|
517
|
-
stateList.forEach(async
|
|
508
|
+
stateList.forEach(async(changedState) => {
|
|
518
509
|
const stateDesc = changedState.stateDesc;
|
|
519
510
|
const value = changedState.value;
|
|
520
511
|
|
|
521
|
-
if (stateDesc.id
|
|
512
|
+
if (stateDesc.id == 'send_payload') {
|
|
522
513
|
try {
|
|
523
514
|
const json_value = JSON.parse(value);
|
|
524
|
-
const payload = {device:
|
|
525
|
-
const result = await(this.
|
|
515
|
+
const payload = { device:deviceId.replace('0x', ''), payload:json_value };
|
|
516
|
+
const result = await(this.SendPayload(payload));
|
|
526
517
|
if (result.hasOwnProperty('success') && result.success) {
|
|
527
518
|
this.acknowledgeState(deviceId, model, stateDesc, value);
|
|
528
519
|
}
|
|
@@ -541,7 +532,7 @@ class Zigbee extends utils.Adapter {
|
|
|
541
532
|
// if this is the device query state => trigger the device query
|
|
542
533
|
|
|
543
534
|
// on activation of the 'device_query' state trigger hardware query where possible
|
|
544
|
-
if (stateDesc.id
|
|
535
|
+
if (stateDesc.id == 'device_query') {
|
|
545
536
|
if (this.query_device_block.indexOf(deviceId) > -1) {
|
|
546
537
|
this.log.warn(`Device query for '${entity.device.ieeeAddr}' blocked`);
|
|
547
538
|
return;
|
|
@@ -563,18 +554,16 @@ class Zigbee extends utils.Adapter {
|
|
|
563
554
|
}
|
|
564
555
|
this.log.debug(`Device query for '${entity.device.ieeeAddr}' done`);
|
|
565
556
|
const idToRemove = deviceId;
|
|
566
|
-
setTimeout(()
|
|
557
|
+
setTimeout(()=>{
|
|
567
558
|
const idx = this.query_device_block.indexOf(idToRemove);
|
|
568
|
-
if (idx > -1)
|
|
569
|
-
this.query_device_block.splice(idx);
|
|
570
|
-
}
|
|
559
|
+
if (idx > -1) this.query_device_block.splice(idx);
|
|
571
560
|
}, 10000);
|
|
572
561
|
}
|
|
573
562
|
return;
|
|
574
563
|
}
|
|
575
564
|
return;
|
|
576
565
|
}
|
|
577
|
-
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)));
|
|
578
567
|
if (!converter) {
|
|
579
568
|
this.log.error(`No converter available for '${model}' with key '${stateDesc.id}' `);
|
|
580
569
|
this.sendError(`No converter available for '${model}' with key '${stateDesc.id}' `);
|
|
@@ -585,7 +574,7 @@ class Zigbee extends utils.Adapter {
|
|
|
585
574
|
const preparedOptions = (stateDesc.setterOpt) ? stateDesc.setterOpt(value, options) : {};
|
|
586
575
|
let syncStateList = [];
|
|
587
576
|
if (stateModel && stateModel.syncStates) {
|
|
588
|
-
stateModel.syncStates.forEach(syncFunct => {
|
|
577
|
+
stateModel.syncStates.forEach((syncFunct) => {
|
|
589
578
|
const res = syncFunct(stateDesc, value, options);
|
|
590
579
|
if (res) {
|
|
591
580
|
syncStateList = syncStateList.concat(res);
|
|
@@ -604,14 +593,12 @@ class Zigbee extends utils.Adapter {
|
|
|
604
593
|
target = await this.zbController.resolveEntity(deviceId, epName);
|
|
605
594
|
target = target.endpoint;
|
|
606
595
|
}
|
|
607
|
-
|
|
608
596
|
this.log.debug(`target: ${safeJsonStringify(target)}`);
|
|
609
|
-
|
|
610
597
|
const meta = {
|
|
611
598
|
endpoint_name: epName,
|
|
612
599
|
options: preparedOptions,
|
|
613
600
|
device: entity.device,
|
|
614
|
-
mapped: model
|
|
601
|
+
mapped: (model == 'group') ? [] : mappedModel,
|
|
615
602
|
message: {[key]: preparedValue},
|
|
616
603
|
logger: this.log,
|
|
617
604
|
state: {},
|
|
@@ -623,24 +610,23 @@ class Zigbee extends utils.Adapter {
|
|
|
623
610
|
const result = await converter.convertSet(target, key, preparedValue, meta);
|
|
624
611
|
this.log.debug(`convert result ${safeJsonStringify(result)}`);
|
|
625
612
|
if (result !== undefined) {
|
|
626
|
-
if (stateModel && !isGroup)
|
|
613
|
+
if (stateModel && !isGroup)
|
|
627
614
|
this.acknowledgeState(deviceId, model, stateDesc, value);
|
|
628
|
-
}
|
|
629
615
|
// process sync state list
|
|
630
616
|
this.processSyncStatesList(deviceId, model, syncStateList);
|
|
631
|
-
|
|
632
617
|
if (isGroup) {
|
|
633
618
|
await this.callPluginMethod('queryGroupMemberState', [deviceId, stateDesc]);
|
|
634
619
|
this.acknowledgeState(deviceId, model, stateDesc, value);
|
|
635
620
|
}
|
|
636
621
|
}
|
|
637
|
-
} catch
|
|
622
|
+
} catch(error) {
|
|
638
623
|
this.filterError(`Error ${error.code} on send command to ${deviceId}.`+
|
|
639
624
|
` Error: ${error.stack}`, `Send command to ${deviceId} failed with`, error);
|
|
640
625
|
}
|
|
641
626
|
});
|
|
642
627
|
}
|
|
643
|
-
|
|
628
|
+
//
|
|
629
|
+
//
|
|
644
630
|
// This function is introduced to explicitly allow user level scripts to send Commands
|
|
645
631
|
// directly to the zigbee device. It utilizes the zigbee-herdsman-converters to generate
|
|
646
632
|
// the exact zigbee message to be sent and can be used to set device options which are
|
|
@@ -653,76 +639,74 @@ class Zigbee extends utils.Adapter {
|
|
|
653
639
|
// payload: The data to send to the device as JSON object (key/Value pairs)
|
|
654
640
|
// endpoint: optional: the endpoint to send the data to, if supported.
|
|
655
641
|
//
|
|
656
|
-
async
|
|
642
|
+
async SendPayload(payload) {
|
|
657
643
|
this.log.debug(`publishToDevice called with ${safeJsonStringify(payload)}`);
|
|
658
|
-
let
|
|
644
|
+
let payload_obj = {};
|
|
659
645
|
if (typeof payload === 'string') {
|
|
660
646
|
try {
|
|
661
|
-
|
|
647
|
+
payload_obj = JSON.parse();
|
|
662
648
|
} catch (e) {
|
|
663
649
|
this.log.error(`Unable to parse ${safeJsonStringify(payload)}: ${safeJsonStringify(e)}`);
|
|
664
650
|
this.sendError(e, `Unable to parse ${safeJsonStringify(payload)}: ${safeJsonStringify(e)}`);
|
|
665
|
-
return {success:
|
|
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}`:parseInt(payload.device.replace('group_', ''));
|
|
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
|
-
}
|
|
696
|
+
}
|
|
697
|
+
catch (error)
|
|
698
|
+
{
|
|
716
699
|
this.filterError(`Error ${error.code} on send command to ${payload.device}.`+
|
|
717
700
|
` Error: ${error.stack}`, `Send command to ${payload.device} failed with`, error);
|
|
718
|
-
return {success:
|
|
701
|
+
return {success:false, error: error};
|
|
719
702
|
}
|
|
720
|
-
} catch (e) {
|
|
721
|
-
return {success: false, error: e};
|
|
722
703
|
}
|
|
723
|
-
|
|
704
|
+
catch (e) {
|
|
705
|
+
return {success:false, error: e};
|
|
706
|
+
}
|
|
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
|
*/
|
|
@@ -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);
|