iobroker.zigbee2mqtt 2.1.1 → 2.2.1

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.
@@ -3,242 +3,248 @@ const defineDeviceFromExposes = require('./exposes').defineDeviceFromExposes;
3
3
  const utils = require('./utils');
4
4
  const colors = require('./colors.js');
5
5
  const rgb = require('./rgb.js');
6
- const createCache = {};
6
+ //const createCache = {};
7
7
 
8
8
  class DeviceController {
9
- constructor(adapter, deviceCache, groupCache, config) {
10
- this.adapter = adapter;
11
- this.groupCache = groupCache;
12
- this.deviceCache = deviceCache;
13
- this.config = config;
14
- }
15
-
16
- async createDeviceDefinitions(exposes) {
17
- utils.clearArray(this.deviceCache);
18
- for (const expose of exposes) {
19
- if (expose.definition != null) {
20
- // search for scenes in the endpoints and build them into an array
21
- let scenes = [];
22
- for (const key in expose.endpoints) {
23
- if (expose.endpoints[key].scenes) {
24
- scenes = scenes.concat(expose.endpoints[key].scenes);
25
- }
26
- }
27
- // if the device is already present in the cache, remove it
28
- this.removeDeviceByIeee(this.deviceCache, expose.ieee_address);
29
- defineDeviceFromExposes(this.deviceCache, expose.friendly_name, expose.ieee_address, expose.definition, expose.power_source, scenes, this.config);
30
- }
31
- }
32
- }
33
-
34
-
35
- async defineGroupDevice(groupID, ieee_address, scenes) {
36
- const newDevice = {
37
- id: groupID,
38
- ieee_address: ieee_address,
39
- icon: undefined,
40
- states: [
41
- states.state,
42
- states.brightness,
43
- //states.color,
44
- states.brightness_move,
45
- states.colortemp_move,
46
- ],
47
- };
48
-
49
- const color = {
50
- id: 'color',
51
- prop: 'color',
52
- name: 'Color',
53
- icon: undefined,
54
- role: 'level.color.rgb',
55
- write: true,
56
- read: true,
57
- type: 'string',
58
- setter: (value) => {
59
- let xy = [0, 0];
60
- const rgbcolor = colors.ParseColor(value);
61
- xy = rgb.rgb_to_cie(rgbcolor.r, rgbcolor.g, rgbcolor.b);
62
- return {
63
- x: xy[0],
64
- y: xy[1]
65
- };
66
- },
67
- getter: payload => {
68
- if (payload.color_mode != 'xy' && this.config.colorTempSyncColor == false) {
69
- return undefined;
70
- }
71
- if (payload.color && payload.color.x && payload.color.y) {
72
- const colorval = rgb.cie_to_rgb(payload.color.x, payload.color.y);
73
- return '#' + utils.decimalToHex(colorval[0]) + utils.decimalToHex(colorval[1]) + utils.decimalToHex(colorval[2]);
74
- } else {
75
- return undefined;
76
- }
77
- },
78
- };
79
-
80
- // @ts-ignore
81
- newDevice.states.push(color);
82
-
83
- const colortemp = {
84
- id: 'colortemp',
85
- prop: 'color_temp',
86
- name: 'Color temperature',
87
- icon: undefined,
88
- role: 'level.color.temperature',
89
- write: true,
90
- read: true,
91
- type: 'number',
92
- min: this.config.useKelvin == true ? utils.miredKelvinConversion(500) : 150,
93
- max: this.config.useKelvin == true ? utils.miredKelvinConversion(150) : 500,
94
- unit: this.config.useKelvin == true ? 'K' : 'mired',
95
- setter: (value) => {
96
- return utils.toMired(value);
97
- },
98
- getter: (payload) => {
99
- if (payload.color_mode != 'color_temp') {
100
- return undefined;
101
- }
102
- if (this.config.useKelvin == true) {
103
- return utils.miredKelvinConversion(payload.color_temp);
104
- } else {
105
- return payload.color_temp;
106
- }
107
- },
108
- };
109
-
110
- // @ts-ignore
111
- newDevice.states.push(colortemp);
112
-
113
- // Create buttons for scenes
114
- for (const scene of scenes) {
115
- const sceneSate = {
116
- id: `scene_${scene.id}`,
117
- prop: `scene_recall`,
118
- name: scene.name,
119
- icon: undefined,
120
- role: 'button',
121
- write: true,
122
- read: true,
123
- type: 'boolean',
124
- setter: (value) => (value) ? scene.id : undefined
125
- };
126
- // @ts-ignore
127
- newDevice.states.push(sceneSate);
128
- }
129
-
130
- // if the device is already present in the cache, remove it
131
- this.removeDeviceByIeee(this.groupCache, ieee_address);
132
- this.groupCache.push(newDevice);
133
- }
134
-
135
- async createGroupDefinitions(exposes) {
136
- utils.clearArray(this.groupCache);
137
- for (const expose of exposes) {
138
- await this.defineGroupDevice(expose.friendly_name, `group_${expose.id}`, expose.scenes);
139
- }
140
- }
141
-
142
- async createOrUpdateDevices() {
143
- for (const device of this.groupCache.concat(this.deviceCache)) {
144
- const deviceName = device.id == device.ieee_address ? '' : device.id;
145
- if (!createCache[device.ieee_address] || createCache[device.ieee_address].common.name != deviceName) {
146
- const deviceObj = {
147
- type: 'device',
148
- common: {
149
- name: deviceName,
150
- },
151
-
152
- native: {}
153
- };
154
-
155
- if (!device.ieee_address.includes('group_')) {
156
- deviceObj.common.statusStates = {
157
- onlineId: `${this.adapter.name}.${this.adapter.instance}.${device.ieee_address}.available`
158
- };
159
- }
160
-
161
- //@ts-ignore
162
- await this.adapter.extendObjectAsync(device.ieee_address, deviceObj);
163
- createCache[device.ieee_address] = deviceObj;
164
- }
165
-
166
- // Here it is checked whether the scenes match the current data from z2m.
167
- // If necessary, scenes are automatically deleted from ioBroker.
168
- const sceneStates = await this.adapter.getStatesAsync(`${device.ieee_address}.scene_*`);
169
- const sceneIDs = Object.keys(sceneStates);
170
- for (const sceneID of sceneIDs) {
171
- const stateID = sceneID.split('.')[3];
172
- if (device.states.find(x => x.id == stateID) == null) {
173
- this.adapter.delObject(sceneID);
174
- }
175
- }
176
-
177
- for (const state of device.states) {
178
- if (!createCache[device.ieee_address][state.id] || createCache[device.ieee_address][state.id].name != state.name) {
179
- const iobState = await this.copyAndCleanStateObj(state);
180
- await this.adapter.extendObjectAsync(`${device.ieee_address}.${state.id}`, {
181
- type: 'state',
182
- common: iobState,
183
- native: {},
184
- });
185
- createCache[device.ieee_address][state.id] = state.name;
186
- }
187
- }
188
- }
189
- }
190
-
191
- async renameDeviceInCache(messageObj) {
192
- const renamedDevice = this.groupCache.concat(this.deviceCache).find(x => x.id == messageObj.payload.data.from);
193
- if (renamedDevice) {
194
- renamedDevice.id = messageObj.payload.data.to;
195
- }
196
- }
197
-
198
- processRemoveEvent(messageObj) {
199
- let ieee_address = undefined;
200
- if (messageObj.payload && messageObj.payload.type == 'device_leave') {
201
- ieee_address = messageObj.payload.data.ieee_address;
202
- }
203
-
204
- //{"data":{"block":false,"force":true,"id":"0xa4c138c954baaf54"},"status":"ok","transaction":"zhvjf-5"}
205
- if (messageObj.payload && messageObj.payload.data) {
206
- ieee_address = messageObj.payload.data.id;
207
- }
208
-
209
- if (ieee_address != undefined) {
210
- this.adapter.setStateAsync(`${ieee_address}.available`, false, true);
211
- this.adapter.extendObject(`${ieee_address}`, { common: { name: 'Device removed!', } });
212
- }
213
- }
214
-
215
- removeDeviceByIeee(devices, ieee_address) {
216
- const idx = devices.findIndex(x => x.ieee_address == ieee_address);
217
- if (idx > -1) {
218
- devices.splice(idx, 1);
219
- }
220
- }
221
-
222
- async copyAndCleanStateObj(state) {
223
- const iobState = { ...state };
224
- const blacklistedKeys = [
225
- 'setter',
226
- 'setterOpt',
227
- 'getter',
228
- 'setattr',
229
- 'readable',
230
- 'writable',
231
- 'isOption',
232
- 'inOptions',
233
- 'isEvent',
234
- ];
235
- for (const blacklistedKey of blacklistedKeys) {
236
- delete iobState[blacklistedKey];
237
- }
238
- return iobState;
239
- }
9
+ constructor(adapter, deviceCache, groupCache, config, createCache) {
10
+ this.adapter = adapter;
11
+ this.groupCache = groupCache;
12
+ this.deviceCache = deviceCache;
13
+ this.config = config;
14
+ this.createCache = createCache;
15
+ }
16
+
17
+ async createDeviceDefinitions(exposes) {
18
+ utils.clearArray(this.deviceCache);
19
+ for (const expose of exposes) {
20
+ if (expose.definition != null) {
21
+ // search for scenes in the endpoints and build them into an array
22
+ let scenes = [];
23
+ for (const key in expose.endpoints) {
24
+ if (expose.endpoints[key].scenes) {
25
+ scenes = scenes.concat(expose.endpoints[key].scenes);
26
+ }
27
+ }
28
+ // if the device is already present in the cache, remove it
29
+ this.removeDeviceByIeee(this.deviceCache, expose.ieee_address);
30
+ defineDeviceFromExposes(this.deviceCache, expose.friendly_name, expose.ieee_address, expose.definition, expose.power_source, scenes, this.config);
31
+ }
32
+ }
33
+ }
34
+
35
+
36
+ async defineGroupDevice(groupID, ieee_address, scenes) {
37
+ const newDevice = {
38
+ id: groupID,
39
+ ieee_address: ieee_address,
40
+ icon: undefined,
41
+ states: [
42
+ states.state,
43
+ states.brightness,
44
+ //states.color,
45
+ states.brightness_move,
46
+ states.colortemp_move,
47
+ ],
48
+ };
49
+
50
+ const color = {
51
+ id: 'color',
52
+ prop: 'color',
53
+ name: 'Color',
54
+ icon: undefined,
55
+ role: 'level.color.rgb',
56
+ write: true,
57
+ read: true,
58
+ type: 'string',
59
+ setter: (value) => {
60
+ let xy = [0, 0];
61
+ const rgbcolor = colors.ParseColor(value);
62
+ xy = rgb.rgb_to_cie(rgbcolor.r, rgbcolor.g, rgbcolor.b);
63
+ return {
64
+ x: xy[0],
65
+ y: xy[1]
66
+ };
67
+ },
68
+ getter: payload => {
69
+ if (payload.color_mode != 'xy' && this.config.colorTempSyncColor == false) {
70
+ return undefined;
71
+ }
72
+ if (payload.color && payload.color.x && payload.color.y) {
73
+ const colorval = rgb.cie_to_rgb(payload.color.x, payload.color.y);
74
+ return '#' + utils.decimalToHex(colorval[0]) + utils.decimalToHex(colorval[1]) + utils.decimalToHex(colorval[2]);
75
+ } else {
76
+ return undefined;
77
+ }
78
+ },
79
+ };
80
+
81
+ // @ts-ignore
82
+ newDevice.states.push(color);
83
+
84
+ const colortemp = {
85
+ id: 'colortemp',
86
+ prop: 'color_temp',
87
+ name: 'Color temperature',
88
+ icon: undefined,
89
+ role: 'level.color.temperature',
90
+ write: true,
91
+ read: true,
92
+ type: 'number',
93
+ min: this.config.useKelvin == true ? utils.miredKelvinConversion(500) : 150,
94
+ max: this.config.useKelvin == true ? utils.miredKelvinConversion(150) : 500,
95
+ unit: this.config.useKelvin == true ? 'K' : 'mired',
96
+ setter: (value) => {
97
+ return utils.toMired(value);
98
+ },
99
+ getter: (payload) => {
100
+ if (payload.color_mode != 'color_temp') {
101
+ return undefined;
102
+ }
103
+ if (this.config.useKelvin == true) {
104
+ return utils.miredKelvinConversion(payload.color_temp);
105
+ } else {
106
+ return payload.color_temp;
107
+ }
108
+ },
109
+ };
110
+
111
+ // @ts-ignore
112
+ newDevice.states.push(colortemp);
113
+
114
+ // Create buttons for scenes
115
+ for (const scene of scenes) {
116
+ const sceneSate = {
117
+ id: `scene_${scene.id}`,
118
+ prop: `scene_recall`,
119
+ name: scene.name,
120
+ icon: undefined,
121
+ role: 'button',
122
+ write: true,
123
+ read: true,
124
+ type: 'boolean',
125
+ setter: (value) => (value) ? scene.id : undefined
126
+ };
127
+ // @ts-ignore
128
+ newDevice.states.push(sceneSate);
129
+ }
130
+
131
+ // if the device is already present in the cache, remove it
132
+ this.removeDeviceByIeee(this.groupCache, ieee_address);
133
+ this.groupCache.push(newDevice);
134
+ }
135
+
136
+ async createGroupDefinitions(exposes) {
137
+ utils.clearArray(this.groupCache);
138
+ for (const expose of exposes) {
139
+ await this.defineGroupDevice(expose.friendly_name, `group_${expose.id}`, expose.scenes);
140
+ }
141
+ }
142
+
143
+ async createOrUpdateDevices() {
144
+ for (const device of this.groupCache.concat(this.deviceCache)) {
145
+ const deviceName = device.id == device.ieee_address ? '' : device.id;
146
+ if (!this.createCache[device.ieee_address] || this.createCache[device.ieee_address].name != deviceName) {
147
+ const deviceObj = {
148
+ type: 'device',
149
+ common: {
150
+ name: deviceName,
151
+ },
152
+
153
+ native: {}
154
+ };
155
+
156
+ if (!device.ieee_address.includes('group_')) {
157
+ deviceObj.common.statusStates = {
158
+ onlineId: `${this.adapter.name}.${this.adapter.instance}.${device.ieee_address}.available`
159
+ };
160
+ }
161
+
162
+ //@ts-ignore
163
+ await this.adapter.extendObjectAsync(device.ieee_address, deviceObj);
164
+ this.createCache[device.ieee_address] = { name: deviceName };
165
+ }
166
+
167
+ // Here it is checked whether the scenes match the current data from z2m.
168
+ // If necessary, scenes are automatically deleted from ioBroker.
169
+ const sceneStates = await this.adapter.getStatesAsync(`${device.ieee_address}.scene_*`);
170
+ const sceneIDs = Object.keys(sceneStates);
171
+ for (const sceneID of sceneIDs) {
172
+ const stateID = sceneID.split('.')[3];
173
+ if (device.states.find(x => x.id == stateID) == null) {
174
+ this.adapter.delObject(sceneID);
175
+ }
176
+ }
177
+
178
+ for (const state of device.states) {
179
+ if (!this.createCache[device.ieee_address][state.id] || this.createCache[device.ieee_address][state.id].name != state.name) {
180
+ const iobState = {
181
+ type: 'state',
182
+ common: await this.copyAndCleanStateObj(state),
183
+ native: {},
184
+ };
185
+
186
+ await this.adapter.extendObjectAsync(`${device.ieee_address}.${state.id}`, iobState);
187
+ this.createCache[device.ieee_address][state.id] = { name: state.name, created: true };
188
+ }
189
+ }
190
+ }
191
+ }
192
+
193
+ async renameDeviceInCache(messageObj) {
194
+ const renamedDevice = this.groupCache.concat(this.deviceCache).find(x => x.id == messageObj.payload.data.from);
195
+ if (renamedDevice) {
196
+ renamedDevice.id = messageObj.payload.data.to;
197
+ }
198
+ }
199
+
200
+ processRemoveEvent(messageObj) {
201
+ let ieee_address = undefined;
202
+ if (messageObj.payload && messageObj.payload.type == 'device_leave') {
203
+ ieee_address = messageObj.payload.data.ieee_address;
204
+ }
205
+
206
+ //{"data":{"block":false,"force":true,"id":"0xa4c138c954baaf54"},"status":"ok","transaction":"zhvjf-5"}
207
+ if (messageObj.payload && messageObj.payload.data) {
208
+ const device = this.deviceCache.find(x => x.id == messageObj.payload.data.id);
209
+ if (device) {
210
+ ieee_address = device.ieee_address;
211
+ }
212
+ }
213
+
214
+ if (ieee_address != undefined) {
215
+ this.adapter.setStateAsync(`${ieee_address}.available`, false, true);
216
+ this.adapter.extendObject(`${ieee_address}`, { common: { name: 'Device removed!', } });
217
+ delete this.createCache[ieee_address];
218
+ }
219
+ }
220
+
221
+ removeDeviceByIeee(devices, ieee_address) {
222
+ const idx = devices.findIndex(x => x.ieee_address == ieee_address);
223
+ if (idx > -1) {
224
+ devices.splice(idx, 1);
225
+ }
226
+ }
227
+
228
+ async copyAndCleanStateObj(state) {
229
+ const iobState = { ...state };
230
+ const blacklistedKeys = [
231
+ 'setter',
232
+ 'setterOpt',
233
+ 'getter',
234
+ 'setattr',
235
+ 'readable',
236
+ 'writable',
237
+ 'isOption',
238
+ 'inOptions',
239
+ 'isEvent',
240
+ ];
241
+ for (const blacklistedKey of blacklistedKeys) {
242
+ delete iobState[blacklistedKey];
243
+ }
244
+ return iobState;
245
+ }
240
246
  }
241
247
 
242
248
  module.exports = {
243
- DeviceController
249
+ DeviceController
244
250
  };