iobroker.zigbee 2.0.0 → 2.0.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +27 -9
- package/admin/admin.js +312 -125
- package/admin/img/PTM 215Z.png +0 -0
- package/admin/img/group_0.png +0 -0
- package/admin/img/group_x.png +0 -0
- package/admin/img/philips_hue_lom001.png +0 -0
- package/admin/index_m.html +95 -45
- package/admin/tab_m.html +116 -48
- package/docs/de/img/Zigbee_config_de.png +0 -0
- package/docs/de/img/Zigbee_tab_de.png +0 -0
- package/docs/en/img/Zigbee_config_en.png +0 -0
- package/docs/en/img/Zigbee_tab_en.png +0 -0
- package/docs/tutorial/groups-1.png +0 -0
- package/docs/tutorial/groups-2.png +0 -0
- package/docs/tutorial/tab-dev-1.png +0 -0
- package/io-package.json +40 -39
- package/lib/binding.js +1 -1
- package/lib/colors.js +7 -0
- package/lib/commands.js +127 -17
- package/lib/developer.js +0 -0
- package/lib/devices.js +78 -74
- package/lib/exclude.js +30 -54
- package/lib/exposes.js +203 -246
- package/lib/groups.js +84 -29
- package/lib/localConfig.js +295 -0
- package/lib/ota.js +0 -0
- package/lib/statescontroller.js +410 -183
- package/lib/utils.js +1 -1
- package/lib/zbDeviceAvailability.js +15 -23
- package/lib/zbDeviceConfigure.js +0 -0
- package/lib/zbDeviceEvent.js +2 -13
- package/lib/zigbeecontroller.js +299 -207
- package/main.js +145 -56
- package/package.json +8 -7
package/lib/groups.js
CHANGED
|
@@ -1,25 +1,40 @@
|
|
|
1
1
|
'use strict';
|
|
2
2
|
|
|
3
|
+
const json = require('iobroker.zigbee/lib/json');
|
|
3
4
|
const statesMapping = require('./devices');
|
|
5
|
+
const { numberWithinRange } = require('zigbee-herdsman-converters/lib/utils');
|
|
6
|
+
const idRegExp = new RegExp(/group_(\d+)/);
|
|
7
|
+
|
|
8
|
+
|
|
4
9
|
|
|
5
10
|
class Groups {
|
|
6
11
|
constructor(adapter) {
|
|
7
12
|
this.adapter = adapter;
|
|
8
13
|
this.adapter.on('message', this.onMessage.bind(this));
|
|
14
|
+
this.log = this.adapter.log;
|
|
15
|
+
this.idRegex = new RegExp(/group_(\d+)/)
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
static extractGroupID(id) {
|
|
19
|
+
switch (typeof id) {
|
|
20
|
+
case 'number': return id;
|
|
21
|
+
case 'string': {
|
|
22
|
+
const regexResult = id.match(idRegExp);
|
|
23
|
+
if (regexResult) return Number(regexResult[1]);
|
|
24
|
+
break;
|
|
25
|
+
}
|
|
26
|
+
default: return -1;
|
|
27
|
+
}
|
|
9
28
|
}
|
|
10
29
|
|
|
30
|
+
static generateGroupID(gnum) {
|
|
31
|
+
return `group_${gnum}`;
|
|
32
|
+
};
|
|
33
|
+
|
|
11
34
|
start(zbController, stController) {
|
|
12
35
|
this.zbController = zbController;
|
|
13
36
|
this.stController = stController;
|
|
14
|
-
this.
|
|
15
|
-
.then(groupsState => {
|
|
16
|
-
const groups = (groupsState && groupsState.val) ? JSON.parse(groupsState.val) : {};
|
|
17
|
-
for (const gid in groups) {
|
|
18
|
-
stController.storeDeviceName(`group_${gid}`, groups[gid]);
|
|
19
|
-
}
|
|
20
|
-
// this.Adapter.deleteState('info.groups');
|
|
21
|
-
this.syncGroups();
|
|
22
|
-
});
|
|
37
|
+
this.syncGroups();
|
|
23
38
|
}
|
|
24
39
|
|
|
25
40
|
stop() {
|
|
@@ -66,10 +81,22 @@ class Groups {
|
|
|
66
81
|
}
|
|
67
82
|
}
|
|
68
83
|
|
|
84
|
+
|
|
85
|
+
buildGroupID(id, withInstance) {
|
|
86
|
+
const parts = [];
|
|
87
|
+
if (withInstance) parts.push(this.adapter.namespace);
|
|
88
|
+
if (Number(id) > 0)
|
|
89
|
+
parts.push(Groups.generateGroupID(id));
|
|
90
|
+
else {
|
|
91
|
+
if (Groups.extractGroupID(id) > 0) parts.push(id);
|
|
92
|
+
}
|
|
93
|
+
return parts.join('.');
|
|
94
|
+
}
|
|
95
|
+
|
|
69
96
|
async getGroupMembersFromController(id) {
|
|
70
97
|
const members = [];
|
|
71
98
|
try {
|
|
72
|
-
const group = await this.zbController.getGroupByID(id);
|
|
99
|
+
const group = await this.zbController.getGroupByID(Number(id));
|
|
73
100
|
if (group) {
|
|
74
101
|
const groupmembers = group.members;
|
|
75
102
|
|
|
@@ -85,29 +112,32 @@ class Groups {
|
|
|
85
112
|
}
|
|
86
113
|
|
|
87
114
|
} catch (error) {
|
|
88
|
-
if (error) this.error(`getGroupMembersFromController: error is ${(error
|
|
115
|
+
if (error) this.error(`getGroupMembersFromController: error is ${JSON.stringify(error)} ${JSON.stringify(new Error().stack)}`);
|
|
89
116
|
else this.error('unidentifed error in getGroupMembersFromController');
|
|
90
117
|
}
|
|
91
118
|
return members;
|
|
92
119
|
}
|
|
93
120
|
|
|
94
121
|
async getGroups(obj) {
|
|
122
|
+
this.debug('get groupes called with ' + JSON.stringify(obj));
|
|
95
123
|
const response = {groups: {}};
|
|
96
124
|
|
|
97
125
|
const isEnable = await this.adapter.getStateAsync('info.connection');
|
|
126
|
+
this.debug('get groupes called with ' + (obj ? JSON.stringify(obj) : 'no object') + ' ' + (isEnable.val ? 'connected' : 'disconnected'));
|
|
98
127
|
if (isEnable.val) {
|
|
99
128
|
try {
|
|
100
129
|
const herdsmanGroups = await this.zbController.getGroups();
|
|
101
130
|
const groups = {};
|
|
102
131
|
if (typeof herdsmanGroups === 'object') {
|
|
103
132
|
for (const group of herdsmanGroups) {
|
|
104
|
-
const gid = group.
|
|
133
|
+
const gid = group.id;
|
|
105
134
|
if (gid) {
|
|
106
|
-
|
|
135
|
+
const name = this.stController.verifyDeviceName(`group_${gid}`, `Group`, `Group ${gid}`);
|
|
136
|
+
groups[gid] = name;
|
|
107
137
|
}
|
|
108
138
|
}
|
|
109
139
|
}
|
|
110
|
-
this.debug(`getGroups result: ${JSON.stringify(groups)}`);
|
|
140
|
+
this.debug(`getGroups result: ${JSON.stringify(groups)} ( ${JSON.stringify(herdsmanGroups)})`);
|
|
111
141
|
response.groups = groups;
|
|
112
142
|
} catch (error) {
|
|
113
143
|
response.error = `res getGroups: caught error: ${error}`;
|
|
@@ -123,7 +153,7 @@ class Groups {
|
|
|
123
153
|
try {
|
|
124
154
|
const groups = message && message.groups ? message.groups : {};
|
|
125
155
|
const devId = message && message.id ? message.id : undefined;
|
|
126
|
-
this.
|
|
156
|
+
this.debug('updateGroupMembership called with ' + JSON.stringify(devId));
|
|
127
157
|
if (devId === undefined) {
|
|
128
158
|
this.adapter.sendTo(from, command, {error: 'No device specified'}, callback);
|
|
129
159
|
}
|
|
@@ -138,30 +168,32 @@ class Groups {
|
|
|
138
168
|
for (const gpid of groups[epid]) {
|
|
139
169
|
const gpidn = parseInt(gpid);
|
|
140
170
|
if (gpidn < 0) {
|
|
141
|
-
this.
|
|
171
|
+
this.warn(`calling removeDevFromGroup with ${sysid}, ${-gpidn}, ${epid}` );
|
|
142
172
|
const response = await this.zbController.removeDevFromGroup(sysid, (-gpidn), epid);
|
|
143
173
|
if (response && response.error) {
|
|
144
174
|
errors.push(response.error);
|
|
145
175
|
this.error(`remove dev from group Error: ${JSON.stringify(response.error)}`);
|
|
146
176
|
}
|
|
147
|
-
|
|
177
|
+
const icon = this.stController.getDefaultGroupIcon(-gpidn)
|
|
148
178
|
} else if (gpidn > 0) {
|
|
149
|
-
this.
|
|
179
|
+
this.warn(`calling addDevToGroup with ${sysid}, ${gpidn}, ${epid}` );
|
|
150
180
|
const response = await this.zbController.addDevToGroup(sysid, (gpidn), epid);
|
|
151
181
|
if (response && response.error) {
|
|
152
182
|
errors.push(response.error);
|
|
153
183
|
this.error(`add dev to group Error: ${JSON.stringify(response.error)}`);
|
|
154
184
|
}
|
|
155
185
|
} else {
|
|
156
|
-
this.
|
|
186
|
+
this.error('illegal group id 0');
|
|
157
187
|
}
|
|
158
188
|
}
|
|
159
189
|
}
|
|
160
190
|
} catch (e) {
|
|
161
|
-
this.warn('caught error ' + (e
|
|
191
|
+
this.warn('caught error ' + JSON.stringify(e) + ' in updateGroupMembership');
|
|
162
192
|
this.adapter.sendTo(from, command, {error: e}, callback);
|
|
163
193
|
return;
|
|
164
194
|
}
|
|
195
|
+
//await this.renameGroup(from, command, { name: undefined, id: message.id});
|
|
196
|
+
this.syncGroups();
|
|
165
197
|
this.adapter.sendTo(from, command, {}, callback);
|
|
166
198
|
}
|
|
167
199
|
|
|
@@ -211,28 +243,41 @@ class Groups {
|
|
|
211
243
|
}
|
|
212
244
|
|
|
213
245
|
async renameGroup(from, command, message) {
|
|
246
|
+
this.debug(`rename group called with ${from}, ${command}, ${JSON.stringify(message)}`);
|
|
214
247
|
// const groupsEntry = await this.adapter.getStateAsync('info.groups');
|
|
215
248
|
// const objGroups = (groupsEntry && groupsEntry.val ? JSON.parse(groupsEntry.val) : {});
|
|
216
249
|
const name = message.name;
|
|
217
250
|
const id = `group_${message.id}`;
|
|
218
|
-
this.stController.
|
|
251
|
+
let icon = this.stController.localConfig.IconForId(id, 'group', await this.stController.getDefaultGroupIcon(id));
|
|
219
252
|
try {
|
|
220
|
-
await this.zbController.verifyGroupExists(message.id);
|
|
253
|
+
const group = await this.zbController.verifyGroupExists(message.id);
|
|
254
|
+
if (message.remove) {
|
|
255
|
+
for (const member of message.remove) {
|
|
256
|
+
const response = await this.zbController.removeDevFromGroup(member.id, id, member.ep);
|
|
257
|
+
this.warn('trying to remove ' + member.id + (member.ep ? '.'+member.ep : '') + ' ' + ' from group ' + message.id + ' response is '+JSON.stringify(response));
|
|
258
|
+
}
|
|
259
|
+
}
|
|
260
|
+
if (icon.match(/img\/group_\d+\.png/g)) {
|
|
261
|
+
this.warn('.');
|
|
262
|
+
icon = await this.zbController.rebuildGroupIcon(group);
|
|
263
|
+
}
|
|
221
264
|
} catch (e) {
|
|
265
|
+
this.warn('renameGroup caught error ' + (e && e.message ? e.message : 'no message'));
|
|
222
266
|
if (e && e.hasOwnProperty('code')) {
|
|
223
267
|
this.warn(`renameGroup caught error ${JSON.stringify(e.code)}`);
|
|
224
268
|
}
|
|
225
269
|
}
|
|
226
|
-
|
|
227
|
-
const group = await this.adapter.
|
|
270
|
+
this.warn(`rename group name ${name}, id ${id}, icon ${icon} remove ${JSON.stringify(message.removeMembers)}`);
|
|
271
|
+
const group = await this.adapter.getObjectAsync(id);
|
|
228
272
|
if (!group) {
|
|
273
|
+
this.warn('group object doesnt exist ')
|
|
229
274
|
// assume we have to create the group
|
|
230
275
|
this.adapter.setObjectNotExists(id, {
|
|
231
276
|
type: 'device',
|
|
232
|
-
common: {name: name, type: 'group'},
|
|
277
|
+
common: {name: (name ? name : `Group ${message.id}` ), type: 'group', icon: icon},
|
|
233
278
|
native: {id}
|
|
234
279
|
}, () => {
|
|
235
|
-
this.adapter.extendObject(id, {common: {name, type: 'group'}});
|
|
280
|
+
this.adapter.extendObject(id, {common: {name, type: 'group', icon: icon}});
|
|
236
281
|
// create writable states for groups from their devices
|
|
237
282
|
for (const stateInd in statesMapping.groupStates) {
|
|
238
283
|
if (!statesMapping.groupStates.hasOwnProperty(stateInd)) {
|
|
@@ -252,25 +297,35 @@ class Groups {
|
|
|
252
297
|
};
|
|
253
298
|
this.stController.updateState(id, statedesc.id, undefined, common);
|
|
254
299
|
}
|
|
300
|
+
this.stController.storeDeviceName(id, name);
|
|
255
301
|
});
|
|
256
302
|
}
|
|
303
|
+
else {
|
|
304
|
+
this.warn('group object exists');
|
|
305
|
+
this.adapter.extendObject(id, {common: {name, type: 'group', icon: icon}});
|
|
306
|
+
}
|
|
257
307
|
}
|
|
258
308
|
|
|
259
309
|
async syncGroups() {
|
|
260
310
|
const groups = await this.getGroups();
|
|
311
|
+
this.debug('sync Groups called: groups is '+ JSON.stringify(groups))
|
|
261
312
|
const chain = [];
|
|
262
313
|
const usedGroupsIds = [];
|
|
314
|
+
let GroupCount = 0;
|
|
263
315
|
for (const j in groups) {
|
|
316
|
+
GroupCount++;
|
|
317
|
+
this.debug(`group ${GroupCount} is ${JSON.stringify(j)}`);
|
|
264
318
|
if (groups.hasOwnProperty(j)) {
|
|
265
319
|
const id = `group_${j}`;
|
|
266
320
|
const name = groups[j];
|
|
321
|
+
const icon = this.stController.localConfig.IconForId(id, 'group', await this.stController.getDefaultGroupIcon(id));
|
|
267
322
|
chain.push(new Promise(resolve => {
|
|
268
323
|
this.adapter.setObjectNotExists(id, {
|
|
269
324
|
type: 'device',
|
|
270
|
-
common: {name: name, type: 'group'},
|
|
325
|
+
common: {name: name, type: 'group', icon: icon },
|
|
271
326
|
native: {id: j}
|
|
272
327
|
}, () => {
|
|
273
|
-
this.adapter.extendObject(id, {common: {type: 'group'}});
|
|
328
|
+
this.adapter.extendObject(id, {common: {type: 'group', icon: icon}});
|
|
274
329
|
// create writable states for groups from their devices
|
|
275
330
|
for (const stateInd in statesMapping.groupStates) {
|
|
276
331
|
if (!statesMapping.groupStates.hasOwnProperty(stateInd)) {
|
|
@@ -316,4 +371,4 @@ class Groups {
|
|
|
316
371
|
}
|
|
317
372
|
}
|
|
318
373
|
|
|
319
|
-
module.exports = Groups;
|
|
374
|
+
module.exports = Groups ;
|
|
@@ -0,0 +1,295 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
/*eslint no-unused-vars: ['off']*/
|
|
4
|
+
const fs = require('fs');
|
|
5
|
+
const path = require('path');
|
|
6
|
+
const utils = require('@iobroker/adapter-core'); // Get common adapter utils
|
|
7
|
+
// const { src } = require('gulp');
|
|
8
|
+
|
|
9
|
+
const EventEmitter = require('events').EventEmitter;
|
|
10
|
+
|
|
11
|
+
class localConfig extends EventEmitter {
|
|
12
|
+
constructor(adapter, options) {
|
|
13
|
+
super();
|
|
14
|
+
this.adapter = adapter;
|
|
15
|
+
this.name = 'localConfig';
|
|
16
|
+
this.localData = { by_id:{}, by_model:{} };
|
|
17
|
+
this.filename = undefined;
|
|
18
|
+
this.basefolder = undefined;
|
|
19
|
+
this.retTimeoutHanlde = undefined;
|
|
20
|
+
this.adapter.on('ready', () => this.onReady());
|
|
21
|
+
this.adapter.on('unload', callback => this.onUnload(callback));
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
|
|
25
|
+
async onReady()
|
|
26
|
+
{
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
async onUnload(callback)
|
|
30
|
+
{
|
|
31
|
+
this.info('local config saved');
|
|
32
|
+
this.retainData();
|
|
33
|
+
callback();
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
info(message) {
|
|
37
|
+
this.adapter.log.info(message);
|
|
38
|
+
this.emit('log', 'info',message);
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
error(message, errobj) {
|
|
42
|
+
this.adapter.log.error(`${this.name}:${message}`);
|
|
43
|
+
this.emit('log', 'error',`${this.name}:${message}`, errobj);
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
warn(message) {
|
|
47
|
+
this.adapter.log.warn(`${this.name}:${message}`);
|
|
48
|
+
this.emit('log', 'warn',`${this.name}:${message}`);
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
debug(message) {
|
|
52
|
+
this.adapter.log.debug(`${this.name}:${message}`);
|
|
53
|
+
this.emit('log', 'debug',`${this.name}:${message}`);
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
async updateDeviceName(id, name) {
|
|
57
|
+
this.debug('updateDev with ' + id + ' and .'+ name +'.');
|
|
58
|
+
if (typeof id != 'string') {
|
|
59
|
+
this.error(`update called with illegal device entry:${JSON.stringify(id)}`)
|
|
60
|
+
return true;
|
|
61
|
+
}
|
|
62
|
+
if (typeof name != 'string' || name.trim().length < 1)
|
|
63
|
+
{
|
|
64
|
+
if (this.localData.hasOwnProperty(id))
|
|
65
|
+
delete this.localData.by_id[id].name;
|
|
66
|
+
}
|
|
67
|
+
else {
|
|
68
|
+
if (this.localData.by_id.hasOwnProperty(id))
|
|
69
|
+
this.localData.by_id[id].name = name;
|
|
70
|
+
else
|
|
71
|
+
this.localData.by_id[id] = { name: name };
|
|
72
|
+
}
|
|
73
|
+
return true;
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
async updateLocalOverride(_target, model, key, data, global)
|
|
77
|
+
{
|
|
78
|
+
const target = (global ? model : _target);
|
|
79
|
+
this.info(`updating local data: (${global ? 'global':'local'}) : ${target}:${key}:${data}`);
|
|
80
|
+
|
|
81
|
+
if (typeof target != 'string' || typeof key != 'string') {
|
|
82
|
+
this.error(`update called with illegal id data:${JSON.stringify(target)}:${JSON.stringify(key)}:${JSON.stringify(data)}`)
|
|
83
|
+
return false;
|
|
84
|
+
}
|
|
85
|
+
const base = global ? this.localData.by_model[target] || {} : this.localData.by_id[target] || {};
|
|
86
|
+
if (data && data.length > 0 && data != 'none') {
|
|
87
|
+
if (key == 'icon')
|
|
88
|
+
base[key] = data.replace(this.basefolder, '.');
|
|
89
|
+
else
|
|
90
|
+
base[key] = data;
|
|
91
|
+
}
|
|
92
|
+
else
|
|
93
|
+
{
|
|
94
|
+
if (base.hasOwnProperty(key)) {
|
|
95
|
+
delete base[key]
|
|
96
|
+
}
|
|
97
|
+
}
|
|
98
|
+
if (global) {
|
|
99
|
+
if (base == {}) delete this.localData.by_model[target];
|
|
100
|
+
else this.localData.by_model[target] = base;
|
|
101
|
+
}
|
|
102
|
+
else {
|
|
103
|
+
if (base == {}) delete this.localData.by_id[target];
|
|
104
|
+
else this.localData.by_id[target] = base;
|
|
105
|
+
}
|
|
106
|
+
this.info(`Local Data for ${target} is ${JSON.stringify(base)}`);
|
|
107
|
+
return true;
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
|
|
111
|
+
NameForId(id, model, defaultName) {
|
|
112
|
+
this.debug('name for id with ' + id + ' and ' + defaultName + ' from ' + JSON.stringify(this.localData));
|
|
113
|
+
const localstorage = (this.localData.by_id[id] || this.localData.by_model[model]);
|
|
114
|
+
if (localstorage && localstorage.hasOwnProperty['name']) return localstorage.name;
|
|
115
|
+
return defaultName;
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
IconForId(id, model, defaultIcon) {
|
|
119
|
+
let modeloverride = {};
|
|
120
|
+
this.debug('Icon for id with ' + id + ', ' + model + ' and ' + defaultIcon);
|
|
121
|
+
if (this.localData.by_id.hasOwnProperty(id))
|
|
122
|
+
{
|
|
123
|
+
modeloverride = this.localData.by_id[id]
|
|
124
|
+
}
|
|
125
|
+
if (!modeloverride.icon) {
|
|
126
|
+
if (this.localData.by_model.hasOwnProperty(model))
|
|
127
|
+
modeloverride = this.localData.by_model[model];
|
|
128
|
+
}
|
|
129
|
+
const iconPath = modeloverride.icon;
|
|
130
|
+
this.debug('icon Path is '+ JSON.stringify(iconPath));
|
|
131
|
+
if (typeof iconPath != 'string') {
|
|
132
|
+
this.debug('icon path is no string, returning ' + JSON.stringify(defaultIcon));
|
|
133
|
+
return defaultIcon;
|
|
134
|
+
}
|
|
135
|
+
if (iconPath.startsWith('http')) return iconPath;
|
|
136
|
+
const namespace = `${this.adapter.name}.admin`;
|
|
137
|
+
const rv = `img/${path.basename(iconPath)}`;
|
|
138
|
+
try {
|
|
139
|
+
this.adapter.fileExists(namespace, rv, (err, result) => {
|
|
140
|
+
if (result) return;
|
|
141
|
+
const src = this.adapter.expandFileName(iconPath).replace('.','_');
|
|
142
|
+
fs.readFile(src, (err, data) => {
|
|
143
|
+
if (err) {
|
|
144
|
+
this.error('unable to read ' + src + ' : '+ (err && err.message? err.message:' no message given'))
|
|
145
|
+
return;
|
|
146
|
+
}
|
|
147
|
+
if (data) {
|
|
148
|
+
this.adapter.writeFile(namespace, rv, data, (err) => {
|
|
149
|
+
if (err) {
|
|
150
|
+
this.error('error writing file ' + path + JSON.stringify(err))
|
|
151
|
+
return;
|
|
152
|
+
}
|
|
153
|
+
this.info('Updated image file ' + rv)
|
|
154
|
+
})
|
|
155
|
+
}
|
|
156
|
+
})
|
|
157
|
+
|
|
158
|
+
})
|
|
159
|
+
} catch (error) {
|
|
160
|
+
this.error(`Error accessing target image: ${error && error.message ? error.message : 'no error message'}`);
|
|
161
|
+
}
|
|
162
|
+
return rv;
|
|
163
|
+
}
|
|
164
|
+
|
|
165
|
+
|
|
166
|
+
async copyDeviceImage(src) {
|
|
167
|
+
const dst = `${this.adapter.adapterDir}/admin/img/${path.basename(src)}`;
|
|
168
|
+
const _src = this.adapter.expandFileName(src);
|
|
169
|
+
if (fs.existsSync(src) && !fs.existsSync(dst))
|
|
170
|
+
{
|
|
171
|
+
try {
|
|
172
|
+
this.log.info(`copying image from :${src} to ${dst}`)
|
|
173
|
+
fs.copyFileSync(src, dst)
|
|
174
|
+
}
|
|
175
|
+
catch {
|
|
176
|
+
this.log.debug(`failed to copy from :${src} to ${dst}`)
|
|
177
|
+
}
|
|
178
|
+
}
|
|
179
|
+
}
|
|
180
|
+
|
|
181
|
+
getOverrideData(target, isGlobal) {
|
|
182
|
+
const base = (isGlobal ? this.localData.by_model : this.localData.by_Id);
|
|
183
|
+
if (base.hasOwnProperty(target)) return base.target;
|
|
184
|
+
return {};
|
|
185
|
+
}
|
|
186
|
+
|
|
187
|
+
setOverrideData(target, isGlobal, data) {
|
|
188
|
+
const base = (isGlobal ? this.localData.by_model : this.localData.byId);
|
|
189
|
+
if (typeof target != 'string') {
|
|
190
|
+
this.error('illegal target for override data: '+JSON.stringify(target));
|
|
191
|
+
return;
|
|
192
|
+
}
|
|
193
|
+
base[target]=data;
|
|
194
|
+
};
|
|
195
|
+
|
|
196
|
+
async getLegacyModels() {
|
|
197
|
+
const legacyModels = [];
|
|
198
|
+
for (const model in this.localData.by_model) {
|
|
199
|
+
if (this.localData.by_model[model].hasOwnProperty('legacy') && this.localData.by_model[model].legacy)
|
|
200
|
+
legacyModels.push(model);
|
|
201
|
+
}
|
|
202
|
+
return legacyModels;
|
|
203
|
+
}
|
|
204
|
+
|
|
205
|
+
getOverridesWithKey(key, isGlobal) {
|
|
206
|
+
const base = (isGlobal ? this.localData.by_model : this.localData.by_Id);
|
|
207
|
+
const rv = [];
|
|
208
|
+
for(const prop in base) {
|
|
209
|
+
if (base[prop].hasOwnProperty(key)) {
|
|
210
|
+
rv.push({key:prop, value:base[prop][key]});
|
|
211
|
+
}
|
|
212
|
+
}
|
|
213
|
+
return rv;
|
|
214
|
+
}
|
|
215
|
+
|
|
216
|
+
async updateFromDeviceNames() {
|
|
217
|
+
this.warn('updateFromDeviceNames');
|
|
218
|
+
const fn = this.adapter.expandFileName('dev_names').replace('.', '_').concat('.json');
|
|
219
|
+
fs.readFile(fn, (err, content) => {
|
|
220
|
+
if (!err) {
|
|
221
|
+
let data_js = {};
|
|
222
|
+
try {
|
|
223
|
+
data_js = JSON.parse(content);
|
|
224
|
+
}
|
|
225
|
+
catch (error) {
|
|
226
|
+
this.error(`unable to parse data read from ${fn} : ${error.message ? error.message : 'undefined error'}`)
|
|
227
|
+
return;
|
|
228
|
+
}
|
|
229
|
+
try {
|
|
230
|
+
for (const prop in data_js) {
|
|
231
|
+
if (data_js[prop] != 'undefined')
|
|
232
|
+
this.updateDeviceName(prop, data_js[prop]);
|
|
233
|
+
}
|
|
234
|
+
}
|
|
235
|
+
catch (error) {
|
|
236
|
+
this.error(`error in updateFromDeviceNames : ${error.message ? error.message : 'undefined error'}`)
|
|
237
|
+
return;
|
|
238
|
+
|
|
239
|
+
}
|
|
240
|
+
this.retainData();
|
|
241
|
+
}
|
|
242
|
+
});
|
|
243
|
+
}
|
|
244
|
+
|
|
245
|
+
async init() {
|
|
246
|
+
this.info('init localConfig');
|
|
247
|
+
const fn = this.adapter.expandFileName('LocalOverrides').replace('.','_').concat('.json');
|
|
248
|
+
this.filename = fn;
|
|
249
|
+
this.basefolder = path.dirname(fn);
|
|
250
|
+
|
|
251
|
+
try {
|
|
252
|
+
const content = fs.readFileSync(fn);
|
|
253
|
+
try {
|
|
254
|
+
const data_js = JSON.parse(content);
|
|
255
|
+
this.localData = data_js;
|
|
256
|
+
}
|
|
257
|
+
catch (error) {
|
|
258
|
+
this.error(`unable to parse data read from ${fn} : ${error.message ? error.message : 'undefined error'}`);
|
|
259
|
+
}
|
|
260
|
+
} catch(error) {
|
|
261
|
+
await this.updateFromDeviceNames();
|
|
262
|
+
}
|
|
263
|
+
}
|
|
264
|
+
|
|
265
|
+
async retainData() {
|
|
266
|
+
//this.warn('retaining local config: ' + JSON.stringify(this.localData));
|
|
267
|
+
try {
|
|
268
|
+
fs.writeFileSync(this.filename, JSON.stringify(this.localData, null, 2))
|
|
269
|
+
this.info('Saved local configuration data');
|
|
270
|
+
}
|
|
271
|
+
catch (error) {
|
|
272
|
+
this.error(`error saving local config: ${error.message}`);
|
|
273
|
+
}
|
|
274
|
+
}
|
|
275
|
+
|
|
276
|
+
enumerateImages(_path) {
|
|
277
|
+
const rv = [];
|
|
278
|
+
try
|
|
279
|
+
{
|
|
280
|
+
const files= fs.readdirSync(_path, {withFileTypes: true, recursive: true}).filter(item => (!item.isDirectory() && item.name.endsWith('.png')));
|
|
281
|
+
files.forEach((item) => {
|
|
282
|
+
const fn = path.join(item.parentPath, item.name);
|
|
283
|
+
rv.push({file: fn, name: item.name, data: fs.readFileSync(path.join(item.parentPath, item.name), 'base64')})
|
|
284
|
+
});
|
|
285
|
+
//this.warn('enumerateImages for ' + _path + ' is ' + JSON.stringify(rv));
|
|
286
|
+
}
|
|
287
|
+
catch (error) {
|
|
288
|
+
this.error(`error in enumerateImages : ${error.message ? error.message : 'undefined error'}`)
|
|
289
|
+
}
|
|
290
|
+
return rv;
|
|
291
|
+
}
|
|
292
|
+
|
|
293
|
+
}
|
|
294
|
+
|
|
295
|
+
module.exports = localConfig;
|
package/lib/ota.js
CHANGED
|
File without changes
|