iobroker.zigbee2mqtt 2.1.1 → 2.2.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/README.md +6 -0
- package/io-package.json +14 -13
- package/lib/check.js +30 -30
- package/lib/colors.js +442 -443
- package/lib/deviceController.js +235 -232
- package/lib/exposes.js +867 -859
- package/lib/messages.js +31 -31
- package/lib/mqttServerController.js +30 -30
- package/lib/nonGenericDevicesExtension.js +47 -0
- package/lib/rgb.js +227 -203
- package/lib/states.js +6374 -6374
- package/lib/statesController.js +129 -129
- package/lib/utils.js +82 -82
- package/lib/websocketController.js +58 -58
- package/lib/z2mController.js +69 -64
- package/main.js +201 -201
- package/package.json +1 -1
package/lib/deviceController.js
CHANGED
|
@@ -6,239 +6,242 @@ const rgb = require('./rgb.js');
|
|
|
6
6
|
const createCache = {};
|
|
7
7
|
|
|
8
8
|
class DeviceController {
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
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
|
+
const device = this.deviceCache.find(x => x.id == messageObj.payload.data.id);
|
|
207
|
+
if (device) {
|
|
208
|
+
ieee_address = device.ieee_address;
|
|
209
|
+
}
|
|
210
|
+
}
|
|
211
|
+
|
|
212
|
+
if (ieee_address != undefined) {
|
|
213
|
+
this.adapter.setStateAsync(`${ieee_address}.available`, false, true);
|
|
214
|
+
this.adapter.extendObject(`${ieee_address}`, { common: { name: 'Device removed!', } });
|
|
215
|
+
}
|
|
216
|
+
}
|
|
217
|
+
|
|
218
|
+
removeDeviceByIeee(devices, ieee_address) {
|
|
219
|
+
const idx = devices.findIndex(x => x.ieee_address == ieee_address);
|
|
220
|
+
if (idx > -1) {
|
|
221
|
+
devices.splice(idx, 1);
|
|
222
|
+
}
|
|
223
|
+
}
|
|
224
|
+
|
|
225
|
+
async copyAndCleanStateObj(state) {
|
|
226
|
+
const iobState = { ...state };
|
|
227
|
+
const blacklistedKeys = [
|
|
228
|
+
'setter',
|
|
229
|
+
'setterOpt',
|
|
230
|
+
'getter',
|
|
231
|
+
'setattr',
|
|
232
|
+
'readable',
|
|
233
|
+
'writable',
|
|
234
|
+
'isOption',
|
|
235
|
+
'inOptions',
|
|
236
|
+
'isEvent',
|
|
237
|
+
];
|
|
238
|
+
for (const blacklistedKey of blacklistedKeys) {
|
|
239
|
+
delete iobState[blacklistedKey];
|
|
240
|
+
}
|
|
241
|
+
return iobState;
|
|
242
|
+
}
|
|
240
243
|
}
|
|
241
244
|
|
|
242
245
|
module.exports = {
|
|
243
|
-
|
|
246
|
+
DeviceController
|
|
244
247
|
};
|