iobroker.zigbee 1.7.5 → 1.8.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/LICENSE +1 -1
- package/README.md +22 -29
- package/admin/admin.js +494 -469
- package/admin/i18n/de/translations.json +108 -0
- package/admin/i18n/en/translations.json +108 -0
- package/admin/i18n/es/translations.json +102 -0
- package/admin/i18n/fr/translations.json +108 -0
- package/admin/i18n/it/translations.json +102 -0
- package/admin/i18n/nl/translations.json +108 -0
- package/admin/i18n/pl/translations.json +108 -0
- package/admin/i18n/pt/translations.json +102 -0
- package/admin/i18n/ru/translations.json +108 -0
- package/admin/i18n/uk/translations.json +108 -0
- package/admin/i18n/zh-cn/translations.json +102 -0
- package/admin/index_m.html +1 -1
- package/admin/tab_m.html +44 -3
- package/admin/words.js +107 -108
- package/io-package.json +326 -357
- package/lib/backup.js +2 -2
- package/lib/binding.js +23 -24
- package/lib/colors.js +16 -14
- package/lib/commands.js +89 -82
- package/lib/developer.js +6 -7
- package/lib/devices.js +145 -154
- package/lib/exclude.js +30 -36
- package/lib/exposes.js +106 -111
- package/lib/groups.js +53 -54
- package/lib/json.js +3 -4
- package/lib/networkmap.js +2 -2
- package/lib/ota.js +23 -15
- package/lib/rgb.js +47 -44
- package/lib/seriallist.js +21 -10
- package/lib/states.js +488 -498
- package/lib/statescontroller.js +170 -164
- package/lib/utils.js +22 -21
- package/lib/zbBaseExtension.js +4 -4
- package/lib/zbDelayedAction.js +5 -13
- package/lib/zbDeviceAvailability.js +47 -44
- package/lib/zbDeviceConfigure.js +18 -23
- package/lib/zbDeviceEvent.js +3 -4
- package/lib/zigbeecontroller.js +97 -100
- package/main.js +149 -133
- package/package.json +33 -19
- package/.eslintignore +0 -2
- package/.eslintrc.json +0 -37
- package/.github/FUNDING.yml +0 -3
- package/.github/stale.yml +0 -13
- package/.github/workflows/test-and-release.yml +0 -151
- package/.travis/wiki.sh +0 -28
- package/admin/adapter-settings.js +0 -244
package/lib/statescontroller.js
CHANGED
|
@@ -7,9 +7,8 @@ const getZbId = require('./utils').getZbId;
|
|
|
7
7
|
const fs = require('fs');
|
|
8
8
|
const request = require('request');
|
|
9
9
|
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
10
|
+
let savedDeviceNames = {};
|
|
11
|
+
let knownUndefinedDevices = {};
|
|
13
12
|
|
|
14
13
|
class StatesController extends EventEmitter {
|
|
15
14
|
constructor(adapter) {
|
|
@@ -18,19 +17,16 @@ class StatesController extends EventEmitter {
|
|
|
18
17
|
this.adapter.on('stateChange', this.onStateChange.bind(this));
|
|
19
18
|
this.query_device_block = [];
|
|
20
19
|
this.debugDevices = undefined;
|
|
21
|
-
|
|
20
|
+
const fn = adapter.expandFileName('dev_names.json');
|
|
22
21
|
this.dev_names_fn = fn.replace('.', '_');
|
|
23
22
|
this.retTimeoutHandle = null;
|
|
24
23
|
fs.readFile(this.dev_names_fn, (err, data) => {
|
|
25
24
|
if (!err) {
|
|
26
|
-
try
|
|
27
|
-
{
|
|
25
|
+
try {
|
|
28
26
|
savedDeviceNames = JSON.parse(data);
|
|
29
|
-
}
|
|
30
|
-
catch
|
|
31
|
-
{
|
|
27
|
+
} catch {
|
|
32
28
|
savedDeviceNames = {};
|
|
33
|
-
}
|
|
29
|
+
}
|
|
34
30
|
}
|
|
35
31
|
});
|
|
36
32
|
}
|
|
@@ -55,68 +51,77 @@ class StatesController extends EventEmitter {
|
|
|
55
51
|
this.adapter.sendError(error, message);
|
|
56
52
|
}
|
|
57
53
|
|
|
58
|
-
retainDeviceNames()
|
|
59
|
-
{
|
|
54
|
+
retainDeviceNames() {
|
|
60
55
|
clearTimeout(this.retTimeoutHandle);
|
|
61
|
-
this.retTimeoutHanlde = setTimeout(()=> {
|
|
62
|
-
fs.writeFile(this.dev_names_fn, JSON.stringify(savedDeviceNames, null, 2),
|
|
63
|
-
if (err)
|
|
64
|
-
this.error(
|
|
65
|
-
else
|
|
56
|
+
this.retTimeoutHanlde = setTimeout(() => {
|
|
57
|
+
fs.writeFile(this.dev_names_fn, JSON.stringify(savedDeviceNames, null, 2), err => {
|
|
58
|
+
if (err) {
|
|
59
|
+
this.error(`error saving device names: ${JSON.stringify(err)}`);
|
|
60
|
+
} else {
|
|
66
61
|
this.debug('saved device names');
|
|
67
|
-
|
|
62
|
+
}
|
|
63
|
+
});
|
|
64
|
+
}, 5000);
|
|
68
65
|
}
|
|
69
66
|
|
|
70
67
|
getDebugDevices() {
|
|
71
68
|
this.debugDevices = [];
|
|
72
|
-
this.adapter.getState(this.adapter.namespace
|
|
69
|
+
this.adapter.getState(`${this.adapter.namespace}.info.debugmessages`, (err, state) => {
|
|
73
70
|
if (state) {
|
|
74
|
-
if (typeof(state.val) == 'string' && state.val.length > 2)
|
|
75
|
-
|
|
71
|
+
if (typeof(state.val) == 'string' && state.val.length > 2) {
|
|
72
|
+
this.debugDevices = state.val.split(';');
|
|
73
|
+
}
|
|
74
|
+
this.info(`debug devices set to ${JSON.stringify(this.debugDevices)}`);
|
|
76
75
|
} else {
|
|
77
76
|
this.adapter.setObject('info.debugmessages', {
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
77
|
+
type: 'state',
|
|
78
|
+
common: {
|
|
79
|
+
name: 'Log changes as warnings for',
|
|
80
|
+
role: '',
|
|
81
|
+
type: 'string',
|
|
82
|
+
read: true,
|
|
83
|
+
write: true,
|
|
85
84
|
},
|
|
86
|
-
|
|
85
|
+
native: {},
|
|
87
86
|
});
|
|
88
87
|
}
|
|
89
88
|
});
|
|
90
89
|
this.adapter.setObject('info.undefinedDevices', {
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
90
|
+
type: 'state',
|
|
91
|
+
common: {
|
|
92
|
+
name: 'Recorded undefined devices',
|
|
93
|
+
role: '',
|
|
94
|
+
type: 'string',
|
|
95
|
+
read: true,
|
|
96
|
+
write: false,
|
|
98
97
|
},
|
|
99
|
-
|
|
98
|
+
native: {},
|
|
100
99
|
});
|
|
101
100
|
this.adapter.setStateAsync(`info.undefinedDevices`, JSON.stringify(knownUndefinedDevices), true);
|
|
102
101
|
}
|
|
103
102
|
|
|
104
103
|
onStateChange(id, state){
|
|
105
|
-
if (!this.adapter.zbController || !this.adapter.zbController.connected())
|
|
106
|
-
|
|
104
|
+
if (!this.adapter.zbController || !this.adapter.zbController.connected()) {
|
|
105
|
+
return;
|
|
106
|
+
}
|
|
107
|
+
if (this.debugDevices === undefined) {
|
|
108
|
+
this.getDebugDevices();
|
|
109
|
+
}
|
|
107
110
|
if (state && !state.ack) {
|
|
108
|
-
if (id.endsWith('pairingCountdown') || id.endsWith('pairingMessage') || id.endsWith('connection'))
|
|
111
|
+
if (id.endsWith('pairingCountdown') || id.endsWith('pairingMessage') || id.endsWith('connection')) {
|
|
112
|
+
return;
|
|
113
|
+
}
|
|
109
114
|
if (id.endsWith('debugmessages')) {
|
|
110
|
-
if (typeof(state.val) == 'string' && state.val.length > 2)
|
|
115
|
+
if (typeof(state.val) == 'string' && state.val.length > 2) {
|
|
111
116
|
this.debugDevices = state.val.split(';');
|
|
112
|
-
|
|
117
|
+
|
|
118
|
+
} else {
|
|
113
119
|
this.debugDevices = [];
|
|
114
120
|
}
|
|
115
121
|
return;
|
|
116
122
|
}
|
|
117
123
|
for (const addressPart of this.debugDevices) {
|
|
118
|
-
if (typeof(id) == 'string' && id.indexOf(addressPart) > -1)
|
|
119
|
-
{
|
|
124
|
+
if (typeof(id) == 'string' && id.indexOf(addressPart) > -1) {
|
|
120
125
|
this.warn(`ELEVATED: User stateChange ${id} ${JSON.stringify(state)}`);
|
|
121
126
|
break;
|
|
122
127
|
}
|
|
@@ -134,7 +139,9 @@ class StatesController extends EventEmitter {
|
|
|
134
139
|
this.adapter.getObject(devId, (err, obj) => {
|
|
135
140
|
if (obj) {
|
|
136
141
|
const model = obj.common.type;
|
|
137
|
-
if (!model)
|
|
142
|
+
if (!model) {
|
|
143
|
+
return;
|
|
144
|
+
}
|
|
138
145
|
if (obj.common.deactivated) {
|
|
139
146
|
this.debug('State Change detected on deactivated Device - ignored');
|
|
140
147
|
return;
|
|
@@ -142,9 +149,8 @@ class StatesController extends EventEmitter {
|
|
|
142
149
|
if (model === 'group') {
|
|
143
150
|
deviceId = parseInt(deviceId.replace('0xgroup_', ''));
|
|
144
151
|
}
|
|
145
|
-
this.collectOptions(id.split('.')[2], model, options =>
|
|
146
|
-
this.publishFromState(deviceId, model, stateKey, state, options);
|
|
147
|
-
});
|
|
152
|
+
this.collectOptions(id.split('.')[2], model, options =>
|
|
153
|
+
this.publishFromState(deviceId, model, stateKey, state, options));
|
|
148
154
|
}
|
|
149
155
|
});
|
|
150
156
|
}
|
|
@@ -158,7 +164,7 @@ class StatesController extends EventEmitter {
|
|
|
158
164
|
callback(result);
|
|
159
165
|
return;
|
|
160
166
|
}
|
|
161
|
-
const states = devStates.states.filter(
|
|
167
|
+
const states = devStates.states.filter(statedesc => statedesc.isOption || statedesc.inOptions);
|
|
162
168
|
if (!states) {
|
|
163
169
|
callback(result);
|
|
164
170
|
return;
|
|
@@ -167,7 +173,7 @@ class StatesController extends EventEmitter {
|
|
|
167
173
|
try {
|
|
168
174
|
const len = states.length;
|
|
169
175
|
states.forEach(statedesc => {
|
|
170
|
-
const id = this.adapter.namespace
|
|
176
|
+
const id = `${this.adapter.namespace}.${devId}.${statedesc.id}`;
|
|
171
177
|
this.adapter.getState(id, (err, state) => {
|
|
172
178
|
cnt = cnt + 1;
|
|
173
179
|
if (!err && state) {
|
|
@@ -178,16 +184,18 @@ class StatesController extends EventEmitter {
|
|
|
178
184
|
}
|
|
179
185
|
});
|
|
180
186
|
});
|
|
181
|
-
if (!len)
|
|
182
|
-
|
|
187
|
+
if (!len) {
|
|
188
|
+
callback(result);
|
|
189
|
+
}
|
|
190
|
+
} catch (error) {
|
|
183
191
|
this.sendError(error);
|
|
184
192
|
this.error(`Error collectOptions for ${devId}. Error: ${error.stack}`);
|
|
185
|
-
|
|
193
|
+
}
|
|
186
194
|
}
|
|
187
195
|
|
|
188
196
|
async getDevStates(deviceId, model) {
|
|
189
197
|
try {
|
|
190
|
-
let states
|
|
198
|
+
let states;
|
|
191
199
|
let stateModel;
|
|
192
200
|
if (model === 'group') {
|
|
193
201
|
states = statesMapping.groupStates;
|
|
@@ -200,7 +208,7 @@ class StatesController extends EventEmitter {
|
|
|
200
208
|
}
|
|
201
209
|
else {
|
|
202
210
|
knownUndefinedDevices[deviceId] = 1;
|
|
203
|
-
this.error(
|
|
211
|
+
this.error(`Device ${deviceId} "${model}" not described in statesMapping.`);
|
|
204
212
|
}
|
|
205
213
|
this.adapter.setStateAsync(`info.undefinedDevices`, JSON.stringify(knownUndefinedDevices), true);
|
|
206
214
|
states = statesMapping.commonStates;
|
|
@@ -209,11 +217,12 @@ class StatesController extends EventEmitter {
|
|
|
209
217
|
}
|
|
210
218
|
if (typeof states === 'function' && !states.prototype) {
|
|
211
219
|
const entity = await this.adapter.zbController.resolveEntity(deviceId);
|
|
212
|
-
if (entity)
|
|
220
|
+
if (entity) {
|
|
213
221
|
states = states(entity);
|
|
222
|
+
}
|
|
214
223
|
}
|
|
215
224
|
}
|
|
216
|
-
return {states
|
|
225
|
+
return {states, stateModel};
|
|
217
226
|
} catch (error) {
|
|
218
227
|
this.sendError(error);
|
|
219
228
|
this.error(`Error getDevStates for ${deviceId}. Error: ${error.stack}`);
|
|
@@ -224,8 +233,7 @@ class StatesController extends EventEmitter {
|
|
|
224
233
|
if (this.debugDevices === undefined) this.getDebugDevices();
|
|
225
234
|
this.debug(`Change state '${stateKey}' at device ${deviceId} type '${model}'`);
|
|
226
235
|
for (const addressPart of this.debugDevices) {
|
|
227
|
-
if (typeof(deviceId) == 'string' && deviceId.
|
|
228
|
-
{
|
|
236
|
+
if (typeof(deviceId) == 'string' && deviceId.includes(addressPart)) {
|
|
229
237
|
this.warn(`ELEVATED Change state '${stateKey}' at device ${deviceId} type '${model}'`);
|
|
230
238
|
break;
|
|
231
239
|
}
|
|
@@ -234,8 +242,8 @@ class StatesController extends EventEmitter {
|
|
|
234
242
|
if (!devStates) {
|
|
235
243
|
return;
|
|
236
244
|
}
|
|
237
|
-
const commonStates = statesMapping.commonStates.find(
|
|
238
|
-
const stateDesc = (commonStates === undefined ? devStates.states.find(
|
|
245
|
+
const commonStates = statesMapping.commonStates.find(statedesc => stateKey === statedesc.id);
|
|
246
|
+
const stateDesc = (commonStates === undefined ? devStates.states.find(statedesc => stateKey === statedesc.id) : commonStates);
|
|
239
247
|
const stateModel = devStates.stateModel;
|
|
240
248
|
if (!stateDesc) {
|
|
241
249
|
this.error(`No state available for '${model}' with key '${stateKey}'`);
|
|
@@ -243,11 +251,12 @@ class StatesController extends EventEmitter {
|
|
|
243
251
|
}
|
|
244
252
|
|
|
245
253
|
const value = state.val;
|
|
246
|
-
if (value === undefined || value === '')
|
|
254
|
+
if (value === undefined || value === '') {
|
|
247
255
|
return;
|
|
256
|
+
}
|
|
248
257
|
let stateList = [{stateDesc: stateDesc, value: value, index: 0, timeout: 0}];
|
|
249
258
|
if (stateModel && stateModel.linkedStates) {
|
|
250
|
-
stateModel.linkedStates.forEach(
|
|
259
|
+
stateModel.linkedStates.forEach(linkedFunct => {
|
|
251
260
|
try {
|
|
252
261
|
if (typeof linkedFunct === 'function') {
|
|
253
262
|
const res = linkedFunct(stateDesc, value, options, this.adapter.config.disableQueue);
|
|
@@ -256,7 +265,7 @@ class StatesController extends EventEmitter {
|
|
|
256
265
|
}
|
|
257
266
|
}
|
|
258
267
|
else {
|
|
259
|
-
this.warn(
|
|
268
|
+
this.warn(`publish from State - LinkedState is not a function ${JSON.stringify(linkedFunct)}`);
|
|
260
269
|
}
|
|
261
270
|
} catch (e) {
|
|
262
271
|
this.sendError(e);
|
|
@@ -265,17 +274,14 @@ class StatesController extends EventEmitter {
|
|
|
265
274
|
|
|
266
275
|
});
|
|
267
276
|
// sort by index
|
|
268
|
-
stateList.sort((a, b) =>
|
|
269
|
-
return a.index - b.index;
|
|
270
|
-
});
|
|
277
|
+
stateList.sort((a, b) => a.index - b.index);
|
|
271
278
|
}
|
|
272
279
|
|
|
273
|
-
// holds the states for
|
|
280
|
+
// holds the states for read after write requests
|
|
274
281
|
let readAfterWriteStates = [];
|
|
275
282
|
if (stateModel && stateModel.readAfterWriteStates) {
|
|
276
|
-
stateModel.readAfterWriteStates.forEach((readAfterWriteStateDesc) =>
|
|
277
|
-
readAfterWriteStates = readAfterWriteStates.concat(readAfterWriteStateDesc.id);
|
|
278
|
-
});
|
|
283
|
+
stateModel.readAfterWriteStates.forEach((readAfterWriteStateDesc) =>
|
|
284
|
+
readAfterWriteStates = readAfterWriteStates.concat(readAfterWriteStateDesc.id));
|
|
279
285
|
}
|
|
280
286
|
|
|
281
287
|
this.emit('changed', deviceId, model, stateModel, stateList, options);
|
|
@@ -287,42 +293,42 @@ class StatesController extends EventEmitter {
|
|
|
287
293
|
}
|
|
288
294
|
|
|
289
295
|
setDeviceActivated(id, active) {
|
|
290
|
-
|
|
296
|
+
this.adapter.extendObject(id, {common: {deactivated: active }})
|
|
291
297
|
}
|
|
292
298
|
|
|
293
299
|
storeDeviceName(id, name) {
|
|
294
|
-
|
|
295
|
-
|
|
300
|
+
savedDeviceNames[id.replace(`${this.adapter.namespace}.`, '')] = name;
|
|
301
|
+
this.retainDeviceNames();
|
|
296
302
|
}
|
|
297
303
|
|
|
298
304
|
verifyDeviceName(id, name) {
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
305
|
+
const savedId = id.replace(`${this.adapter.namespace}.`, '');
|
|
306
|
+
if (!savedDeviceNames.hasOwnProperty(savedId)) {
|
|
307
|
+
savedDeviceNames[savedId] = name;
|
|
308
|
+
this.retainDeviceNames();
|
|
309
|
+
}
|
|
310
|
+
return savedDeviceNames[savedId];
|
|
305
311
|
}
|
|
306
312
|
|
|
307
313
|
deleteDeviceStates(devId, callback) {
|
|
308
314
|
this.adapter.getStatesOf(devId, (err, states) => {
|
|
309
315
|
if (!err && states) {
|
|
310
|
-
states.forEach(
|
|
311
|
-
this.adapter.deleteState(devId, null, state._id);
|
|
312
|
-
});
|
|
316
|
+
states.forEach(state =>
|
|
317
|
+
this.adapter.deleteState(devId, null, state._id));
|
|
313
318
|
}
|
|
314
|
-
this.adapter.deleteDevice(devId, () =>
|
|
315
|
-
callback && callback();
|
|
316
|
-
});
|
|
319
|
+
this.adapter.deleteDevice(devId, () =>
|
|
320
|
+
callback && callback());
|
|
317
321
|
});
|
|
318
322
|
}
|
|
319
323
|
|
|
320
324
|
async deleteDeviceStatesAsync(devId) {
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|
|
325
|
+
const states = await this.adapter.getStatesOf(devId);
|
|
326
|
+
if (states) {
|
|
327
|
+
for (const state of states) {
|
|
328
|
+
await this.adapter.deleteState(devId, null, state._id);
|
|
329
|
+
}
|
|
330
|
+
}
|
|
331
|
+
await this.adapter.deleteDevice(devId);
|
|
326
332
|
}
|
|
327
333
|
|
|
328
334
|
// eslint-disable-next-line no-unused-vars
|
|
@@ -342,16 +348,16 @@ class StatesController extends EventEmitter {
|
|
|
342
348
|
} else {
|
|
343
349
|
statename = arr[1];
|
|
344
350
|
}
|
|
345
|
-
if (commonStates.find(
|
|
346
|
-
devStates.states.find(
|
|
351
|
+
if (commonStates.find(statedesc => statename === statedesc.id) === undefined &&
|
|
352
|
+
devStates.states.find(statedesc => statename === statedesc.id) === undefined
|
|
353
|
+
) {
|
|
347
354
|
if (state.common.hasOwnProperty('custom') && !force) {
|
|
348
355
|
this.info(`keeping disconnected state ${JSON.stringify(statename)} of ${devId} `);
|
|
349
356
|
} else {
|
|
350
357
|
this.info(`deleting disconnected state ${JSON.stringify(statename)} of ${devId} `);
|
|
351
358
|
this.adapter.deleteState(devId, null, state._id);
|
|
352
359
|
}
|
|
353
|
-
}
|
|
354
|
-
else {
|
|
360
|
+
} else {
|
|
355
361
|
this.debug(`keeping connecte state ${JSON.stringify(statename)} of ${devId} `);
|
|
356
362
|
}
|
|
357
363
|
});
|
|
@@ -408,10 +414,11 @@ class StatesController extends EventEmitter {
|
|
|
408
414
|
let hasChanges = false;
|
|
409
415
|
if (stobj) {
|
|
410
416
|
// update state - not change name and role (user can it changed)
|
|
411
|
-
if (stobj.common.name)
|
|
417
|
+
if (stobj.common.name) {
|
|
412
418
|
delete new_common.name;
|
|
413
|
-
else
|
|
414
|
-
new_common.name = new_name
|
|
419
|
+
} else {
|
|
420
|
+
new_common.name = `${new_name} ${new_common.name}`;
|
|
421
|
+
}
|
|
415
422
|
delete new_common.role;
|
|
416
423
|
|
|
417
424
|
// check whether any common property is different
|
|
@@ -432,9 +439,8 @@ class StatesController extends EventEmitter {
|
|
|
432
439
|
|
|
433
440
|
// only change object when any common property has changed
|
|
434
441
|
if (hasChanges) {
|
|
435
|
-
this.adapter.extendObject(id, {type: 'state', common: new_common, native: {} }, () =>
|
|
436
|
-
value !== undefined && this.setState_typed(id, value, true,
|
|
437
|
-
});
|
|
442
|
+
this.adapter.extendObject(id, {type: 'state', common: new_common, native: {} }, () =>
|
|
443
|
+
value !== undefined && this.setState_typed(id, value, true, stobj ? stobj.common.type : new_common.type));
|
|
438
444
|
} else if (value !== undefined) {
|
|
439
445
|
this.setState_typed(id, value, true, stobj.common.type);
|
|
440
446
|
}
|
|
@@ -453,19 +459,19 @@ class StatesController extends EventEmitter {
|
|
|
453
459
|
// never set a null or undefined value
|
|
454
460
|
if (value === null || value === undefined) return;
|
|
455
461
|
if (!type) {
|
|
456
|
-
this.debug(
|
|
462
|
+
this.debug('SetState_typed called without type');
|
|
457
463
|
// identify datatype, recursively call this function with set datatype
|
|
458
464
|
this.adapter.getObject(id, (err, obj) => {
|
|
459
|
-
if (obj && obj.common)
|
|
460
|
-
|
|
461
|
-
else {
|
|
465
|
+
if (obj && obj.common) {
|
|
466
|
+
this.setState_typed(id, value, ack, obj.common.type, callback);
|
|
467
|
+
} else {
|
|
462
468
|
this.setState_typed(id, value, ack, 'noobj', callback);
|
|
463
469
|
}
|
|
464
470
|
});
|
|
465
471
|
return;
|
|
466
472
|
}
|
|
467
|
-
if (typeof value
|
|
468
|
-
this.debug(
|
|
473
|
+
if (typeof value !== type) {
|
|
474
|
+
this.debug(`SetState_typed : converting ${JSON.stringify(value)} for ${id} from ${typeof value} to ${type}`);
|
|
469
475
|
switch (type) {
|
|
470
476
|
case 'number':
|
|
471
477
|
value = parseFloat(value);
|
|
@@ -473,13 +479,14 @@ class StatesController extends EventEmitter {
|
|
|
473
479
|
break;
|
|
474
480
|
case 'string':
|
|
475
481
|
case 'text': value = JSON.stringify(value); break;
|
|
476
|
-
case 'boolean':
|
|
482
|
+
case 'boolean': {
|
|
477
483
|
if (typeof value == 'number') {
|
|
478
|
-
value =
|
|
484
|
+
value = value !== 0;
|
|
479
485
|
break;
|
|
480
486
|
}
|
|
481
487
|
const sval = JSON.stringify(value).toLowerCase().trim();
|
|
482
|
-
value =
|
|
488
|
+
value = sval === 'true' || sval === 'yes' || sval === 'on';
|
|
489
|
+
}
|
|
483
490
|
break;
|
|
484
491
|
}
|
|
485
492
|
}
|
|
@@ -491,27 +498,27 @@ class StatesController extends EventEmitter {
|
|
|
491
498
|
const id = '' + dev_id;
|
|
492
499
|
const modelDesc = statesMapping.findModel(model);
|
|
493
500
|
let icon = (modelDesc && modelDesc.icon) ? modelDesc.icon : 'img/unknown.png';
|
|
494
|
-
|
|
501
|
+
|
|
495
502
|
// download icon if it external and not undef
|
|
496
|
-
if (model === undefined) {
|
|
503
|
+
if (model === undefined) {
|
|
497
504
|
this.warn(`download icon ${__dev_name} for undefined Device not available. Check your devices.`);
|
|
498
505
|
} else {
|
|
499
|
-
const model_modif = model.replace(/\//g, '-');
|
|
500
|
-
const pathToIcon = this.adapter.adapterDir
|
|
506
|
+
const model_modif = model.replace(/\//g, '-');
|
|
507
|
+
const pathToIcon = `${this.adapter.adapterDir}/admin/img/${model_modif}.png`;
|
|
501
508
|
|
|
502
|
-
if (icon.startsWith('http')) {
|
|
503
|
-
try {
|
|
509
|
+
if (icon.startsWith('http')) {
|
|
510
|
+
try {
|
|
504
511
|
if (!fs.existsSync(pathToIcon)) {
|
|
505
512
|
this.warn(`download icon from ${icon} saved into ${pathToIcon}`);
|
|
506
|
-
this.downloadIcon(icon, pathToIcon);
|
|
513
|
+
this.downloadIcon(icon, pathToIcon);
|
|
507
514
|
}
|
|
508
|
-
icon =
|
|
515
|
+
icon = `img/${model_modif}.png`;
|
|
509
516
|
} catch (e) {
|
|
510
517
|
this.debug(`ERROR : icon not found from ${icon} saved into ${pathToIcon}`);
|
|
511
518
|
}
|
|
512
519
|
}
|
|
513
520
|
}
|
|
514
|
-
|
|
521
|
+
|
|
515
522
|
this.adapter.setObjectNotExists(id, {
|
|
516
523
|
type: 'device',
|
|
517
524
|
// actually this is an error, so device.common has no attribute type. It must be in native part
|
|
@@ -524,27 +531,22 @@ class StatesController extends EventEmitter {
|
|
|
524
531
|
}
|
|
525
532
|
|
|
526
533
|
async downloadIcon(url, image_path) {
|
|
527
|
-
|
|
528
|
-
|
|
529
|
-
|
|
530
|
-
|
|
531
|
-
|
|
532
|
-
|
|
533
|
-
|
|
534
|
-
|
|
535
|
-
|
|
536
|
-
|
|
537
|
-
|
|
538
|
-
|
|
539
|
-
|
|
540
|
-
|
|
541
|
-
resolve();
|
|
542
|
-
});
|
|
543
|
-
});
|
|
544
|
-
});
|
|
545
|
-
}
|
|
534
|
+
if (!fs.existsSync(image_path)) {
|
|
535
|
+
return new Promise((resolve, reject) => {
|
|
536
|
+
request.head(url, (err, res, body) => {
|
|
537
|
+
if (err) {
|
|
538
|
+
return reject(err);
|
|
539
|
+
}
|
|
540
|
+
const stream = request(url);
|
|
541
|
+
stream.pipe(
|
|
542
|
+
fs.createWriteStream(image_path)
|
|
543
|
+
.on('error', err => reject(err)))
|
|
544
|
+
.on('close', () => resolve());
|
|
545
|
+
});
|
|
546
|
+
});
|
|
547
|
+
}
|
|
546
548
|
}
|
|
547
|
-
|
|
549
|
+
|
|
548
550
|
async syncDevStates(dev, model) {
|
|
549
551
|
const devId = dev.ieeeAddr.substr(2);
|
|
550
552
|
// devId - iobroker device id
|
|
@@ -555,20 +557,25 @@ class StatesController extends EventEmitter {
|
|
|
555
557
|
const states = statesMapping.commonStates.concat(devStates.states);
|
|
556
558
|
|
|
557
559
|
for (const stateInd in states) {
|
|
558
|
-
if (!states.hasOwnProperty(stateInd))
|
|
560
|
+
if (!states.hasOwnProperty(stateInd)) {
|
|
561
|
+
continue;
|
|
562
|
+
}
|
|
559
563
|
|
|
560
564
|
const statedesc = states[stateInd];
|
|
561
|
-
if (statedesc === undefined)
|
|
562
|
-
{
|
|
565
|
+
if (statedesc === undefined) {
|
|
563
566
|
this.error(`syncDevStates: Illegal state in ${JSON.stringify(dev)} - ${JSON.stringify(stateInd)}`);
|
|
564
567
|
return;
|
|
565
568
|
}
|
|
566
569
|
// Filter out non routers or devices that are battery driven for the availability flag
|
|
567
|
-
if (statedesc.id === 'available')
|
|
568
|
-
if (!(dev.type === 'Router') || dev.powerSource === 'Battery')
|
|
570
|
+
if (statedesc.id === 'available') {
|
|
571
|
+
if (!(dev.type === 'Router') || dev.powerSource === 'Battery') {
|
|
569
572
|
continue;
|
|
573
|
+
}
|
|
574
|
+
}
|
|
570
575
|
// lazy states
|
|
571
|
-
if (statedesc.lazy)
|
|
576
|
+
if (statedesc.lazy) {
|
|
577
|
+
continue;
|
|
578
|
+
}
|
|
572
579
|
|
|
573
580
|
const common = {
|
|
574
581
|
name: statedesc.name,
|
|
@@ -586,19 +593,16 @@ class StatesController extends EventEmitter {
|
|
|
586
593
|
}
|
|
587
594
|
}
|
|
588
595
|
|
|
589
|
-
|
|
590
596
|
async getExcludeExposes(allExcludesObj) {
|
|
591
597
|
statesMapping.fillStatesWithExposes(allExcludesObj);
|
|
592
598
|
}
|
|
593
599
|
|
|
594
|
-
|
|
595
600
|
async publishToState(devId, model, payload) {
|
|
596
|
-
const devStates = await this.getDevStates(
|
|
601
|
+
const devStates = await this.getDevStates(`0x${devId}`, model);
|
|
597
602
|
let has_debug=false;
|
|
598
603
|
if (this.debugDevices === undefined) this.getDebugDevices();
|
|
599
604
|
for (const addressPart of this.debugDevices) {
|
|
600
|
-
if (typeof(devId) == 'string' && devId.
|
|
601
|
-
{
|
|
605
|
+
if (typeof(devId) == 'string' && devId.includes(addressPart)) {
|
|
602
606
|
if (payload.hasOwnProperty('msg_from_zigbee')) break;
|
|
603
607
|
this.warn(`ELEVATED publishToState: message received '${JSON.stringify(payload)}' from device ${devId} type '${model}'`);
|
|
604
608
|
has_debug = true;
|
|
@@ -611,8 +615,8 @@ class StatesController extends EventEmitter {
|
|
|
611
615
|
// find states for payload
|
|
612
616
|
if (devStates.states !== undefined) {
|
|
613
617
|
const states = statesMapping.commonStates.concat(
|
|
614
|
-
devStates.states.filter(
|
|
615
|
-
|
|
618
|
+
devStates.states.filter(statedesc => payload.hasOwnProperty(statedesc.prop || statedesc.id)));
|
|
619
|
+
|
|
616
620
|
for (const stateInd in states) {
|
|
617
621
|
const statedesc = states[stateInd];
|
|
618
622
|
let value;
|
|
@@ -622,10 +626,12 @@ class StatesController extends EventEmitter {
|
|
|
622
626
|
value = payload[statedesc.prop || statedesc.id];
|
|
623
627
|
}
|
|
624
628
|
// checking value
|
|
625
|
-
if (value === undefined || value === null)
|
|
629
|
+
if (value === undefined || value === null) {
|
|
630
|
+
continue;
|
|
631
|
+
}
|
|
626
632
|
let stateID = statedesc.id;
|
|
627
633
|
|
|
628
|
-
if (has_debug && statedesc.id
|
|
634
|
+
if (has_debug && statedesc.id !== 'msg_from_zigbee') {
|
|
629
635
|
this.warn(`ELEVATED publishToState: value generated '${JSON.stringify(value)}' from device ${devId} for '${statedesc.name}'`);
|
|
630
636
|
}
|
|
631
637
|
|
|
@@ -640,24 +646,24 @@ class StatesController extends EventEmitter {
|
|
|
640
646
|
min: statedesc.min,
|
|
641
647
|
max: statedesc.max,
|
|
642
648
|
};
|
|
643
|
-
if (
|
|
644
|
-
stateID = stateID
|
|
645
|
-
if (value.hasOwnProperty('unit'))
|
|
649
|
+
if (typeof value === 'object' && value.hasOwnProperty('stateid')) {
|
|
650
|
+
stateID = `${stateID}.${value.stateid}`;
|
|
651
|
+
if (value.hasOwnProperty('unit')) {
|
|
652
|
+
common.unit = value.unit;
|
|
653
|
+
}
|
|
646
654
|
common.name = (value.name? value.name:value.stateid);
|
|
647
|
-
common.role = (value.role ?
|
|
655
|
+
common.role = (value.role ? `value.${value.role}` : 'number');
|
|
648
656
|
value = value.value;
|
|
649
657
|
|
|
650
658
|
}
|
|
651
|
-
// if
|
|
659
|
+
// if needs to return value to back after timeout
|
|
652
660
|
if (statedesc.isEvent) {
|
|
653
661
|
this.updateStateWithTimeout(devId, statedesc.id, value, common, 300, !value);
|
|
654
662
|
} else {
|
|
655
663
|
if (statedesc.prepublish) {
|
|
656
|
-
this.collectOptions(devId, model,
|
|
657
|
-
statedesc.prepublish(devId, value,
|
|
658
|
-
this.updateState(devId, stateID, newvalue, common);
|
|
659
|
-
}, options);
|
|
660
|
-
});
|
|
664
|
+
this.collectOptions(devId, model, options =>
|
|
665
|
+
statedesc.prepublish(devId, value, newvalue =>
|
|
666
|
+
this.updateState(devId, stateID, newvalue, common), options));
|
|
661
667
|
} else {
|
|
662
668
|
this.updateState(devId, stateID, value, common);
|
|
663
669
|
}
|