iobroker.zigbee2mqtt 0.2.0 → 1.0.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/main.js CHANGED
@@ -7,16 +7,17 @@
7
7
  // The adapter-core module gives you access to the core ioBroker functions
8
8
  // you need to create an adapter
9
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
- let isConnected = false;
18
- const incStatsQueue = [];
19
- const createCache = {};
10
+ const NedbPersistence = require('aedes-persistence-nedb');
11
+ const mqtt = require('mqtt');
12
+ const checkConfig = require('./lib/check').checkConfig;
13
+ const adapterInfo = require('./lib/messages').adapterInfo;
14
+ const zigbee2mqttInfo = require('./lib/messages').zigbee2mqttInfo;
15
+ const Z2mController = require('./lib/z2mController').Z2mController;
16
+ const DeviceController = require('./lib/deviceController').DeviceController;
17
+ const StatesController = require('./lib/statesController').StatesController;
18
+
19
+
20
+ let mqttClient;
20
21
  // eslint-disable-next-line prefer-const
21
22
  let deviceCache = [];
22
23
  // eslint-disable-next-line prefer-const
@@ -24,14 +25,13 @@ let groupCache = [];
24
25
  let ping;
25
26
  let pingTimeout;
26
27
  let autoRestartTimeout;
27
- const wsHeartbeatIntervall = 5000;
28
- const restartTimeout = 1000;
29
- let debugLogEnabled;
30
- let proxyZ2MLogsEnabled;
31
28
  let checkAvailableTimout;
32
29
  let debugDevices = '';
33
30
  let logfilter = [];
34
- let useKelvin = false;
31
+ let showInfo = true;
32
+ let statesController;
33
+ let deviceController;
34
+ let z2mController;
35
35
 
