iobroker.zigbee 1.8.1 → 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 +31 -8
- 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 +18 -25
- package/lib/backup.js +2 -2
- package/lib/binding.js +37 -32
- package/lib/colors.js +158 -163
- package/lib/commands.js +90 -99
- package/lib/developer.js +12 -9
- package/lib/devices.js +179 -169
- package/lib/exclude.js +36 -30
- package/lib/exposes.js +139 -163
- package/lib/groups.js +83 -81
- package/lib/json.js +6 -5
- package/lib/networkmap.js +3 -2
- package/lib/ota.js +18 -34
- package/lib/rgb.js +72 -114
- package/lib/seriallist.js +20 -25
- package/lib/states.js +526 -511
- package/lib/statescontroller.js +183 -206
- package/lib/utils.js +23 -24
- package/lib/zbBaseExtension.js +4 -4
- package/lib/zbDelayedAction.js +13 -5
- package/lib/zbDeviceAvailability.js +65 -69
- package/lib/zbDeviceConfigure.js +21 -9
- package/lib/zbDeviceEvent.js +4 -3
- package/lib/zigbeecontroller.js +103 -109
- package/main.js +147 -163
- 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
|
|
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
|
-
onStateChange(id, state)
|
|
104
|
-
if (!this.adapter.zbController || !this.adapter.zbController.connected())
|
|
105
|
-
|
|
106
|
-
}
|
|
107
|
-
if (this.debugDevices === undefined) {
|
|
108
|
-
this.getDebugDevices();
|
|
109
|
-
}
|
|
104
|
+
onStateChange(id, state){
|
|
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
|
|
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
|
|
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
|
}
|
|
@@ -159,12 +153,12 @@ class StatesController extends EventEmitter {
|
|
|
159
153
|
async collectOptions(devId, model, callback) {
|
|
160
154
|
const result = {};
|
|
161
155
|
// find model states for options and get it values
|
|
162
|
-
const devStates = await this.getDevStates('0x'
|
|
156
|
+
const devStates = await this.getDevStates('0x'+devId, model);
|
|
163
157
|
if (!devStates) {
|
|
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,29 +178,29 @@ 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;
|
|
202
194
|
} else {
|
|
203
195
|
stateModel = statesMapping.findModel(model);
|
|
204
196
|
if (!stateModel) {
|
|
205
|
-
if (knownUndefinedDevices[deviceId])
|
|
197
|
+
if (knownUndefinedDevices[deviceId])
|
|
198
|
+
{
|
|
206
199
|
knownUndefinedDevices[deviceId]++;
|
|
207
|
-
}
|
|
200
|
+
}
|
|
201
|
+
else {
|
|
208
202
|
knownUndefinedDevices[deviceId] = 1;
|
|
209
|
-
this.error(
|
|
203
|
+
this.error('Device ' + deviceId + ' "' + model + '" not described in statesMapping.');
|
|
210
204
|
}
|
|
211
205
|
this.adapter.setStateAsync(`info.undefinedDevices`, JSON.stringify(knownUndefinedDevices), true);
|
|
212
206
|
states = statesMapping.commonStates;
|
|
@@ -215,12 +209,11 @@ class StatesController extends EventEmitter {
|
|
|
215
209
|
}
|
|
216
210
|
if (typeof states === 'function' && !states.prototype) {
|
|
217
211
|
const entity = await this.adapter.zbController.resolveEntity(deviceId);
|
|
218
|
-
if (entity)
|
|
212
|
+
if (entity)
|
|
219
213
|
states = states(entity);
|
|
220
|
-
}
|
|
221
214
|
}
|
|
222
215
|
}
|
|
223
|
-
return {states, stateModel};
|
|
216
|
+
return {states: states, stateModel: stateModel};
|
|
224
217
|
} catch (error) {
|
|
225
218
|
this.sendError(error);
|
|
226
219
|
this.error(`Error getDevStates for ${deviceId}. Error: ${error.stack}`);
|
|
@@ -231,7 +224,8 @@ class StatesController extends EventEmitter {
|
|
|
231
224
|
if (this.debugDevices === undefined) this.getDebugDevices();
|
|
232
225
|
this.debug(`Change state '${stateKey}' at device ${deviceId} type '${model}'`);
|
|
233
226
|
for (const addressPart of this.debugDevices) {
|
|
234
|
-
if (typeof
|
|
227
|
+
if (typeof(deviceId) == 'string' && deviceId.indexOf(addressPart) > -1)
|
|
228
|
+
{
|
|
235
229
|
this.warn(`ELEVATED Change state '${stateKey}' at device ${deviceId} type '${model}'`);
|
|
236
230
|
break;
|
|
237
231
|
}
|
|
@@ -240,8 +234,8 @@ class StatesController extends EventEmitter {
|
|
|
240
234
|
if (!devStates) {
|
|
241
235
|
return;
|
|
242
236
|
}
|
|
243
|
-
const commonStates = statesMapping.commonStates.find(statedesc => stateKey === statedesc.id);
|
|
244
|
-
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);
|
|
245
239
|
const stateModel = devStates.stateModel;
|
|
246
240
|
if (!stateDesc) {
|
|
247
241
|
this.error(`No state available for '${model}' with key '${stateKey}'`);
|
|
@@ -249,20 +243,20 @@ class StatesController extends EventEmitter {
|
|
|
249
243
|
}
|
|
250
244
|
|
|
251
245
|
const value = state.val;
|
|
252
|
-
if (value === undefined || value === '')
|
|
246
|
+
if (value === undefined || value === '')
|
|
253
247
|
return;
|
|
254
|
-
}
|
|
255
248
|
let stateList = [{stateDesc: stateDesc, value: value, index: 0, timeout: 0}];
|
|
256
249
|
if (stateModel && stateModel.linkedStates) {
|
|
257
|
-
stateModel.linkedStates.forEach(linkedFunct => {
|
|
250
|
+
stateModel.linkedStates.forEach((linkedFunct) => {
|
|
258
251
|
try {
|
|
259
252
|
if (typeof linkedFunct === 'function') {
|
|
260
253
|
const res = linkedFunct(stateDesc, value, options, this.adapter.config.disableQueue);
|
|
261
254
|
if (res) {
|
|
262
255
|
stateList = stateList.concat(res);
|
|
263
256
|
}
|
|
264
|
-
}
|
|
265
|
-
|
|
257
|
+
}
|
|
258
|
+
else {
|
|
259
|
+
this.warn('publish from State - LinkedState is not a function ' + JSON.stringify(linkedFunct));
|
|
266
260
|
}
|
|
267
261
|
} catch (e) {
|
|
268
262
|
this.sendError(e);
|
|
@@ -271,14 +265,17 @@ class StatesController extends EventEmitter {
|
|
|
271
265
|
|
|
272
266
|
});
|
|
273
267
|
// sort by index
|
|
274
|
-
stateList.sort((a, b) =>
|
|
268
|
+
stateList.sort((a, b) => {
|
|
269
|
+
return a.index - b.index;
|
|
270
|
+
});
|
|
275
271
|
}
|
|
276
272
|
|
|
277
|
-
// holds the states for read after write requests
|
|
273
|
+
// holds the states for for read after write requests
|
|
278
274
|
let readAfterWriteStates = [];
|
|
279
275
|
if (stateModel && stateModel.readAfterWriteStates) {
|
|
280
|
-
stateModel.readAfterWriteStates.forEach((readAfterWriteStateDesc) =>
|
|
281
|
-
readAfterWriteStates = readAfterWriteStates.concat(readAfterWriteStateDesc.id)
|
|
276
|
+
stateModel.readAfterWriteStates.forEach((readAfterWriteStateDesc) => {
|
|
277
|
+
readAfterWriteStates = readAfterWriteStates.concat(readAfterWriteStateDesc.id);
|
|
278
|
+
});
|
|
282
279
|
}
|
|
283
280
|
|
|
284
281
|
this.emit('changed', deviceId, model, stateModel, stateList, options);
|
|
@@ -290,42 +287,42 @@ class StatesController extends EventEmitter {
|
|
|
290
287
|
}
|
|
291
288
|
|
|
292
289
|
setDeviceActivated(id, active) {
|
|
293
|
-
|
|
290
|
+
this.adapter.extendObject(id, {common: {deactivated: active }})
|
|
294
291
|
}
|
|
295
292
|
|
|
296
293
|
storeDeviceName(id, name) {
|
|
297
|
-
|
|
298
|
-
|
|
294
|
+
savedDeviceNames[id.replace(`${this.adapter.namespace}.`, '')] = name;
|
|
295
|
+
this.retainDeviceNames();
|
|
299
296
|
}
|
|
300
297
|
|
|
301
298
|
verifyDeviceName(id, name) {
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
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];
|
|
308
305
|
}
|
|
309
306
|
|
|
310
307
|
deleteDeviceStates(devId, callback) {
|
|
311
308
|
this.adapter.getStatesOf(devId, (err, states) => {
|
|
312
309
|
if (!err && states) {
|
|
313
|
-
states.forEach(state =>
|
|
314
|
-
this.adapter.deleteState(devId, null, state._id)
|
|
310
|
+
states.forEach((state) => {
|
|
311
|
+
this.adapter.deleteState(devId, null, state._id);
|
|
312
|
+
});
|
|
315
313
|
}
|
|
316
|
-
this.adapter.deleteDevice(devId, () =>
|
|
317
|
-
callback && callback()
|
|
314
|
+
this.adapter.deleteDevice(devId, () => {
|
|
315
|
+
callback && callback();
|
|
316
|
+
});
|
|
318
317
|
});
|
|
319
318
|
}
|
|
320
319
|
|
|
321
320
|
async deleteDeviceStatesAsync(devId) {
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
}
|
|
328
|
-
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);
|
|
329
326
|
}
|
|
330
327
|
|
|
331
328
|
// eslint-disable-next-line no-unused-vars
|
|
@@ -341,22 +338,20 @@ class StatesController extends EventEmitter {
|
|
|
341
338
|
if (arr[1] === undefined) {
|
|
342
339
|
this.warn(`unable to extract id from state ${statename}`);
|
|
343
340
|
const idx = statename.lastIndexOf('.');
|
|
344
|
-
if (idx > -1)
|
|
345
|
-
statename = statename.slice(idx + 1);
|
|
346
|
-
}
|
|
341
|
+
if (idx > -1) statename = statename.slice(idx+1);
|
|
347
342
|
} else {
|
|
348
343
|
statename = arr[1];
|
|
349
344
|
}
|
|
350
|
-
if (commonStates.find(statedesc => statename === statedesc.id) === undefined &&
|
|
351
|
-
devStates.states.find(statedesc => statename === statedesc.id) === undefined
|
|
352
|
-
) {
|
|
345
|
+
if (commonStates.find((statedesc) => statename === statedesc.id) === undefined &&
|
|
346
|
+
devStates.states.find((statedesc) => statename === statedesc.id) === undefined) {
|
|
353
347
|
if (state.common.hasOwnProperty('custom') && !force) {
|
|
354
348
|
this.info(`keeping disconnected state ${JSON.stringify(statename)} of ${devId} `);
|
|
355
349
|
} else {
|
|
356
350
|
this.info(`deleting disconnected state ${JSON.stringify(statename)} of ${devId} `);
|
|
357
351
|
this.adapter.deleteState(devId, null, state._id);
|
|
358
352
|
}
|
|
359
|
-
}
|
|
353
|
+
}
|
|
354
|
+
else {
|
|
360
355
|
this.debug(`keeping connecte state ${JSON.stringify(statename)} of ${devId} `);
|
|
361
356
|
}
|
|
362
357
|
});
|
|
@@ -413,11 +408,10 @@ class StatesController extends EventEmitter {
|
|
|
413
408
|
let hasChanges = false;
|
|
414
409
|
if (stobj) {
|
|
415
410
|
// update state - not change name and role (user can it changed)
|
|
416
|
-
if (stobj.common.name)
|
|
411
|
+
if (stobj.common.name)
|
|
417
412
|
delete new_common.name;
|
|
418
|
-
|
|
419
|
-
new_common.name =
|
|
420
|
-
}
|
|
413
|
+
else
|
|
414
|
+
new_common.name = new_name + ' ' + new_common.name;
|
|
421
415
|
delete new_common.role;
|
|
422
416
|
|
|
423
417
|
// check whether any common property is different
|
|
@@ -438,58 +432,54 @@ class StatesController extends EventEmitter {
|
|
|
438
432
|
|
|
439
433
|
// only change object when any common property has changed
|
|
440
434
|
if (hasChanges) {
|
|
441
|
-
this.adapter.extendObject(id, {type: 'state', common: new_common, native: {}}, () =>
|
|
442
|
-
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
|
+
});
|
|
443
438
|
} else if (value !== undefined) {
|
|
444
439
|
this.setState_typed(id, value, true, stobj.common.type);
|
|
445
440
|
}
|
|
446
441
|
|
|
447
442
|
});
|
|
448
|
-
} else {
|
|
449
|
-
this.debug(`UpdateState: Device is deactivated ${devId} ${JSON.stringify(obj)}`);
|
|
450
443
|
}
|
|
444
|
+
else this.debug(`UpdateState: Device is deactivated ${devId} ${JSON.stringify(obj)}`);
|
|
451
445
|
} else {
|
|
452
446
|
this.debug(`UpdateState: missing device ${devId} ${JSON.stringify(obj)}`);
|
|
453
447
|
}
|
|
454
448
|
});
|
|
455
449
|
}
|
|
456
450
|
|
|
457
|
-
setState_typed(id, value, ack, type, callback)
|
|
451
|
+
setState_typed(id, value, ack, type, callback)
|
|
452
|
+
{
|
|
458
453
|
// never set a null or undefined value
|
|
459
454
|
if (value === null || value === undefined) return;
|
|
460
455
|
if (!type) {
|
|
461
|
-
this.debug(
|
|
456
|
+
this.debug("SetState_typed called without type");
|
|
462
457
|
// identify datatype, recursively call this function with set datatype
|
|
463
458
|
this.adapter.getObject(id, (err, obj) => {
|
|
464
|
-
if (obj && obj.common)
|
|
465
|
-
|
|
466
|
-
|
|
459
|
+
if (obj && obj.common)
|
|
460
|
+
this.setState_typed(id, value, ack, obj.common.type, callback);
|
|
461
|
+
else {
|
|
467
462
|
this.setState_typed(id, value, ack, 'noobj', callback);
|
|
468
463
|
}
|
|
469
464
|
});
|
|
470
465
|
return;
|
|
471
466
|
}
|
|
472
|
-
if (typeof value
|
|
473
|
-
this.debug(
|
|
467
|
+
if (typeof value != type) {
|
|
468
|
+
this.debug("SetState_typed : converting " + JSON.stringify(value) + " for " + id + " from " + typeof value + " to " + type);
|
|
474
469
|
switch (type) {
|
|
475
470
|
case 'number':
|
|
476
471
|
value = parseFloat(value);
|
|
477
|
-
if (isNaN(value))
|
|
478
|
-
value = 0;
|
|
479
|
-
}
|
|
472
|
+
if (isNaN (value)) value = 0;
|
|
480
473
|
break;
|
|
481
474
|
case 'string':
|
|
482
|
-
case 'text':
|
|
483
|
-
|
|
484
|
-
break;
|
|
485
|
-
case 'boolean': {
|
|
475
|
+
case 'text': value = JSON.stringify(value); break;
|
|
476
|
+
case 'boolean':
|
|
486
477
|
if (typeof value == 'number') {
|
|
487
|
-
value = value
|
|
478
|
+
value = (value != 0);
|
|
488
479
|
break;
|
|
489
480
|
}
|
|
490
481
|
const sval = JSON.stringify(value).toLowerCase().trim();
|
|
491
|
-
value = sval
|
|
492
|
-
}
|
|
482
|
+
value = (sval == 'true' || sval == 'yes' || sval == 'on');
|
|
493
483
|
break;
|
|
494
484
|
}
|
|
495
485
|
}
|
|
@@ -501,68 +491,60 @@ class StatesController extends EventEmitter {
|
|
|
501
491
|
const id = '' + dev_id;
|
|
502
492
|
const modelDesc = statesMapping.findModel(model);
|
|
503
493
|
let icon = (modelDesc && modelDesc.icon) ? modelDesc.icon : 'img/unknown.png';
|
|
504
|
-
|
|
494
|
+
|
|
505
495
|
// download icon if it external and not undef
|
|
506
|
-
if (model === undefined) {
|
|
496
|
+
if (model === undefined) {
|
|
507
497
|
this.warn(`download icon ${__dev_name} for undefined Device not available. Check your devices.`);
|
|
508
498
|
} else {
|
|
509
|
-
const model_modif = model.replace(/\//g, '-');
|
|
510
|
-
const pathToIcon =
|
|
499
|
+
const model_modif = model.replace(/\//g, '-');
|
|
500
|
+
const pathToIcon = this.adapter.adapterDir + '/admin/img/' + model_modif + '.png';
|
|
511
501
|
|
|
512
|
-
if (icon.startsWith('http')) {
|
|
513
|
-
try {
|
|
502
|
+
if (icon.startsWith('http')) {
|
|
503
|
+
try {
|
|
514
504
|
if (!fs.existsSync(pathToIcon)) {
|
|
515
505
|
this.warn(`download icon from ${icon} saved into ${pathToIcon}`);
|
|
516
|
-
this.downloadIcon(icon, pathToIcon);
|
|
506
|
+
this.downloadIcon(icon, pathToIcon);
|
|
517
507
|
}
|
|
518
|
-
icon =
|
|
508
|
+
icon = 'img/' + model_modif + '.png';
|
|
519
509
|
} catch (e) {
|
|
520
510
|
this.debug(`ERROR : icon not found from ${icon} saved into ${pathToIcon}`);
|
|
521
511
|
}
|
|
522
512
|
}
|
|
523
513
|
}
|
|
524
|
-
|
|
514
|
+
|
|
525
515
|
this.adapter.setObjectNotExists(id, {
|
|
526
516
|
type: 'device',
|
|
527
517
|
// actually this is an error, so device.common has no attribute type. It must be in native part
|
|
528
|
-
common: {
|
|
529
|
-
name: __dev_name,
|
|
530
|
-
type: model,
|
|
531
|
-
icon,
|
|
532
|
-
color: null,
|
|
533
|
-
statusStates: {onlineId: `${this.adapter.namespace}.${dev_id}.available`}
|
|
534
|
-
},
|
|
518
|
+
common: {name: __dev_name, type: model, icon: icon, color: null, statusStates: {onlineId: `${this.adapter.namespace}.${dev_id}.available`}},
|
|
535
519
|
native: {id: dev_id}
|
|
536
520
|
}, () => {
|
|
537
521
|
// update type and icon
|
|
538
|
-
this.adapter.extendObject(id, {
|
|
539
|
-
common: {
|
|
540
|
-
type: model,
|
|
541
|
-
icon,
|
|
542
|
-
color: null,
|
|
543
|
-
statusStates: {onlineId: `${this.adapter.namespace}.${dev_id}.available`}
|
|
544
|
-
}
|
|
545
|
-
}, callback);
|
|
522
|
+
this.adapter.extendObject(id, {common: {type: model, icon: icon, color: null, statusStates: {onlineId: `${this.adapter.namespace}.${dev_id}.available`}}}, callback);
|
|
546
523
|
});
|
|
547
524
|
}
|
|
548
525
|
|
|
549
526
|
async downloadIcon(url, image_path) {
|
|
550
|
-
|
|
551
|
-
|
|
552
|
-
|
|
553
|
-
|
|
554
|
-
|
|
555
|
-
|
|
556
|
-
|
|
557
|
-
|
|
558
|
-
|
|
559
|
-
|
|
560
|
-
|
|
561
|
-
|
|
562
|
-
|
|
563
|
-
|
|
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
|
+
}
|
|
564
546
|
}
|
|
565
|
-
|
|
547
|
+
|
|
566
548
|
async syncDevStates(dev, model) {
|
|
567
549
|
const devId = dev.ieeeAddr.substr(2);
|
|
568
550
|
// devId - iobroker device id
|
|
@@ -573,25 +555,20 @@ class StatesController extends EventEmitter {
|
|
|
573
555
|
const states = statesMapping.commonStates.concat(devStates.states);
|
|
574
556
|
|
|
575
557
|
for (const stateInd in states) {
|
|
576
|
-
if (!states.hasOwnProperty(stateInd))
|
|
577
|
-
continue;
|
|
578
|
-
}
|
|
558
|
+
if (!states.hasOwnProperty(stateInd)) continue;
|
|
579
559
|
|
|
580
560
|
const statedesc = states[stateInd];
|
|
581
|
-
if (statedesc === undefined)
|
|
561
|
+
if (statedesc === undefined)
|
|
562
|
+
{
|
|
582
563
|
this.error(`syncDevStates: Illegal state in ${JSON.stringify(dev)} - ${JSON.stringify(stateInd)}`);
|
|
583
564
|
return;
|
|
584
565
|
}
|
|
585
566
|
// Filter out non routers or devices that are battery driven for the availability flag
|
|
586
|
-
if (statedesc.id === 'available')
|
|
587
|
-
if (!(dev.type === 'Router') || dev.powerSource === 'Battery')
|
|
567
|
+
if (statedesc.id === 'available')
|
|
568
|
+
if (!(dev.type === 'Router') || dev.powerSource === 'Battery')
|
|
588
569
|
continue;
|
|
589
|
-
}
|
|
590
|
-
}
|
|
591
570
|
// lazy states
|
|
592
|
-
if (statedesc.lazy)
|
|
593
|
-
continue;
|
|
594
|
-
}
|
|
571
|
+
if (statedesc.lazy) continue;
|
|
595
572
|
|
|
596
573
|
const common = {
|
|
597
574
|
name: statedesc.name,
|
|
@@ -609,16 +586,19 @@ class StatesController extends EventEmitter {
|
|
|
609
586
|
}
|
|
610
587
|
}
|
|
611
588
|
|
|
589
|
+
|
|
612
590
|
async getExcludeExposes(allExcludesObj) {
|
|
613
591
|
statesMapping.fillStatesWithExposes(allExcludesObj);
|
|
614
592
|
}
|
|
615
593
|
|
|
594
|
+
|
|
616
595
|
async publishToState(devId, model, payload) {
|
|
617
|
-
const devStates = await this.getDevStates(
|
|
618
|
-
let has_debug
|
|
596
|
+
const devStates = await this.getDevStates('0x'+devId, model);
|
|
597
|
+
let has_debug=false;
|
|
619
598
|
if (this.debugDevices === undefined) this.getDebugDevices();
|
|
620
599
|
for (const addressPart of this.debugDevices) {
|
|
621
|
-
if (typeof
|
|
600
|
+
if (typeof(devId) == 'string' && devId.indexOf(addressPart) > -1)
|
|
601
|
+
{
|
|
622
602
|
if (payload.hasOwnProperty('msg_from_zigbee')) break;
|
|
623
603
|
this.warn(`ELEVATED publishToState: message received '${JSON.stringify(payload)}' from device ${devId} type '${model}'`);
|
|
624
604
|
has_debug = true;
|
|
@@ -631,8 +611,8 @@ class StatesController extends EventEmitter {
|
|
|
631
611
|
// find states for payload
|
|
632
612
|
if (devStates.states !== undefined) {
|
|
633
613
|
const states = statesMapping.commonStates.concat(
|
|
634
|
-
devStates.states.filter(statedesc => payload.hasOwnProperty(statedesc.prop || statedesc.id))
|
|
635
|
-
|
|
614
|
+
devStates.states.filter((statedesc) => payload.hasOwnProperty(statedesc.prop || statedesc.id))
|
|
615
|
+
);
|
|
636
616
|
for (const stateInd in states) {
|
|
637
617
|
const statedesc = states[stateInd];
|
|
638
618
|
let value;
|
|
@@ -642,12 +622,10 @@ class StatesController extends EventEmitter {
|
|
|
642
622
|
value = payload[statedesc.prop || statedesc.id];
|
|
643
623
|
}
|
|
644
624
|
// checking value
|
|
645
|
-
if (value === undefined || value === null)
|
|
646
|
-
continue;
|
|
647
|
-
}
|
|
625
|
+
if (value === undefined || value === null) continue;
|
|
648
626
|
let stateID = statedesc.id;
|
|
649
627
|
|
|
650
|
-
if (has_debug && statedesc.id
|
|
628
|
+
if (has_debug && statedesc.id != 'msg_from_zigbee') {
|
|
651
629
|
this.warn(`ELEVATED publishToState: value generated '${JSON.stringify(value)}' from device ${devId} for '${statedesc.name}'`);
|
|
652
630
|
}
|
|
653
631
|
|
|
@@ -662,25 +640,24 @@ class StatesController extends EventEmitter {
|
|
|
662
640
|
min: statedesc.min,
|
|
663
641
|
max: statedesc.max,
|
|
664
642
|
};
|
|
665
|
-
|
|
666
|
-
|
|
667
|
-
|
|
668
|
-
|
|
669
|
-
|
|
670
|
-
}
|
|
671
|
-
common.name = value.name ? value.name : value.stateid;
|
|
672
|
-
common.role = value.role ? `value.${value.role}` : 'number';
|
|
643
|
+
if ( (typeof(value)== 'object') && (value.hasOwnProperty('stateid'))) {
|
|
644
|
+
stateID = stateID + '.' + value.stateid;
|
|
645
|
+
if (value.hasOwnProperty('unit')) common.unit = value.unit;
|
|
646
|
+
common.name = (value.name? value.name:value.stateid);
|
|
647
|
+
common.role = (value.role ? 'value.'+value.role:'number');
|
|
673
648
|
value = value.value;
|
|
674
|
-
}
|
|
675
649
|
|
|
676
|
-
|
|
650
|
+
}
|
|
651
|
+
// if need return value to back after timeout
|
|
677
652
|
if (statedesc.isEvent) {
|
|
678
653
|
this.updateStateWithTimeout(devId, statedesc.id, value, common, 300, !value);
|
|
679
654
|
} else {
|
|
680
655
|
if (statedesc.prepublish) {
|
|
681
|
-
this.collectOptions(devId, model, options =>
|
|
682
|
-
statedesc.prepublish(devId, value, newvalue =>
|
|
683
|
-
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
|
+
});
|
|
684
661
|
} else {
|
|
685
662
|
this.updateState(devId, stateID, value, common);
|
|
686
663
|
}
|