iobroker.zigbee 1.8.3 → 1.8.7
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 +6 -0
- package/admin/adapter-settings.js +244 -0
- package/admin/admin.js +520 -494
- package/admin/index_m.html +1171 -1001
- package/admin/tab_m.html +44 -2
- 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 +27 -0
- 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 +30 -0
- package/docs/flashing_via_arduino_(en).md +110 -0
- 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 +28 -0
- 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/io-package.json +34 -33
- package/lib/backup.js +2 -2
- package/lib/binding.js +32 -37
- package/lib/colors.js +163 -158
- package/lib/commands.js +100 -91
- package/lib/developer.js +9 -12
- package/lib/devices.js +168 -178
- package/lib/exclude.js +30 -36
- package/lib/exposes.js +168 -143
- package/lib/groups.js +81 -83
- package/lib/json.js +5 -6
- package/lib/networkmap.js +2 -3
- package/lib/ota.js +34 -18
- package/lib/rgb.js +114 -72
- package/lib/seriallist.js +25 -20
- package/lib/statescontroller.js +206 -183
- package/lib/utils.js +29 -23
- package/lib/zbBaseExtension.js +4 -4
- package/lib/zbDelayedAction.js +5 -13
- package/lib/zbDeviceAvailability.js +69 -65
- package/lib/zbDeviceConfigure.js +9 -21
- package/lib/zbDeviceEvent.js +3 -4
- package/lib/zigbeecontroller.js +133 -128
- package/main.js +169 -154
- package/package.json +28 -14
- package/.eslintignore +0 -2
- package/.eslintrc.json +0 -37
- package/.github/FUNDING.yml +0 -3
- package/.github/auto-merge.yml +0 -17
- package/.github/dependabot.yml +0 -24
- package/.github/stale.yml +0 -13
- package/.github/workflows/codeql.yml +0 -41
- package/.github/workflows/dependabot-automerge.yml +0 -22
- package/.github/workflows/test-and-release.yml +0 -149
- package/.releaseconfig.json +0 -3
- package/.travis/wiki.sh +0 -28
- package/.travis.yml +0 -41
- package/gulpfile.js +0 -464
- package/test/integration.js +0 -5
- package/test/mocha.custom.opts +0 -2
- package/test/mocha.setup.js +0 -14
- package/test/package.js +0 -5
- package/test/unit.js +0 -5
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={}]
|
|
@@ -65,9 +64,9 @@ class Zigbee extends utils.Adapter {
|
|
|
65
64
|
name: 'zigbee',
|
|
66
65
|
systemConfig: true,
|
|
67
66
|
}));
|
|
68
|
-
this.on('ready', this.onReady
|
|
69
|
-
this.on('unload', this.onUnload
|
|
70
|
-
this.on('message', this.onMessage
|
|
67
|
+
this.on('ready', () => this.onReady());
|
|
68
|
+
this.on('unload', callback => this.onUnload(callback));
|
|
69
|
+
this.on('message', obj => this.onMessage(obj));
|
|
71
70
|
|
|
72
71
|
this.query_device_block = [];
|
|
73
72
|
|
|
@@ -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
|
-
|
|
142
|
-
this.log.error(`${message} no error code (${(em ? em[1]:'undefined')})`);
|
|
138
|
+
if (error.code === undefined) {
|
|
139
|
+
let em = error.stack.match(/failed \((.+?)\) at/);
|
|
140
|
+
em = em || error.stack.match(/failed \((.+?)\)/);
|
|
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,10 +168,11 @@ 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
|
|
|
170
|
-
debugLog
|
|
175
|
+
debugLog(data, ...args) {
|
|
171
176
|
const message = (args) ? util.format(data, ...args) : data;
|
|
172
177
|
this.log.debug(message.slice(message.indexOf('zigbee-herdsman')));
|
|
173
178
|
}
|
|
@@ -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'];
|
|
@@ -244,24 +250,23 @@ class Zigbee extends utils.Adapter {
|
|
|
244
250
|
try {
|
|
245
251
|
const DebugIdentify = require('./debugidentify');
|
|
246
252
|
debugversion = DebugIdentify.ReportIdentifier();
|
|
247
|
-
}
|
|
248
|
-
catch {
|
|
253
|
+
} catch {
|
|
249
254
|
debugversion = ' npm ...';
|
|
250
255
|
}
|
|
251
256
|
|
|
252
257
|
// installed version
|
|
253
258
|
let gitVers = '';
|
|
254
259
|
try {
|
|
255
|
-
this.log.info(
|
|
260
|
+
this.log.info(`Starting Zigbee ${debugversion}`);
|
|
256
261
|
|
|
257
|
-
await this.getForeignObject(
|
|
262
|
+
await this.getForeignObject(`system.adapter.${this.namespace}`, (err, obj) => {
|
|
258
263
|
if (!err && obj && obj.common.installedFrom && obj.common.installedFrom.includes('://')) {
|
|
259
264
|
const instFrom = obj.common.installedFrom;
|
|
260
|
-
gitVers = gitVers + instFrom.replace('tarball','commit');
|
|
265
|
+
gitVers = gitVers + instFrom.replace('tarball', 'commit');
|
|
261
266
|
} else {
|
|
262
267
|
gitVers = obj.common.installedFrom;
|
|
263
268
|
}
|
|
264
|
-
this.log.info(
|
|
269
|
+
this.log.info(`Installed Version: ${gitVers}`);
|
|
265
270
|
});
|
|
266
271
|
|
|
267
272
|
await this.zbController.start();
|
|
@@ -274,6 +279,7 @@ class Zigbee extends utils.Adapter {
|
|
|
274
279
|
this.log.error(error);
|
|
275
280
|
}
|
|
276
281
|
this.sendError(error, `Failed to start Zigbee`);
|
|
282
|
+
|
|
277
283
|
if (this.reconnectCounter > 0) {
|
|
278
284
|
this.tryToReconnect();
|
|
279
285
|
}
|
|
@@ -290,11 +296,11 @@ class Zigbee extends utils.Adapter {
|
|
|
290
296
|
}
|
|
291
297
|
|
|
292
298
|
tryToReconnect() {
|
|
293
|
-
this.reconnectTimer = setTimeout(()=>{
|
|
294
|
-
if (this.config.port.
|
|
295
|
-
// Controller connect though
|
|
296
|
-
// Unlikely USB dongle, connection broken may only
|
|
297
|
-
//
|
|
299
|
+
this.reconnectTimer = setTimeout(() => {
|
|
300
|
+
if (this.config.port.includes('tcp://')) {
|
|
301
|
+
// Controller connect though Wi-Fi.
|
|
302
|
+
// Unlikely USB dongle, connection broken may only cause user unplugged the dongle,
|
|
303
|
+
// Wi-Fi connected gateway is possible that device connection is broken caused by
|
|
298
304
|
// AP issue or Zigbee gateway power is turned off unexpectedly.
|
|
299
305
|
// So try to reconnect gateway every 10 seconds all the time.
|
|
300
306
|
this.log.info(`Try to reconnect.`);
|
|
@@ -303,22 +309,22 @@ class Zigbee extends utils.Adapter {
|
|
|
303
309
|
this.reconnectCounter -= 1;
|
|
304
310
|
}
|
|
305
311
|
this.doConnect();
|
|
306
|
-
}, 10*1000); // every 10 seconds
|
|
312
|
+
}, 10 * 1000); // every 10 seconds
|
|
307
313
|
}
|
|
308
314
|
|
|
309
315
|
async onZigbeeAdapterReady() {
|
|
310
|
-
|
|
316
|
+
this.reconnectTimer && clearTimeout(this.reconnectTimer);
|
|
311
317
|
this.log.info(`Zigbee started`);
|
|
312
318
|
// https://github.com/ioBroker/ioBroker.zigbee/issues/668
|
|
313
319
|
const extPanIdFix = this.config.extPanIdFix ? this.config.extPanIdFix : false;
|
|
314
320
|
if (!extPanIdFix) {
|
|
315
|
-
const configExtPanId = this.config.extPanID ?
|
|
321
|
+
const configExtPanId = this.config.extPanID ? `0x${this.config.extPanID.toLowerCase()}` : '0xdddddddddddddddd';
|
|
316
322
|
let networkExtPanId = (await this.zbController.herdsman.getNetworkParameters()).extendedPanID;
|
|
317
323
|
let needChange = false;
|
|
318
324
|
this.log.debug(`Config value ${configExtPanId} : Network value ${networkExtPanId}`);
|
|
319
325
|
const adapterType = this.config.adapterType || 'zstack';
|
|
320
326
|
if (adapterType === 'zstack') {
|
|
321
|
-
if (configExtPanId
|
|
327
|
+
if (configExtPanId !== networkExtPanId) {
|
|
322
328
|
try {
|
|
323
329
|
// try to read from nvram
|
|
324
330
|
const result = await this.zbController.herdsman.adapter.znp.request(
|
|
@@ -334,9 +340,9 @@ class Zigbee extends utils.Adapter {
|
|
|
334
340
|
2, // ZnpCommandStatus.INVALID_PARAM
|
|
335
341
|
]
|
|
336
342
|
);
|
|
337
|
-
const nwExtPanId =
|
|
343
|
+
const nwExtPanId = `0x${result.payload.value.reverse().toString('hex')}`;
|
|
338
344
|
this.log.debug(`Config value ${configExtPanId} : nw value ${nwExtPanId}`);
|
|
339
|
-
if (configExtPanId
|
|
345
|
+
if (configExtPanId !== nwExtPanId) {
|
|
340
346
|
networkExtPanId = nwExtPanId;
|
|
341
347
|
needChange = true;
|
|
342
348
|
}
|
|
@@ -366,37 +372,36 @@ class Zigbee extends utils.Adapter {
|
|
|
366
372
|
for (const device of devicesFromDB) {
|
|
367
373
|
const entity = await this.zbController.resolveEntity(device);
|
|
368
374
|
if (entity) {
|
|
369
|
-
const model =
|
|
370
|
-
this.stController.updateDev(device.ieeeAddr.substr(2), model, model, () =>
|
|
371
|
-
this.stController.syncDevStates(device, model);
|
|
372
|
-
});
|
|
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));
|
|
373
378
|
}
|
|
374
379
|
}
|
|
375
380
|
await this.callPluginMethod('start', [this.zbController, this.stController]);
|
|
376
381
|
}
|
|
377
382
|
|
|
378
383
|
async checkIfModelUpdate(entity) {
|
|
379
|
-
const model =
|
|
384
|
+
const model = entity.mapped ? entity.mapped.model : entity.device.modelID,
|
|
380
385
|
device = entity.device,
|
|
381
386
|
devId = device.ieeeAddr.substr(2);
|
|
387
|
+
|
|
382
388
|
return new Promise((resolve) => {
|
|
383
389
|
this.getObject(devId, (err, obj) => {
|
|
384
|
-
if (obj && obj.common.type
|
|
390
|
+
if (obj && obj.common.type !== model) {
|
|
385
391
|
// let's change model
|
|
386
392
|
this.getStatesOf(devId, (err, states) => {
|
|
387
393
|
if (!err && states) {
|
|
388
394
|
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
|
-
});
|
|
395
|
+
states.forEach((state) =>
|
|
396
|
+
chain.push(this.deleteStateAsync(devId, null, state._id)));
|
|
397
|
+
|
|
398
|
+
Promise.all(chain)
|
|
399
|
+
.then(() =>
|
|
400
|
+
this.stController.deleteDeviceStates(devId, () =>
|
|
401
|
+
this.stController.updateDev(devId, model, model, async () => {
|
|
402
|
+
await this.stController.syncDevStates(device, model);
|
|
403
|
+
resolve();
|
|
404
|
+
})));
|
|
400
405
|
} else {
|
|
401
406
|
resolve();
|
|
402
407
|
}
|
|
@@ -408,17 +413,16 @@ class Zigbee extends utils.Adapter {
|
|
|
408
413
|
});
|
|
409
414
|
}
|
|
410
415
|
|
|
411
|
-
|
|
412
|
-
|
|
413
|
-
async onZigbeeEvent(type, entity, message){
|
|
416
|
+
async onZigbeeEvent(type, entity, message) {
|
|
414
417
|
this.log.debug(`Type ${type} device ${safeJsonStringify(entity)} incoming event: ${safeJsonStringify(message)}`);
|
|
415
|
-
const device = entity.device
|
|
416
|
-
|
|
417
|
-
|
|
418
|
-
|
|
419
|
-
|
|
420
|
-
|
|
421
|
-
|
|
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
|
+
|
|
425
|
+
// this assigment give possibility to use iobroker logger in code of the converters, via meta.logger
|
|
422
426
|
meta.logger = this.log;
|
|
423
427
|
|
|
424
428
|
await this.checkIfModelUpdate(entity);
|
|
@@ -439,12 +443,14 @@ class Zigbee extends utils.Adapter {
|
|
|
439
443
|
}
|
|
440
444
|
let converters = mappedModel.fromZigbee.filter(c => c && c.cluster === cluster && (
|
|
441
445
|
(c.type instanceof Array) ? c.type.includes(type) : c.type === type));
|
|
446
|
+
|
|
442
447
|
if (!converters.length && type === 'readResponse') {
|
|
443
448
|
converters = mappedModel.fromZigbee.filter(c => c.cluster === cluster && (
|
|
444
449
|
(c.type instanceof Array) ? c.type.includes('attributeReport') : c.type === 'attributeReport'));
|
|
445
450
|
}
|
|
451
|
+
|
|
446
452
|
if (!converters.length) {
|
|
447
|
-
if (type
|
|
453
|
+
if (type !== 'readResponse') {
|
|
448
454
|
this.log.debug(
|
|
449
455
|
`No converter available for '${mappedModel.model}' with cluster '${cluster}' and type '${type}'`
|
|
450
456
|
);
|
|
@@ -476,10 +482,10 @@ class Zigbee extends utils.Adapter {
|
|
|
476
482
|
|
|
477
483
|
acknowledgeState(deviceId, model, stateDesc, value) {
|
|
478
484
|
if (model === 'group') {
|
|
479
|
-
const stateId = this.namespace
|
|
485
|
+
const stateId = `${this.namespace}.group_${deviceId}.${stateDesc.id}`;
|
|
480
486
|
this.setState(stateId, value, true);
|
|
481
487
|
} else {
|
|
482
|
-
const stateId = this.namespace
|
|
488
|
+
const stateId = `${this.namespace}.${deviceId.replace('0x', '')}.${stateDesc.id}`;
|
|
483
489
|
this.setState(stateId, value, true);
|
|
484
490
|
}
|
|
485
491
|
}
|
|
@@ -490,9 +496,9 @@ class Zigbee extends utils.Adapter {
|
|
|
490
496
|
});
|
|
491
497
|
}
|
|
492
498
|
|
|
493
|
-
async publishFromState(deviceId, model, stateModel, stateList, options){
|
|
499
|
+
async publishFromState(deviceId, model, stateModel, stateList, options) {
|
|
494
500
|
let isGroup = false;
|
|
495
|
-
if (model
|
|
501
|
+
if (model === 'group') {
|
|
496
502
|
isGroup = true;
|
|
497
503
|
deviceId = parseInt(deviceId);
|
|
498
504
|
}
|
|
@@ -503,17 +509,17 @@ class Zigbee extends utils.Adapter {
|
|
|
503
509
|
this.log.debug(`No mapped model for ${model}`);
|
|
504
510
|
return;
|
|
505
511
|
}
|
|
506
|
-
this.log.debug(
|
|
512
|
+
this.log.debug(`Mapped Model: ${JSON.stringify(mappedModel)}`);
|
|
507
513
|
|
|
508
|
-
stateList.forEach(async
|
|
514
|
+
stateList.forEach(async changedState => {
|
|
509
515
|
const stateDesc = changedState.stateDesc;
|
|
510
516
|
const value = changedState.value;
|
|
511
517
|
|
|
512
|
-
if (stateDesc.id
|
|
518
|
+
if (stateDesc.id === 'send_payload') {
|
|
513
519
|
try {
|
|
514
520
|
const json_value = JSON.parse(value);
|
|
515
|
-
const payload = {
|
|
516
|
-
const result = await
|
|
521
|
+
const payload = {device: deviceId.replace('0x', ''), payload: json_value};
|
|
522
|
+
const result = await this.sendPayload(payload);
|
|
517
523
|
if (result.hasOwnProperty('success') && result.success) {
|
|
518
524
|
this.acknowledgeState(deviceId, model, stateDesc, value);
|
|
519
525
|
}
|
|
@@ -532,7 +538,7 @@ class Zigbee extends utils.Adapter {
|
|
|
532
538
|
// if this is the device query state => trigger the device query
|
|
533
539
|
|
|
534
540
|
// on activation of the 'device_query' state trigger hardware query where possible
|
|
535
|
-
if (stateDesc.id
|
|
541
|
+
if (stateDesc.id === 'device_query') {
|
|
536
542
|
if (this.query_device_block.indexOf(deviceId) > -1) {
|
|
537
543
|
this.log.warn(`Device query for '${entity.device.ieeeAddr}' blocked`);
|
|
538
544
|
return;
|
|
@@ -554,16 +560,18 @@ class Zigbee extends utils.Adapter {
|
|
|
554
560
|
}
|
|
555
561
|
this.log.debug(`Device query for '${entity.device.ieeeAddr}' done`);
|
|
556
562
|
const idToRemove = deviceId;
|
|
557
|
-
setTimeout(()=>{
|
|
563
|
+
setTimeout(() => {
|
|
558
564
|
const idx = this.query_device_block.indexOf(idToRemove);
|
|
559
|
-
if (idx > -1)
|
|
565
|
+
if (idx > -1) {
|
|
566
|
+
this.query_device_block.splice(idx);
|
|
567
|
+
}
|
|
560
568
|
}, 10000);
|
|
561
569
|
}
|
|
562
570
|
return;
|
|
563
571
|
}
|
|
564
572
|
return;
|
|
565
573
|
}
|
|
566
|
-
const converter = mappedModel.toZigbee.find(
|
|
574
|
+
const converter = mappedModel.toZigbee.find(c => c && (c.key.includes(stateDesc.prop) || c.key.includes(stateDesc.setattr) || c.key.includes(stateDesc.id)));
|
|
567
575
|
if (!converter) {
|
|
568
576
|
this.log.error(`No converter available for '${model}' with key '${stateDesc.id}' `);
|
|
569
577
|
this.sendError(`No converter available for '${model}' with key '${stateDesc.id}' `);
|
|
@@ -574,7 +582,7 @@ class Zigbee extends utils.Adapter {
|
|
|
574
582
|
const preparedOptions = (stateDesc.setterOpt) ? stateDesc.setterOpt(value, options) : {};
|
|
575
583
|
let syncStateList = [];
|
|
576
584
|
if (stateModel && stateModel.syncStates) {
|
|
577
|
-
stateModel.syncStates.forEach(
|
|
585
|
+
stateModel.syncStates.forEach(syncFunct => {
|
|
578
586
|
const res = syncFunct(stateDesc, value, options);
|
|
579
587
|
if (res) {
|
|
580
588
|
syncStateList = syncStateList.concat(res);
|
|
@@ -593,12 +601,14 @@ class Zigbee extends utils.Adapter {
|
|
|
593
601
|
target = await this.zbController.resolveEntity(deviceId, epName);
|
|
594
602
|
target = target.endpoint;
|
|
595
603
|
}
|
|
604
|
+
|
|
596
605
|
this.log.debug(`target: ${safeJsonStringify(target)}`);
|
|
606
|
+
|
|
597
607
|
const meta = {
|
|
598
608
|
endpoint_name: epName,
|
|
599
609
|
options: preparedOptions,
|
|
600
610
|
device: entity.device,
|
|
601
|
-
mapped:
|
|
611
|
+
mapped: model === 'group' ? [] : mappedModel,
|
|
602
612
|
message: {[key]: preparedValue},
|
|
603
613
|
logger: this.log,
|
|
604
614
|
state: {},
|
|
@@ -610,23 +620,24 @@ class Zigbee extends utils.Adapter {
|
|
|
610
620
|
const result = await converter.convertSet(target, key, preparedValue, meta);
|
|
611
621
|
this.log.debug(`convert result ${safeJsonStringify(result)}`);
|
|
612
622
|
if (result !== undefined) {
|
|
613
|
-
if (stateModel && !isGroup)
|
|
623
|
+
if (stateModel && !isGroup) {
|
|
614
624
|
this.acknowledgeState(deviceId, model, stateDesc, value);
|
|
625
|
+
}
|
|
615
626
|
// process sync state list
|
|
616
627
|
this.processSyncStatesList(deviceId, model, syncStateList);
|
|
628
|
+
|
|
617
629
|
if (isGroup) {
|
|
618
630
|
await this.callPluginMethod('queryGroupMemberState', [deviceId, stateDesc]);
|
|
619
631
|
this.acknowledgeState(deviceId, model, stateDesc, value);
|
|
620
632
|
}
|
|
621
633
|
}
|
|
622
|
-
} catch(error) {
|
|
623
|
-
this.filterError(`Error ${error.code} on send command to ${deviceId}
|
|
624
|
-
|
|
634
|
+
} catch (error) {
|
|
635
|
+
this.filterError(`Error ${error.code} on send command to ${deviceId}.` +
|
|
636
|
+
` Error: ${error.stack}`, `Send command to ${deviceId} failed with`, error);
|
|
625
637
|
}
|
|
626
638
|
});
|
|
627
639
|
}
|
|
628
|
-
|
|
629
|
-
//
|
|
640
|
+
|
|
630
641
|
// This function is introduced to explicitly allow user level scripts to send Commands
|
|
631
642
|
// directly to the zigbee device. It utilizes the zigbee-herdsman-converters to generate
|
|
632
643
|
// the exact zigbee message to be sent and can be used to set device options which are
|
|
@@ -639,74 +650,79 @@ class Zigbee extends utils.Adapter {
|
|
|
639
650
|
// payload: The data to send to the device as JSON object (key/Value pairs)
|
|
640
651
|
// endpoint: optional: the endpoint to send the data to, if supported.
|
|
641
652
|
//
|
|
642
|
-
async
|
|
653
|
+
async sendPayload(payload) {
|
|
643
654
|
this.log.debug(`publishToDevice called with ${safeJsonStringify(payload)}`);
|
|
644
|
-
let
|
|
655
|
+
let payloadObj = {};
|
|
645
656
|
if (typeof payload === 'string') {
|
|
646
657
|
try {
|
|
647
|
-
|
|
658
|
+
payloadObj = JSON.parse(payload);
|
|
648
659
|
} catch (e) {
|
|
649
660
|
this.log.error(`Unable to parse ${safeJsonStringify(payload)}: ${safeJsonStringify(e)}`);
|
|
650
661
|
this.sendError(e, `Unable to parse ${safeJsonStringify(payload)}: ${safeJsonStringify(e)}`);
|
|
651
|
-
return {
|
|
662
|
+
return {
|
|
663
|
+
success: false,
|
|
664
|
+
error: `Unable to parse ${safeJsonStringify(payload)}: ${safeJsonStringify(e)}`
|
|
665
|
+
};
|
|
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};
|
|
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};
|
|
696
719
|
}
|
|
697
|
-
|
|
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};
|
|
720
|
+
} catch (e) {
|
|
721
|
+
return {success: false, error: e};
|
|
706
722
|
}
|
|
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
|
|
|
@@ -719,9 +735,8 @@ class Zigbee extends utils.Adapter {
|
|
|
719
735
|
const model = (entity.mapped) ? entity.mapped.model : entity.device.modelID;
|
|
720
736
|
this.log.debug(`new device ${dev.ieeeAddr} ${dev.networkAddress} ${model} `);
|
|
721
737
|
this.logToPairing(`New device joined '${dev.ieeeAddr}' model ${model}`, true);
|
|
722
|
-
this.stController.updateDev(dev.ieeeAddr.substr(2), model, model, () =>
|
|
723
|
-
this.stController.syncDevStates(dev, model);
|
|
724
|
-
});
|
|
738
|
+
this.stController.updateDev(dev.ieeeAddr.substr(2), model, model, () =>
|
|
739
|
+
this.stController.syncDevStates(dev, model));
|
|
725
740
|
}
|
|
726
741
|
// else this.log.warn(`Device ${safeJsonStringify(entity)} rejoined, no new device`);
|
|
727
742
|
});
|
|
@@ -732,7 +747,7 @@ class Zigbee extends utils.Adapter {
|
|
|
732
747
|
this.log.debug(`Leave device event: ${ieeeAddr}`);
|
|
733
748
|
if (ieeeAddr) {
|
|
734
749
|
const devId = ieeeAddr.substr(2);
|
|
735
|
-
this.log.debug(
|
|
750
|
+
this.log.debug(`Delete device ${devId} from iobroker.`);
|
|
736
751
|
this.stController.deleteDeviceStates(devId);
|
|
737
752
|
}
|
|
738
753
|
}
|
|
@@ -756,7 +771,7 @@ class Zigbee extends utils.Adapter {
|
|
|
756
771
|
}
|
|
757
772
|
}
|
|
758
773
|
}
|
|
759
|
-
|
|
774
|
+
|
|
760
775
|
/**
|
|
761
776
|
* @param {() => void} callback
|
|
762
777
|
*/
|
|
@@ -841,7 +856,7 @@ class Zigbee extends utils.Adapter {
|
|
|
841
856
|
}
|
|
842
857
|
if (data === 0) {
|
|
843
858
|
// set pairing mode off
|
|
844
|
-
this.setState('info.pairingMode', false,true);
|
|
859
|
+
this.setState('info.pairingMode', false, true);
|
|
845
860
|
_pairingMode = false;
|
|
846
861
|
}
|
|
847
862
|
if (data) {
|
|
@@ -856,7 +871,7 @@ class Zigbee extends utils.Adapter {
|
|
|
856
871
|
}
|
|
857
872
|
|
|
858
873
|
expandFileName(fn) {
|
|
859
|
-
|
|
874
|
+
return path.join(utils.getAbsoluteInstanceDataDir(this), fn);
|
|
860
875
|
}
|
|
861
876
|
|
|
862
877
|
onLog(level, msg, data) {
|
|
@@ -867,8 +882,8 @@ class Zigbee extends utils.Adapter {
|
|
|
867
882
|
logger = this.log.error;
|
|
868
883
|
if (data)
|
|
869
884
|
data = data.toString();
|
|
870
|
-
this.logToPairing(
|
|
871
|
-
this.sendError(
|
|
885
|
+
this.logToPairing(`Error: ${msg}. ${data}`, true);
|
|
886
|
+
this.sendError(`Error: ${msg}. ${data}`);
|
|
872
887
|
break;
|
|
873
888
|
case 'debug':
|
|
874
889
|
logger = this.log.debug;
|
|
@@ -882,9 +897,9 @@ class Zigbee extends utils.Adapter {
|
|
|
882
897
|
}
|
|
883
898
|
if (data) {
|
|
884
899
|
if (typeof data === 'string') {
|
|
885
|
-
logger(msg
|
|
900
|
+
logger(`${msg}. ${data}`);
|
|
886
901
|
} else {
|
|
887
|
-
logger(msg
|
|
902
|
+
logger(`${msg}. ${safeJsonStringify(data)}`);
|
|
888
903
|
}
|
|
889
904
|
} else {
|
|
890
905
|
logger(msg);
|