36
36
  class Zigbee2mqtt extends core.Adapter {
37
37
 
@@ -46,20 +46,16 @@ class Zigbee2mqtt extends core.Adapter {
46
46
  }
47
47
 
48
48
  async onReady() {
49
+
50
+
51
+ statesController = new StatesController(this, deviceCache, groupCache, debugDevices);
52
+ deviceController = new DeviceController(this, deviceCache, groupCache, this.config.useKelvin);
53
+ z2mController = new Z2mController(this, deviceCache, groupCache, logfilter);
54
+
49
55
  // 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
- this.log.info(`Use Kelvin: ${this.config.useKelvin ? 'yes' : 'no'}`);
56
+ adapterInfo(this.config, this.log);
56
57
 
57
58
  this.setStateAsync('info.connection', false, true);
58
- this.createWsClient(this.config.server, this.config.port);
59
-
60
- debugLogEnabled = this.config.debugLogEnabled;
61
- proxyZ2MLogsEnabled = this.config.proxyZ2MLogs;
62
- useKelvin = this.config.useKelvin;
63
59
 
64
60
  const debugDevicesState = await this.getStateAsync('info.debugmessages');
65
61
  if (debugDevicesState && debugDevicesState.val) {
@@ -71,72 +67,31 @@ class Zigbee2mqtt extends core.Adapter {
71
67
  logfilter = String(logfilterState.val).split(';').filter(x => x); // filter removes empty strings here
72
68
  }
73
69
 
74
-
75
- this.subscribeStatesAsync('*');
76
- }
77
-
78
- async createWsClient(server, port) {
79
- try {
80
- wsClient = new WebSocket(`ws://${server}:${port}/api`);
81
- wsClient.on('open', () => {
82
- this.logDebug('Websocket connectet');
83
- // Set connection state
84
- this.setState('info.connection', true, true);
85
- this.log.info('Connect to server over websocket connection.');
86
- isConnected = true;
87
- // Send ping to server
88
- this.sendPingToServer();
89
- // Start Heartbeat
90
- this.wsHeartbeat();
91
- });
92
- wsClient.on('pong', () => {
93
- //this.logDebug('Receive pong from server');
94
- this.wsHeartbeat();
95
- });
96
- // On Close
97
- wsClient.on('close', async () => {
98
- this.setState('info.connection', false, true);
99
- this.log.warn('Websocket disconnectet');
100
- await this.setAllAvailableToFalse();
101
- clearTimeout(ping);
102
- clearTimeout(pingTimeout);
103
- isConnected = false;
104
-
105
- if (wsClient.readyState === WebSocket.CLOSED) {
106
- this.autoRestart();
107
- }
70
+ if (this.config.useExternalMqtt == true) {
71
+ mqttClient = mqtt.connect(`mqtt://${this.config.externalMqttServerIP}:${this.config.externalMqttServerPort}`, { clientId: `ioBroker.zigbee2mqtt_${Math.random().toString(16).slice(2, 8)}`, clean: true, reconnectPeriod: 500 });
72
+ } else {
73
+ const Aedes = require('aedes');
74
+ const net = require('net');
75
+ const db = new NedbPersistence({
76
+ path: `${core.getAbsoluteInstanceDataDir(this)}/mqttData`,
77
+ prefix: ''
108
78
  });
109
-
110
- wsClient.on('message', (message) => { this.messageParse(message); });
111
- wsClient.on('error', (err) => { adapter.logDebug(err); });
112
- } catch (err) {
113
- this.logDebug(err);
114
- }
115
- }
116
-
117
- async sendPingToServer() {
118
- //this.logDebug('Send ping to server');
119
- wsClient.ping();
120
- ping = setTimeout(() => {
121
- this.sendPingToServer();
122
- }, wsHeartbeatIntervall);
123
- }
124
-
125
- async wsHeartbeat() {
126
- clearTimeout(pingTimeout);
127
- pingTimeout = setTimeout(() => {
128
- this.logDebug('Websocked connection timed out');
129
- wsClient.terminate();
130
- clearTimeout(checkAvailableTimout);
131
- }, wsHeartbeatIntervall + 1000);
79
+ // @ts-ignore
80
+ const aedes = Aedes({ persistence: db });
81
+ const mqttServer = net.createServer(aedes.handle);
82
+ mqttServer.listen(this.config.mqttServerPort, this.config.mqttServerIPBind, () => { });
83
+ mqttClient = mqtt.connect(`mqtt://${this.config.mqttServerIPBind}:${this.config.mqttServerPort}`, { clientId: 'ioBroker.zigbee2mqtt', clean: true, reconnectPeriod: 500 });
84
+ }
85
+
86
+ mqttClient.on('connect', () => { this.setStateAsync('info.connection', true, true); });
87
+ mqttClient.subscribe('#');
88
+ mqttClient.on('message', (topic, payload) => {
89
+ const newMessage = `{"payload":${payload.toString() == '' ? '"null"' : payload.toString()},"topic":"${topic.slice(topic.search('/') + 1)}"}`;
90
+ console.log(newMessage);
91
+ this.messageParse(newMessage);
92
+ });
132
93
  }
133
94
 
134
- async autoRestart() {
135
- this.log.warn(`Start try again in ${restartTimeout / 1000} seconds...`);
136
- autoRestartTimeout = setTimeout(() => {
137
- this.onReady();
138
- }, restartTimeout);
139
- }
140
95
 
141
96
  async messageParse(message) {
142
97
  const messageObj = JSON.parse(message);
@@ -145,36 +100,41 @@ class Zigbee2mqtt extends core.Adapter {
145
100
  case 'bridge/config':
146
101
  break;
147
102
  case 'bridge/info':
103
+ if (showInfo) {
104
+ zigbee2mqttInfo(messageObj.payload, this.log);
105
+ checkConfig(messageObj.payload.config, this.log);
106
+ showInfo = false;
107
+ }
148
108
  break;
149
109
  case 'bridge/state':
150
110
  break;
151
111
  case 'bridge/devices':
152
- // As long as we are busy creating the devices, the states are written to the queue.
153
- createDevicesOrReady = false;
154
- await this.createDeviceDefinitions(deviceCache, messageObj.payload);
155
- await this.createOrUpdateDevices(deviceCache);
156
- this.subscribeWritableStates();
157
- createDevicesOrReady = true;
158
-
159
- // Now process all entries in the states queue
160
- while (incStatsQueue.length > 0) {
161
- this.processDeviceMessage(incStatsQueue.shift());
162
- }
112
+ await deviceController.createDeviceDefinitions(messageObj.payload);
113
+ await deviceController.createOrUpdateDevices();
114
+ await statesController.subscribeWritableStates();
115
+ statesController.processQueue();
163
116
  break;
164
117
  case 'bridge/groups':
165
- await this.createGroupDefinitions(groupCache, messageObj.payload);
166
- await this.createOrUpdateDevices(groupCache);
167
- this.subscribeWritableStates();
118
+ await deviceController.createGroupDefinitions(messageObj.payload);
119
+ await deviceController.createOrUpdateDevices();
120
+ await statesController.subscribeWritableStates();
121
+ statesController.processQueue();
168
122
  break;
169
123
  case 'bridge/event':
124
+ deviceController.processRemoveEvent(messageObj);
170
125
  break;
171
126
  case 'bridge/extensions':
172
127
  break;
173
128
  case 'bridge/logging':
174
- if (proxyZ2MLogsEnabled == true) {
175
- this.proxyZ2MLogs(messageObj);
129
+ if (this.config.proxyZ2MLogs == true) {
130
+ z2mController.proxyZ2MLogs(messageObj);
176
131
  }
177
132
  break;
133
+ case 'bridge/response/device/rename':
134
+ await deviceController.renameDeviceInCache(messageObj);
135
+ await deviceController.createOrUpdateDevices();
136
+ statesController.processQueue();
137
+ break;
178
138
  case 'bridge/response/networkmap':
179
139
  break;
180
140
  case 'bridge/response/touchlink/scan':
@@ -188,292 +148,32 @@ class Zigbee2mqtt extends core.Adapter {
188
148
  // {"payload":{"state":"online"},"topic":"FL.Licht.Links/availability"} ----> {"payload":{"available":true},"topic":"FL.Licht.Links"}
189
149
  if (messageObj.topic.endsWith('/availability')) {
190
150
  const topicSplit = messageObj.topic.split('/');
151
+
152
+ // If an availability message for an old device ID comes with a payload of NULL, this is the indicator that a device has been unnamed.
153
+ // If this is then still available in the cache, the messages must first be cached.
154
+ if (messageObj.payload == 'null') {
155
+ break;
156
+ }
157
+
191
158
  if (topicSplit.length == 2 && messageObj.payload && messageObj.payload.state) {
192
159
  const newMessage = {
193
160
  payload: { available: messageObj.payload.state == 'online' },
194
161
  topic: topicSplit[0]
195
162
  };
196
- // As long as we are busy creating the devices, the states are written to the queue.
197
- if (createDevicesOrReady == false) {
198
- incStatsQueue[incStatsQueue.length] = newMessage;
199
- break;
200
- }
201
- this.processDeviceMessage(newMessage);
163
+ statesController.processDeviceMessage(newMessage);
202
164
  }
203
165
  // States
204
166
  } else if (!messageObj.topic.includes('/')) {
205
- // As long as we are busy creating the devices, the states are written to the queue.
206
- if (createDevicesOrReady == false) {
207
- incStatsQueue[incStatsQueue.length] = messageObj;
208
- break;
209
- }
210
- this.processDeviceMessage(messageObj);
167
+ statesController.processDeviceMessage(messageObj);
211
168
  }
212
169
  }
213
170
  break;
214
171
  }
215
172
  }
216
173
 
217
- async processDeviceMessage(messageObj) {
218
- this.logDebug(`processDeviceMessage -> messageObj: ${JSON.stringify(messageObj)}`);
219
- // Is payload present?
220
- if (messageObj.payload == '') {
221
- return;
222
- }
223
-
224
- const device = groupCache.concat(deviceCache).find(x => x.id == messageObj.topic);
225
- if (device) {
226
- this.logDebug(`processDeviceMessage -> device: ${JSON.stringify(device)}`);
227
- try {
228
- this.setDeviceState(messageObj, device);
229
- } catch (error) {
230
- adapter.log.error(error);
231
- }
232
- }
233
- else {
234
- adapter.log.warn(`Device: ${messageObj.topic} not found`);
235
- }
236
- }
237
-
238
- async setDeviceState(messageObj, device) {
239
-
240
- if (debugDevices.includes(device.ieee_address)) {
241
- this.log.warn(`--->>> fromZ2M -> ${device.ieee_address} states: ${JSON.stringify(messageObj)}`);
242
- }
243
-
244
- for (const [key, value] of Object.entries(messageObj.payload)) {
245
- this.logDebug(`setDeviceState -> key: ${key}`);
246
- this.logDebug(`setDeviceState -> value: ${JSON.stringify(value)}`);
247
-
248
- let states;
249
- if (key == 'action') {
250
- states = device.states.filter(x => (x.prop && x.prop == key) && x.id == value);
251
- } else {
252
- states = device.states.filter(x => (x.prop && x.prop == key) || x.id == key);
253
- }
254
- this.logDebug(`setDeviceState -> states: ${JSON.stringify(states)}`);
255
-
256
- for (const state of states) {
257
- if (!state) {
258
- continue;
259
- }
260
-
261
- const stateName = `${device.ieee_address}.${state.id}`;
262
-
263
- try {
264
- if (state.getter) {
265
- await this.setStateAsync(stateName, state.getter(messageObj.payload), true);
266
- }
267
- else {
268
- await this.setStateAsync(stateName, value, true);
269
- }
270
- } catch (err) {
271
- this.log.warn(`Can not set ${stateName}`);
272
- }
273
- }
274
- }
275
- }
276
-
277
- async createDeviceDefinitions(cache, exposes) {
278
- clearArray(cache);
279
- for (const expose of exposes) {
280
- if (expose.definition != null) {
281
- // search for scenes in the endpoints and build them into an array
282
- let scenes = [];
283
- for (const key in expose.endpoints) {
284
- if (expose.endpoints[key].scenes) {
285
- scenes = scenes.concat(expose.endpoints[key].scenes);
286
- }
287
- }
288
-
289
- await defineDeviceFromExposes(cache, expose.friendly_name, expose.ieee_address, expose.definition, expose.power_source, scenes, useKelvin);
290
- }
291
- }
292
- }
293
-
294
- async createGroupDefinitions(cache, exposes) {
295
- clearArray(cache);
296
- for (const expose of exposes) {
297
- await defineGroupDevice(cache, expose.friendly_name, `group_${expose.id}`, expose.scenes, useKelvin);
298
- }
299
- }
300
-
301
- async createOrUpdateDevices(cache) {
302
- for (const device of cache) {
303
- const deviceName = device.id == device.ieee_address ? '' : device.id;
304
- if (!createCache[device.ieee_address] || createCache[device.ieee_address].common.name != deviceName) {
305
- const deviceObj = {
306
- type: 'device',
307
- common: {
308
- name: deviceName,
309
- },
310
-
311
- native: {}
312
- };
313
-
314
- if (!device.ieee_address.includes('group_')) {
315
- deviceObj.common.statusStates = {
316
- onlineId: `${this.name}.${this.instance}.${device.ieee_address}.available`
317
- };
318
- }
319
-
320
- //@ts-ignore
321
- await this.extendObjectAsync(device.ieee_address, deviceObj);
322
- createCache[device.ieee_address] = deviceObj;
323
- }
324
-
325
- // Here it is checked whether the scenes match the current data from z2m.
326
- // If necessary, scenes are automatically deleted from ioBroker.
327
- const sceneStates = await this.getStatesAsync(`${device.ieee_address}.scene_*`);
328
- const sceneIDs = Object.keys(sceneStates);
329
- for (const sceneID of sceneIDs) {
330
- const stateID = sceneID.split('.')[3];
331
- if (device.states.find(x => x.id == stateID) == null) {
332
- this.delObject(sceneID);
333
- }
334
- }
335
-
336
- for (const state of device.states) {
337
- if (!createCache[device.ieee_address][state.id] || createCache[device.ieee_address][state.id].name != state.name) {
338
- const iobState = await this.copyAndCleanStateObj(state);
339
- this.logDebug(`Orig. state: ${JSON.stringify(state)}`);
340
- this.logDebug(`Cleaned. state: ${JSON.stringify(iobState)}`);
341
-
342
- await this.extendObjectAsync(`${device.ieee_address}.${state.id}`, {
343
- type: 'state',
344
- common: iobState,
345
- native: {},
346
- });
347
- createCache[device.ieee_address][state.id] = state.name;
348
- }
349
- }
350
- }
351
- }
352
-
353
- async copyAndCleanStateObj(state) {
354
- const iobState = { ...state };
355
- const blacklistedKeys = [
356
- 'setter',
357
- 'setterOpt',
358
- 'getter',
359
- 'setattr',
360
- 'readable',
361
- 'writable',
362
- 'isOption',
363
- 'inOptions'
364
- ];
365
- for (const blacklistedKey of blacklistedKeys) {
366
- delete iobState[blacklistedKey];
367
- }
368
- return iobState;
369
- }
370
-
371
- async subscribeWritableStates() {
372
- await this.unsubscribeObjectsAsync('*');
373
-
374
- for (const device of groupCache.concat(deviceCache)) {
375
- for (const state of device.states) {
376
- if (state.write == true) {
377
- this.subscribeStatesAsync(`${device.ieee_address}.${state.id}`);
378
- }
379
- }
380
- }
381
- }
382
-
383
- async createZ2MMessage(id, state) {
384
-
385
- const splitedID = id.split('.');
386
-
387
- if (splitedID.length < 4) {
388
- this.log.warn(`state ${id} not valid`);
389
- return;
390
- }
391
-
392
- const ieee_address = splitedID[2];
393
- const stateName = splitedID[3];
394
-
395
- const device = groupCache.concat(deviceCache).find(d => d.ieee_address == ieee_address);
396
-
397
- if (!device) {
398
- return;
399
- }
400
-
401
- const deviceState = device.states.find(s => s.id == stateName);
402
-
403
- if (!deviceState) {
404
- return;
405
- }
406
-
407
- let stateVal = state.val;
408
- if (deviceState.setter) {
409
- stateVal = deviceState.setter(state.val);
410
- }
411
-
412
-
413
- let stateID = deviceState.id;
414
- if (deviceState.prop) {
415
- stateID = deviceState.prop;
416
- }
417
-
418
- let topic = `${device.ieee_address}/set`;
419
- if (device.ieee_address.includes('group_')) {
420
- topic = `${device.id}/set`;
421
- }
422
-
423
- const controlObj = {
424
- payload: {
425
- [stateID]: stateVal
426
- },
427
- topic: topic
428
- };
429
- // set stats with the mentioned role or ids always immediately to ack = true, because these are not reported back by Zigbee2MQTT
430
- if (isConnected == true && (['button'].includes(deviceState.role) || ['brightness_move', 'color_temp_move'].includes(stateID))) {
431
- this.setState(id, state, true);
432
- }
433
-
434
- return JSON.stringify(controlObj);
435
- }
436
-
437
- async proxyZ2MLogs(messageObj) {
438
- this.logDebug(`proxyZ2MLogs -> messageObj: ${JSON.stringify(messageObj)}`);
439
-
440
- const logMessage = messageObj.payload.message;
441
- if (logfilter.some(x => logMessage.includes(x))) {
442
- return;
443
- }
444
-
445
- const logLevel = messageObj.payload.level;
446
- switch (logLevel) {
447
- case 'debug':
448
- case 'info':
449
- case 'error':
450
- this.log[logLevel](logMessage);
451
- break;
452
- case 'warning':
453
- this.log.warn(logMessage);
454
- break;
455
- }
456
- }
457
-
458
- async logDebug(message) {
459
- if (debugLogEnabled == true) {
460
- this.log.debug(message);
461
- }
462
- }
463
-
464
- async setAllAvailableToFalse() {
465
- for (const device of deviceCache) {
466
- for (const state of device.states) {
467
- if (state.id == 'available') {
468
- await this.setStateAsync(`${device.ieee_address}.${state.id}`, false, true);
469
- }
470
- }
471
- }
472
- }
473
-
474
174
  async onUnload(callback) {
475
175
  try {
476
- await this.setAllAvailableToFalse();
176
+ await statesController.setAllAvailableToFalse();
477
177
  clearTimeout(ping);
478
178
  clearTimeout(pingTimeout);
479
179
  clearTimeout(autoRestartTimeout);
@@ -486,17 +186,19 @@ class Zigbee2mqtt extends core.Adapter {
486
186
 
487
187
  async onStateChange(id, state) {
488
188
  if (state && state.ack == false) {
489
- const message = await this.createZ2MMessage(id, state);
490
- wsClient.send(message);
491
-
492
189
  if (id.includes('info.debugmessages')) {
493
190
  debugDevices = state.val;
494
191
  this.setState(id, state.val, true);
192
+ return;
495
193
  }
496
194
  if (id.includes('info.logfilter')) {
497
195
  logfilter = state.val.split(';').filter(x => x); // filter removes empty strings here
498
196
  this.setState(id, state.val, true);
197
+ return;
499
198
  }
199
+
200
+ const message = await z2mController.createZ2MMessage(id, state) || { topic: '', payload: '' };
201
+ mqttClient.publish('zigbee2mqtt/' + message.topic, JSON.stringify(message.payload));
500
202
  }
501
203
  }
502
204
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "iobroker.zigbee2mqtt",
3
- "version": "0.2.0",
3
+ "version": "1.0.0",
4
4
  "description": "Zigbee2MQTT adapter for ioBroker",
5
5
  "author": {
6
6
  "name": "Dennis Rathjen",
@@ -21,7 +21,11 @@
21
21
  "dependencies": {
22
22
  "@alcalzone/release-script-plugin-iobroker": "^3.5.9",
23
23
  "@alcalzone/release-script-plugin-license": "^3.5.9",
24
- "@iobroker/adapter-core": "^2.6.6",
24
+ "@iobroker/adapter-core": "^2.6.7",
25
+ "aedes": "^0.48.0",
26
+ "aedes-persistence-nedb": "^2.0.3",
27
+ "mqtt": "^4.3.7",
28
+ "net": "^1.0.2",
25
29
  "ws": "^8.9.0"
26
30
  },
27
31
  "devDependencies": {
@@ -32,19 +36,19 @@
32
36
  "@types/chai": "^4.3.3",
33
37
  "@types/chai-as-promised": "^7.1.5",
34
38
  "@types/mocha": "^10.0.0",
35
- "@types/node": "^18.7.23",
39
+ "@types/node": "^18.8.3",
36
40
  "@types/proxyquire": "^1.3.28",
37
41
  "@types/sinon": "^10.0.13",
38
42
  "@types/sinon-chai": "^3.2.8",
39
43
  "chai": "^4.3.6",
40
44
  "chai-as-promised": "^7.1.1",
41
- "eslint": "^8.24.0",
45
+ "eslint": "^8.25.0",
42
46
  "eslint-config-prettier": "^8.5.0",
43
47
  "eslint-plugin-prettier": "^4.2.1",
44
48
  "mocha": "^10.0.0",
45
49
  "prettier": "^2.7.1",
46
50
  "proxyquire": "^2.1.3",
47
- "sinon": "^14.0.0",
51
+ "sinon": "^14.0.1",
48
52
  "sinon-chai": "^3.7.0",
49
53
  "typescript": "~4.8.4"
50
54
  },
package/lib/groups.js DELETED
@@ -1,68 +0,0 @@
1
- const states = require('./states').states;
2
- const utils = require('./utils');
3
-
4
- function defineGroupDevice(devices, groupID, ieee_address, scenes, useKelvin) {
5
- const newDevice = {
6
- id: groupID,
7
- ieee_address: ieee_address,
8
- icon: undefined,
9
- states: [
10
- states.state,
11
- states.brightness,
12
- states.color,
13
- states.brightness_move,
14
- states.colortemp_move,
15
- ],
16
- };
17
-
18
- const colortemp = {
19
- id: 'colortemp',
20
- prop: 'color_temp',
21
- name: 'Color temperature',
22
- icon: undefined,
23
- role: 'level.color.temperature',
24
- write: true,
25
- read: true,
26
- type: 'number',
27
- unit: useKelvin == true ? 'K' : 'mired',
28
- setter: (value) => {
29
- return utils.toMired(value);
30
- },
31
- getter: (payload) => {
32
- if (useKelvin == true) {
33
- return utils.miredKelvinConversion(payload.color_temp);
34
- }
35
- else {
36
- return payload.color_temp;
37
- }
38
- },
39
- };
40
-
41
- // @ts-ignore
42
- newDevice.states.push(colortemp);
43
-
44
- // Create buttons for scenes
45
- for (const scene of scenes) {
46
- const sceneSate = {
47
- id: `scene_${scene.id}`,
48
- prop: `scene_recall`,
49
- name: scene.name,
50
- icon: undefined,
51
- role: 'button',
52
- write: true,
53
- read: true,
54
- type: 'boolean',
55
- setter: (value) => (value) ? scene.id : undefined
56
- };
57
- // @ts-ignore
58
- newDevice.states.push(sceneSate);
59
- }
60
-
61
- // if the device is already present in the cache, remove it
62
- utils.removeDeviceByIeee(devices, ieee_address);
63
- devices.push(newDevice);
64
- }
65
-
66
- module.exports = {
67
- defineGroupDevice: defineGroupDevice,
68
- };