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