iobroker.zigbee2mqtt 0.1.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/lib/utils.js ADDED
@@ -0,0 +1,131 @@
1
+ /**
2
+ * Converts a bulb level of range [0...254] to an adapter level of range [0...100]
3
+ */
4
+ function bulbLevelToAdapterLevel(bulbLevel) {
5
+ // Convert from bulb levels [0...254] to adapter levels [0...100]:
6
+ // - Bulb level 0 is a forbidden value according to the ZigBee spec "ZigBee Cluster Library
7
+ // (for ZigBee 3.0) User Guide", but some bulbs (HUE) accept this value and interpret this
8
+ // value as "switch the bulb off".
9
+ // - A bulb level of "1" is the "minimum possible level" which should mean "bulb off",
10
+ // but there are bulbs that do not switch off (they need "0", some IKEA bulbs are affected).
11
+ // - No visible difference was seen between bulb level 1 and 2 on HUE LCT012 bulbs.
12
+ //
13
+ // Conclusion:
14
+ // - We map adapter level "0" to the (forbidden) bulb level "0" that seems to switch all
15
+ // known bulbs.
16
+ // - Bulb level "1" is not used, but if received nevertheless, it is converted to
17
+ // adapter level "0" (off).
18
+ // - Bulb level range [2...254] is linearly mapped to adapter level range [1...100].
19
+ if (bulbLevel >= 2) {
20
+ // Perform linear mapping of range [2...254] to [1...100]
21
+ return (Math.round((bulbLevel - 2) * 99 / 252) + 1);
22
+ } else {
23
+ // The bulb is considered off. Even a bulb level of "1" is considered as off.
24
+ return 0;
25
+ } // else
26
+ }
27
+
28
+ /**
29
+ * Converts an adapter level of range [0...100] to a bulb level of range [0...254]
30
+ */
31
+ function adapterLevelToBulbLevel(adapterLevel) {
32
+ // Convert from adapter levels [0...100] to bulb levels [0...254].
33
+ // This is the inverse of function bulbLevelToAdapterLevel().
34
+ // Please read the comments there regarding the rules applied here for mapping the values.
35
+ if (adapterLevel) {
36
+ // Perform linear mapping of range [1...100] to [2...254]
37
+ return (Math.round((adapterLevel - 1) * 252 / 99) + 2);
38
+ } else {
39
+ // Switch the bulb off. Some bulbs need "0" (IKEA), others "1" (HUE), and according to the
40
+ // ZigBee docs "1" is the "minimum possible level"... we choose "0" here which seems to work.
41
+ return 0;
42
+ } // else
43
+ }
44
+
45
+ function bytesArrayToWordArray(ba) {
46
+ const wa = [];
47
+ for (let i = 0; i < ba.length; i++) {
48
+ wa[(i / 2) | 0] |= ba[i] << (8 * (i % 2));
49
+ }
50
+ return wa;
51
+ }
52
+
53
+ // If the value is greater than 1000, kelvin is assumed.
54
+ // If smaller, it is assumed to be mired.
55
+ function toMired(t) {
56
+ let miredValue = t;
57
+ if (t > 1000) {
58
+ miredValue = miredKelvinConversion(t);
59
+ }
60
+ return miredValue;
61
+ }
62
+
63
+ function miredKelvinConversion(t) {
64
+ return (1000000 / t).toFixed();
65
+ }
66
+
67
+ /**
68
+ * Converts a decimal number to a hex string with zero-padding
69
+ */
70
+ function decimalToHex(decimal, padding) {
71
+ let hex = Number(decimal).toString(16);
72
+ padding = typeof (padding) === 'undefined' || padding === null ? padding = 2 : padding;
73
+
74
+ while (hex.length < padding) {
75
+ hex = `0${hex}`;
76
+ }
77
+
78
+ return hex;
79
+ }
80
+
81
+ function getZbId(adapterDevId) {
82
+ const idx = adapterDevId.indexOf('group');
83
+ if (idx > 0) return adapterDevId.substr(idx + 6);
84
+ return '0x' + adapterDevId.split('.')[2];
85
+ }
86
+
87
+ function getAdId(adapter, id) {
88
+ return adapter.namespace + '.' + id.split('.')[2]; // iobroker device id
89
+ }
90
+
91
+ function sanitizeImageParameter(parameter) {
92
+ const replaceByDash = [/\?/g, /&/g, /[^a-z\d\-_./:]/gi, /[/]/gi];
93
+ let sanitized = parameter;
94
+ replaceByDash.forEach((r) => sanitized = sanitized.replace(r, '-'));
95
+ return sanitized;
96
+ }
97
+
98
+ function getDeviceIcon(definition) {
99
+ let icon = definition.icon;
100
+ if (icon) {
101
+ icon = icon.replace('${model}', sanitizeImageParameter(definition.model));
102
+ }
103
+ if (!icon) {
104
+ icon = `https://www.zigbee2mqtt.io/images/devices/${sanitizeImageParameter(definition.model)}.jpg`;
105
+ }
106
+ return icon;
107
+ }
108
+
109
+ function removeDeviceByIeee(devices, ieee_address) {
110
+ const idx = devices.findIndex(x => x.ieee_address == ieee_address);
111
+ if (idx > -1) {
112
+ devices.splice(idx, 1);
113
+ }
114
+ }
115
+ function clearArray(array) {
116
+ while (array.length > 0) {
117
+ array.pop();
118
+ }
119
+ }
120
+
121
+ exports.bulbLevelToAdapterLevel = bulbLevelToAdapterLevel;
122
+ exports.adapterLevelToBulbLevel = adapterLevelToBulbLevel;
123
+ exports.bytesArrayToWordArray = bytesArrayToWordArray;
124
+ exports.toMired = toMired;
125
+ exports.miredKelvinConversion = miredKelvinConversion;
126
+ exports.decimalToHex = decimalToHex;
127
+ exports.getZbId = getZbId;
128
+ exports.getAdId = getAdId;
129
+ exports.removeDeviceByIeee = removeDeviceByIeee;
130
+ exports.getDeviceIcon = getDeviceIcon;
131
+ exports.clearArray = clearArray;
package/main.js ADDED
@@ -0,0 +1,504 @@
1
+ 'use strict';
2
+
3
+ /*
4
+ * Created with @iobroker/create-adapter v2.2.1
5
+ */
6
+
7
+ // The adapter-core module gives you access to the core ioBroker functions
8
+ // you need to create an adapter
9
+ const core = require('@iobroker/adapter-core');
10
+ const WebSocket = require('ws');
11
+ const defineDeviceFromExposes = require('./lib/exposes').defineDeviceFromExposes;
12
+ const defineGroupDevice = require('./lib/groups').defineGroupDevice;
13
+ const clearArray = require('./lib/utils').clearArray;
14
+ let wsClient;
15
+ let adapter;
16
+ let createDevicesOrReady = false;
17
+ const incStatsQueue = [];
18
+ const createCache = {};
19
+ // eslint-disable-next-line prefer-const
20
+ let deviceCache = [];
21
+ // eslint-disable-next-line prefer-const
22
+ let groupCache = [];
23
+ const lastSeenCache = {};
24
+ let ping;
25
+ let pingTimeout;
26
+ let autoRestartTimeout;
27
+ const wsHeartbeatIntervall = 5000;
28
+ const restartTimeout = 1000;
29
+ const deviceAvailableTimeout = 10 * 60; // 10 Minutes
30
+ const batteryDeviceAvailableTimeout = 24 * 60 * 60; // 24 Hours
31
+ const checkAvailableInterval = 30 * 1000; // 10 Seconds
32
+ let debugLogEnabled;
33
+ let proxyZ2MLogsEnabled;
34
+ let checkAvailableTimout;
35
+
36
+ class Zigbee2mqtt extends core.Adapter {
37
+
38
+ constructor(options) {
39
+ super({
40
+ ...options,
41
+ name: 'zigbee2mqtt',
42
+ });
43
+ this.on('ready', this.onReady.bind(this));
44
+ this.on('stateChange', this.onStateChange.bind(this));
45
+ this.on('unload', this.onUnload.bind(this));
46
+ }
47
+
48
+ async onReady() {
49
+ // Initialize your adapter here
50
+ adapter = this;
51
+ this.log.info(`Zigbee2MQTT Frontend Server: ${this.config.server}`);
52
+ this.log.info(`Zigbee2MQTT Frontend Port: ${this.config.port}`);
53
+ this.log.info(`Zigbee2MQTT Debug Log: ${this.config.debugLogEnabled ? 'activated' : 'deactivated'}`);
54
+ this.log.info(`Proxy Zigbee2MQTT Logs to ioBroker Logs: ${this.config.proxyZ2MLogs ? 'activated' : 'deactivated'}`);
55
+
56
+ this.setStateAsync('info.connection', false, true);
57
+ this.createWsClient(this.config.server, this.config.port);
58
+
59
+ debugLogEnabled = this.config.debugLogEnabled;
60
+ proxyZ2MLogsEnabled = this.config.proxyZ2MLogs;
61
+ }
62
+
63
+ async createWsClient(server, port) {
64
+ try {
65
+ wsClient = new WebSocket(`ws://${server}:${port}/api`);
66
+ wsClient.on('open', () => {
67
+ this.logDebug('Websocket connectet');
68
+ // Set connection state
69
+ this.setState('info.connection', true, true);
70
+ this.log.info('Connect to server over websocket connection.');
71
+ // Send ping to server
72
+ this.sendPingToServer();
73
+ // Start Heartbeat
74
+ this.wsHeartbeat();
75
+ // Start CheckAvailableTimer
76
+ this.checkAvailableTimer();
77
+ });
78
+ wsClient.on('pong', () => {
79
+ //this.logDebug('Receive pong from server');
80
+ this.wsHeartbeat();
81
+ });
82
+ // On Close
83
+ wsClient.on('close', () => {
84
+ this.setState('info.connection', false, true);
85
+ this.log.warn('Websocket disconnectet');
86
+ clearTimeout(ping);
87
+ clearTimeout(pingTimeout);
88
+
89
+ if (wsClient.readyState === WebSocket.CLOSED) {
90
+ this.autoRestart();
91
+ }
92
+ });
93
+
94
+ wsClient.on('message', (message) => { this.messageParse(message); });
95
+ wsClient.on('error', (err) => { adapter.logDebug(err); });
96
+ } catch (err) {
97
+ this.logDebug(err);
98
+ }
99
+ }
100
+
101
+ async sendPingToServer() {
102
+ //this.logDebug('Send ping to server');
103
+ wsClient.ping();
104
+ ping = setTimeout(() => {
105
+ this.sendPingToServer();
106
+ }, wsHeartbeatIntervall);
107
+ }
108
+
109
+ async wsHeartbeat() {
110
+ clearTimeout(pingTimeout);
111
+ pingTimeout = setTimeout(() => {
112
+ this.logDebug('Websocked connection timed out');
113
+ wsClient.terminate();
114
+ clearTimeout(checkAvailableTimout);
115
+ }, wsHeartbeatIntervall + 1000);
116
+ }
117
+
118
+ async autoRestart() {
119
+ this.log.warn(`Start try again in ${restartTimeout / 1000} seconds...`);
120
+ autoRestartTimeout = setTimeout(() => {
121
+ this.onReady();
122
+ }, restartTimeout);
123
+ }
124
+
125
+ async messageParse(message) {
126
+ const messageObj = JSON.parse(message);
127
+
128
+ switch (messageObj.topic) {
129
+ case 'bridge/config':
130
+ break;
131
+ case 'bridge/info':
132
+ break;
133
+ case 'bridge/state':
134
+ break;
135
+ case 'bridge/devices':
136
+ // As long as we are busy creating the devices, the states are written to the queue.
137
+ createDevicesOrReady = false;
138
+ await this.createDeviceDefinitions(deviceCache, messageObj.payload);
139
+ await this.createOrUpdateDevices(deviceCache);
140
+ createDevicesOrReady = true;
141
+
142
+ // Now process all entries in the states queue
143
+ while (incStatsQueue.length > 0) {
144
+ this.processDeviceMessage(incStatsQueue.shift());
145
+ }
146
+ break;
147
+ case 'bridge/groups':
148
+ await this.createGroupDefinitions(groupCache, messageObj.payload);
149
+ await this.createOrUpdateDevices(groupCache);
150
+ break;
151
+ case 'bridge/event':
152
+ break;
153
+ case 'bridge/extensions':
154
+ break;
155
+ case 'bridge/logging':
156
+ if (proxyZ2MLogsEnabled == true) {
157
+ this.proxyZ2MLogs(messageObj);
158
+ }
159
+ break;
160
+ case 'bridge/response/networkmap':
161
+ break;
162
+ case 'bridge/response/touchlink/scan':
163
+ break;
164
+ case 'bridge/response/touchlink/identify':
165
+ break;
166
+ case 'bridge/response/touchlink/factory_reset':
167
+ break;
168
+ default:
169
+ // States
170
+ {
171
+ if (!messageObj.topic.includes('/')) {
172
+ // As long as we are busy creating the devices, the states are written to the queue.
173
+ if (createDevicesOrReady == false) {
174
+ incStatsQueue[incStatsQueue.length] = messageObj;
175
+ break;
176
+ }
177
+ this.processDeviceMessage(messageObj);
178
+ }
179
+ }
180
+ break;
181
+ }
182
+ }
183
+
184
+ async processDeviceMessage(messageObj) {
185
+ this.logDebug(`processDeviceMessage -> messageObj: ${JSON.stringify(messageObj)}`);
186
+ // Is payload present?
187
+ if (messageObj.payload == '') {
188
+ return;
189
+ }
190
+
191
+ const device = deviceCache.find(x => x.id == messageObj.topic);
192
+ if (device) {
193
+ this.logDebug(`processDeviceMessage -> device: ${JSON.stringify(device)}`);
194
+ try {
195
+ // The state available must not be considered for the cacheLastSeen
196
+ // Groups must not be considered for the cacheLastSeen
197
+ if (messageObj.payload.available == undefined && !device.ieee_address.startsWith('group_')) {
198
+ await this.cacheLastSeen(device, messageObj);
199
+ }
200
+ this.setDeviceState(messageObj, device);
201
+ this.checkAvailable(device.ieee_address);
202
+
203
+ } catch (error) {
204
+ adapter.log.error(error);
205
+ }
206
+ }
207
+ else {
208
+ adapter.log.warn(`Device: ${messageObj.topic} not found`);
209
+ }
210
+ }
211
+
212
+ async cacheLastSeen(device, messageObj) {
213
+ this.logDebug(`cacheLastSeen -> device: ${JSON.stringify(device)}`);
214
+ this.logDebug(`cacheLastSeen -> messageObj: ${JSON.stringify(messageObj)}`);
215
+ if (messageObj.payload.last_seen) {
216
+ lastSeenCache[device.ieee_address] = new Date(messageObj.payload.last_seen).getTime();
217
+ } else {
218
+ lastSeenCache[device.ieee_address] = new Date().getTime();
219
+ }
220
+ this.logDebug(`cacheLastSeen -> deviceLastSeenCache: ${JSON.stringify(lastSeenCache)}`);
221
+ }
222
+
223
+ async checkAvailableTimer() {
224
+ checkAvailableTimout = setTimeout(async () => {
225
+ await this.checkAvailable(null);
226
+ this.checkAvailableTimer();
227
+ }, checkAvailableInterval);
228
+ }
229
+
230
+ async checkAvailable(ieee_address) {
231
+ this.logDebug(`checkAvailable -> ieee_address: ${ieee_address}`);
232
+ let checkList = {};
233
+ if (ieee_address) {
234
+ checkList[ieee_address] = null;
235
+ }
236
+ else {
237
+ checkList = lastSeenCache;
238
+ }
239
+
240
+ for (const ieee_address in checkList) {
241
+ const device = deviceCache.find(x => x.ieee_address == ieee_address);
242
+
243
+ if (!device) {
244
+ continue;
245
+ }
246
+
247
+ const isBatteryDevice = device.power_source == 'Battery' ? true : false;
248
+ const offlineTimeout = isBatteryDevice ? batteryDeviceAvailableTimeout : deviceAvailableTimeout;
249
+ const diffSec = Math.round((new Date().getTime() - lastSeenCache[ieee_address]) / 1000);
250
+ const available = diffSec < offlineTimeout;
251
+
252
+ this.logDebug(`checkAvailable -> device.id: ${device.id}, available: ${available}, diffSec: ${diffSec}, isBatteryDevice: ${isBatteryDevice}`);
253
+
254
+ if (device.available == null || device.available != available) {
255
+ this.logDebug(`checkAvailable -> device.id: ${device.id}, available: ${available}, diffSec: ${diffSec}, isBatteryDevice: ${isBatteryDevice}`);
256
+ device.available = available;
257
+ const messageObj = {
258
+ topic: device.id,
259
+ payload: {
260
+ available: available,
261
+ }
262
+ };
263
+
264
+ this.processDeviceMessage(messageObj);
265
+ }
266
+ }
267
+ }
268
+
269
+ async setDeviceState(messageObj, device) {
270
+
271
+ for (const [key, value] of Object.entries(messageObj.payload)) {
272
+ this.logDebug(`setDeviceState -> key: ${key}`);
273
+ this.logDebug(`setDeviceState -> value: ${JSON.stringify(value)}`);
274
+
275
+ let states;
276
+ if (key == 'action') {
277
+ states = device.states.filter(x => (x.prop && x.prop == key) && x.id == value);
278
+ } else {
279
+ states = device.states.filter(x => (x.prop && x.prop == key) || x.id == key);
280
+ }
281
+ this.logDebug(`setDeviceState -> states: ${JSON.stringify(states)}`);
282
+
283
+ for (const state of states) {
284
+ if (!state) {
285
+ continue;
286
+ }
287
+ const stateName = `${device.ieee_address}.${state.id}`;
288
+
289
+ if (state.getter) {
290
+ this.setState(stateName, state.getter(messageObj.payload), true);
291
+ }
292
+ else {
293
+ this.setState(stateName, value, true);
294
+ }
295
+ }
296
+ }
297
+ }
298
+
299
+ async createDeviceDefinitions(cache, exposes) {
300
+ clearArray(cache);
301
+ for (const expose of exposes) {
302
+ if (expose.definition != null) {
303
+ await defineDeviceFromExposes(cache, expose.friendly_name, expose.ieee_address, expose.definition, expose.power_source);
304
+ }
305
+ }
306
+ }
307
+
308
+ async createGroupDefinitions(cache, exposes) {
309
+ clearArray(cache);
310
+ for (const expose of exposes) {
311
+ await defineGroupDevice(cache, expose.friendly_name, `group_${expose.id}`, expose.scenes);
312
+ }
313
+ }
314
+
315
+ async createOrUpdateDevices(cache) {
316
+ for (const device of cache) {
317
+ const deviceName = device.id == device.ieee_address ? '' : device.id;
318
+ if (!createCache[device.ieee_address] || createCache[device.ieee_address].common.name != deviceName) {
319
+ const deviceObj = {
320
+ type: 'device',
321
+ common: {
322
+ name: deviceName,
323
+ statusStates: {
324
+ onlineId: `${this.name}.${this.instance}.${device.ieee_address}.available`
325
+ },
326
+ },
327
+
328
+ native: {}
329
+ };
330
+ //@ts-ignore
331
+ await this.extendObjectAsync(device.ieee_address, deviceObj);
332
+ createCache[device.ieee_address] = deviceObj;
333
+ }
334
+
335
+ // Special handling for groups, here it is checked whether the scenes match the current data from z2m.
336
+ // If necessary, scenes are automatically deleted from ioBroker.
337
+ if (device.ieee_address.startsWith('group_')) {
338
+ const sceneStates = await this.getStatesAsync(`${device.ieee_address}.scene_*`);
339
+ const sceneIDs = Object.keys(sceneStates);
340
+ for (const sceneID of sceneIDs) {
341
+ const stateID = sceneID.split('.')[3];
342
+ if (device.states.find(x => x.id == stateID) == null) {
343
+ this.delObject(sceneID);
344
+ }
345
+ }
346
+ }
347
+
348
+ for (const state of device.states) {
349
+ if (!createCache[device.ieee_address][state.id] || createCache[device.ieee_address][state.id].name != state.name) {
350
+ const iobState = await this.copyAndCleanStateObj(state);
351
+ this.logDebug(`Orig. state: ${JSON.stringify(state)}`);
352
+ this.logDebug(`Cleaned. state: ${JSON.stringify(iobState)}`);
353
+
354
+
355
+
356
+ await this.extendObjectAsync(`${device.ieee_address}.${state.id}`, {
357
+ type: 'state',
358
+ common: iobState,
359
+ native: {},
360
+ });
361
+ createCache[device.ieee_address][state.id] = state.name;
362
+ }
363
+ }
364
+ }
365
+ this.subscribeWritableStates();
366
+ }
367
+
368
+ async copyAndCleanStateObj(state) {
369
+ const iobState = { ...state };
370
+ const blacklistedKeys = [
371
+ 'setter',
372
+ 'setterOpt',
373
+ 'getter',
374
+ 'setattr',
375
+ 'readable',
376
+ 'writable',
377
+ 'isOption',
378
+ 'inOptions'
379
+ ];
380
+ for (const blacklistedKey of blacklistedKeys) {
381
+ delete iobState[blacklistedKey];
382
+ }
383
+ return iobState;
384
+ }
385
+
386
+ async subscribeWritableStates() {
387
+ await this.unsubscribeObjectsAsync('*');
388
+ for (const device of deviceCache) {
389
+ for (const state of device.states) {
390
+ if (state.write == true) {
391
+ this.subscribeStatesAsync(`${device.ieee_address}.${state.id}`);
392
+ }
393
+ }
394
+ }
395
+ }
396
+
397
+ async createZ2MMessage(id, state) {
398
+
399
+ const splitedID = id.split('.');
400
+
401
+ if (splitedID.length < 4) {
402
+ this.log.warn(`state ${id} not valid`);
403
+ return;
404
+ }
405
+
406
+ const ieee_address = splitedID[2];
407
+ const stateName = splitedID[3];
408
+
409
+ const device = deviceCache.find(d => d.ieee_address == ieee_address);
410
+
411
+ if (!device) {
412
+ return;
413
+ }
414
+
415
+ const deviceState = device.states.find(s => s.id == stateName);
416
+
417
+ if (!deviceState) {
418
+ return;
419
+ }
420
+
421
+ let stateVal = state.val;
422
+ if (deviceState.setter) {
423
+ stateVal = deviceState.setter(state.val);
424
+ }
425
+
426
+
427
+ let stateID = deviceState.id;
428
+ if (deviceState.prop) {
429
+ stateID = deviceState.prop;
430
+ }
431
+
432
+ let topic = `${device.ieee_address}/set`;
433
+ if (device.ieee_address.includes('group_')) {
434
+ topic = `${device.id}/set`;
435
+ }
436
+
437
+ const controlObj = {
438
+ payload: {
439
+ [stateID]: stateVal
440
+ },
441
+ topic: topic
442
+ };
443
+ // set stats with role 'button' always immediately to ack = true, because these are not reported back by Zigbee2MQTT
444
+ if (deviceState.role == 'button') {
445
+ this.setState(id, state, true);
446
+ }
447
+
448
+ return JSON.stringify(controlObj);
449
+ }
450
+
451
+ async proxyZ2MLogs(messageObj) {
452
+ this.logDebug(`proxyZ2MLogs -> messageObj: ${JSON.stringify(messageObj)}`);
453
+
454
+ const logLevel = messageObj.payload.level;
455
+ const logMessage = messageObj.payload.message;
456
+
457
+ switch (logLevel) {
458
+ case 'debug':
459
+ case 'info':
460
+ case 'error':
461
+ this.log[logLevel](logMessage);
462
+ break;
463
+ case 'warning':
464
+ this.log.warn(logMessage);
465
+ break;
466
+ }
467
+ }
468
+
469
+ async logDebug(message) {
470
+ if (debugLogEnabled == true) {
471
+ this.log.debug(message);
472
+ }
473
+ }
474
+
475
+ onUnload(callback) {
476
+ try {
477
+ clearTimeout(ping);
478
+ clearTimeout(pingTimeout);
479
+ clearTimeout(autoRestartTimeout);
480
+ clearTimeout(checkAvailableTimout);
481
+ callback();
482
+ } catch (e) {
483
+ callback();
484
+ }
485
+ }
486
+
487
+ async onStateChange(id, state) {
488
+ if (state && state.ack == false) {
489
+ const message = await this.createZ2MMessage(id, state);
490
+ wsClient.send(message);
491
+ }
492
+ }
493
+ }
494
+
495
+ if (require.main !== module) {
496
+ // Export the constructor in compact mode
497
+ /**
498
+ * @param {Partial<core.AdapterOptions>} [options={}]
499
+ */
500
+ module.exports = (options) => new Zigbee2mqtt(options);
501
+ } else {
502
+ // otherwise start the instance directly
503
+ new Zigbee2mqtt();
504
+ }
package/package.json ADDED
@@ -0,0 +1,75 @@
1
+ {
2
+ "name": "iobroker.zigbee2mqtt",
3
+ "version": "0.1.0",
4
+ "description": "Zigbee2MQTT adapter for ioBroker",
5
+ "author": {
6
+ "name": "Dennis Rathjen",
7
+ "email": "dennis.rathjen@outlook.de"
8
+ },
9
+ "homepage": "https://github.com/o0shojo0o/ioBroker.zigbee2mqtt",
10
+ "license": "MIT",
11
+ "keywords": [
12
+ "ioBroker",
13
+ "template",
14
+ "Smart Home",
15
+ "home automation"
16
+ ],
17
+ "repository": {
18
+ "type": "git",
19
+ "url": "https://github.com/o0shojo0o/ioBroker.zigbee2mqtt.git"
20
+ },
21
+ "dependencies": {
22
+ "@alcalzone/release-script-plugin-iobroker": "^3.5.9",
23
+ "@alcalzone/release-script-plugin-license": "^3.5.9",
24
+ "@iobroker/adapter-core": "^2.6.6",
25
+ "ws": "^8.9.0"
26
+ },
27
+ "devDependencies": {
28
+ "@alcalzone/release-script": "^3.5.9",
29
+ "@iobroker/adapter-dev": "^1.1.0",
30
+ "@iobroker/testing": "^4.1.0",
31
+ "@tsconfig/node14": "^1.0.3",
32
+ "@types/chai": "^4.3.3",
33
+ "@types/chai-as-promised": "^7.1.5",
34
+ "@types/mocha": "^10.0.0",
35
+ "@types/node": "^18.7.23",
36
+ "@types/proxyquire": "^1.3.28",
37
+ "@types/sinon": "^10.0.13",
38
+ "@types/sinon-chai": "^3.2.8",
39
+ "chai": "^4.3.6",
40
+ "chai-as-promised": "^7.1.1",
41
+ "eslint": "^8.24.0",
42
+ "eslint-config-prettier": "^8.5.0",
43
+ "eslint-plugin-prettier": "^4.2.1",
44
+ "mocha": "^10.0.0",
45
+ "prettier": "^2.7.1",
46
+ "proxyquire": "^2.1.3",
47
+ "sinon": "^14.0.0",
48
+ "sinon-chai": "^3.7.0",
49
+ "typescript": "~4.8.4"
50
+ },
51
+ "main": "main.js",
52
+ "files": [
53
+ "admin{,/!(src)/**}/!(tsconfig|tsconfig.*|.eslintrc).json",
54
+ "admin{,/!(src)/**}/*.{html,css,png,svg,jpg,js}",
55
+ "lib/",
56
+ "www/",
57
+ "io-package.json",
58
+ "LICENSE",
59
+ "main.js"
60
+ ],
61
+ "scripts": {
62
+ "test:js": "mocha --config test/mocharc.custom.json \"{!(node_modules|test)/**/*.test.js,*.test.js,test/**/test!(PackageFiles|Startup).js}\"",
63
+ "test:package": "mocha test/package --exit",
64
+ "test:integration": "mocha test/integration --exit",
65
+ "test": "npm run test:js && npm run test:package",
66
+ "check": "tsc --noEmit -p tsconfig.check.json",
67
+ "lint": "eslint .",
68
+ "translate": "translate-adapter",
69
+ "release": "release-script"
70
+ },
71
+ "bugs": {
72
+ "url": "https://github.com/o0shojo0o/ioBroker.zigbee2mqtt/issues"
73
+ },
74
+ "readmeFilename": "README.md"
75
+ }