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