iobroker.lorawan 1.6.4 → 1.6.5
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 +3 -0
- package/io-package.json +23 -14
- package/lib/modules/downlinkConfighandler.js +228 -10
- package/main.js +2 -2
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -23,6 +23,9 @@ For now there is documentation in English here: https://wiki.hafenmeister.de
|
|
|
23
23
|
Placeholder for the next version (at the beginning of the line):
|
|
24
24
|
### **WORK IN PROGRESS**
|
|
25
25
|
-->
|
|
26
|
+
### 1.6.5 (2025-01-26)
|
|
27
|
+
* (BenAhrdt) export and import downlinkconmfigs with filemanager
|
|
28
|
+
|
|
26
29
|
### 1.6.4 (2025-01-21)
|
|
27
30
|
* (BenAhrdt) Set decoded Structure in deviceInfos (with Merge)
|
|
28
31
|
|
package/io-package.json
CHANGED
|
@@ -1,8 +1,21 @@
|
|
|
1
1
|
{
|
|
2
2
|
"common": {
|
|
3
3
|
"name": "lorawan",
|
|
4
|
-
"version": "1.6.
|
|
4
|
+
"version": "1.6.5",
|
|
5
5
|
"news": {
|
|
6
|
+
"1.6.5": {
|
|
7
|
+
"en": "export and import downlinkconmfigs with filemanager",
|
|
8
|
+
"de": "export und import von downlinkconmfigs mit filemanager",
|
|
9
|
+
"ru": "экспортные и импортные скидки с файловым менеджером",
|
|
10
|
+
"pt": "exportação e importação downlinkconmfigs com filemanager",
|
|
11
|
+
"nl": "downlinkconfigs exporteren en importeren met filemanager",
|
|
12
|
+
"fr": "exporter et importer des liens vers le bas avec filemanager",
|
|
13
|
+
"it": "esportazione e importazione downlinkconmfig con filemanager",
|
|
14
|
+
"es": "exportar e importar downlinkconmfigs con filemanager",
|
|
15
|
+
"pl": "eksport i import downlinkconmfigs z fileman",
|
|
16
|
+
"uk": "експорт і імпорт sdlinkconmfigs з файловmanager",
|
|
17
|
+
"zh-cn": "以文件管理器导出和导入下行链路conmfigs"
|
|
18
|
+
},
|
|
6
19
|
"1.6.4": {
|
|
7
20
|
"en": "Set decoded Structure in deviceInfos (with Merge)",
|
|
8
21
|
"de": "Set decodierte Struktur in deviceInfos (mit Verschmelzung)",
|
|
@@ -80,19 +93,6 @@
|
|
|
80
93
|
"pl": "zmiana odpowiedzi nagłówka",
|
|
81
94
|
"uk": "зміна спонтана",
|
|
82
95
|
"zh-cn": "更改信头响应"
|
|
83
|
-
},
|
|
84
|
-
"1.5.6": {
|
|
85
|
-
"en": "add folder \"uplink.remaining.version_ids\" to writecommands",
|
|
86
|
-
"de": "ordner \"uplink.remaining.version_ids\" zu schreibenbefehlen hinzufügen",
|
|
87
|
-
"ru": "добавить папку \"uplink.remaining.version_ids\" в написание",
|
|
88
|
-
"pt": "adicionar pasta \"uplink.remaining.version_ids\" para writecommands",
|
|
89
|
-
"nl": "map \"uplink.resting.version_ids\" toevoegen aan schrijfopdrachten",
|
|
90
|
-
"fr": "ajouter le dossier \"uplink.remaining.version_ids\" aux commandes d'écriture",
|
|
91
|
-
"it": "aggiungere la cartella \"uplink.remaining.version_ids\" per scriverecomandi",
|
|
92
|
-
"es": "añadir carpeta \"uplink.remaining.version_ids\" para escribircommands",
|
|
93
|
-
"pl": "dodaj folder \"uplink.revening.version _ ids\" do writecommands",
|
|
94
|
-
"uk": "додати папку \"uplink.remaining.version_ids\" для записукоманд",
|
|
95
|
-
"zh-cn": "添加文件夹“ uplink. remaining.version_ ids” 以写入命令"
|
|
96
96
|
}
|
|
97
97
|
},
|
|
98
98
|
"titleLang": {
|
|
@@ -349,6 +349,15 @@
|
|
|
349
349
|
},
|
|
350
350
|
"native": {}
|
|
351
351
|
},
|
|
352
|
+
{
|
|
353
|
+
"_id": "",
|
|
354
|
+
"type": "meta",
|
|
355
|
+
"common": {
|
|
356
|
+
"name": "Downlink files",
|
|
357
|
+
"role": "meta.user"
|
|
358
|
+
},
|
|
359
|
+
"native": {}
|
|
360
|
+
},
|
|
352
361
|
{
|
|
353
362
|
"_id": "info.connection",
|
|
354
363
|
"type": "state",
|
|
@@ -11,6 +11,7 @@ class downlinkConfighandlerClass {
|
|
|
11
11
|
constructor(adapter) {
|
|
12
12
|
this.adapter = adapter;
|
|
13
13
|
this.activeDownlinkConfigs = {};
|
|
14
|
+
this.knownProfiles = [];
|
|
14
15
|
this.deviceProfilesPath = '/lib/modules/deviceProfiles';
|
|
15
16
|
this.internalDeviceProfilesPath = '/lib/modules/deviceProfiles/internal';
|
|
16
17
|
this.internalDevices = {
|
|
@@ -39,6 +40,15 @@ class downlinkConfighandlerClass {
|
|
|
39
40
|
swap: false,
|
|
40
41
|
decimalPlaces: 0,
|
|
41
42
|
};
|
|
43
|
+
|
|
44
|
+
this.metafolders = {
|
|
45
|
+
downlink: {
|
|
46
|
+
itself: 'downlink/',
|
|
47
|
+
uploads: 'downlink/uploads/',
|
|
48
|
+
current: 'downlink/current/',
|
|
49
|
+
knownProfiles: 'downlink/knownProfiles/',
|
|
50
|
+
},
|
|
51
|
+
};
|
|
42
52
|
}
|
|
43
53
|
|
|
44
54
|
/*********************************************************************
|
|
@@ -51,20 +61,21 @@ class downlinkConfighandlerClass {
|
|
|
51
61
|
async addAndMergeDownlinkConfigs() {
|
|
52
62
|
const activeFunction = 'addAndMergeDownlinkConfigs';
|
|
53
63
|
this.adapter.log.silly(`the standard and configed downlinks will be merged`);
|
|
64
|
+
// Generate Metafolders
|
|
65
|
+
await this.generateFoldersFromObject(this.metafolders);
|
|
66
|
+
|
|
67
|
+
// Generate downlinks for known Profiles
|
|
68
|
+
this.generateKnownProfiesFromFiles();
|
|
69
|
+
|
|
54
70
|
try {
|
|
55
71
|
// Add user downlink config first
|
|
56
72
|
for (const downlinkConfig of Object.values(this.adapter.config.downlinkConfig)) {
|
|
57
73
|
this.addDownlinkConfigByType(downlinkConfig, this.activeDownlinkConfigs);
|
|
58
74
|
}
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
if (Array.isArray(internalDownlinks)) {
|
|
64
|
-
for (const downlinkConfig of Object.values(internalDownlinks)) {
|
|
65
|
-
this.addDownlinkConfigByType(downlinkConfig, this.activeDownlinkConfigs);
|
|
66
|
-
}
|
|
67
|
-
}
|
|
75
|
+
|
|
76
|
+
// check for uploads
|
|
77
|
+
await this.checkForUploads();
|
|
78
|
+
|
|
68
79
|
// Check active userconfig
|
|
69
80
|
const adapterId = `system.adapter.${this.adapter.namespace}`;
|
|
70
81
|
const obj = await this.adapter.getForeignObjectAsync(adapterId);
|
|
@@ -90,19 +101,205 @@ class downlinkConfighandlerClass {
|
|
|
90
101
|
this.adapter.log.warn('Adapter restart, because of reinit configuration');
|
|
91
102
|
await this.adapter.setForeignObjectAsync(adapterId, obj);
|
|
92
103
|
}
|
|
104
|
+
// write known Profiles
|
|
105
|
+
await this.writeknowsProfiles(this.knownProfiles);
|
|
106
|
+
|
|
107
|
+
// write downlinkconfig to current folder
|
|
108
|
+
await this.writeCurrentDownlinksconfigs(obj.native.downlinkConfig);
|
|
93
109
|
} catch (error) {
|
|
94
110
|
this.adapter.log.error(`error at ${activeFunction}: ${error}`);
|
|
95
111
|
return undefined;
|
|
96
112
|
}
|
|
97
113
|
}
|
|
98
114
|
|
|
115
|
+
/**
|
|
116
|
+
* no Parameters needed
|
|
117
|
+
*/
|
|
118
|
+
async generateKnownProfiesFromFiles() {
|
|
119
|
+
const activeFunction = 'generateStandardDownlinkConfigFromFiles';
|
|
120
|
+
try {
|
|
121
|
+
// Add standard downlink config if devices not present
|
|
122
|
+
const knownProfiles = this.getJsonArrayFromDirectoryfiles(
|
|
123
|
+
`${this.adapter.adapterDir}${this.deviceProfilesPath}`,
|
|
124
|
+
);
|
|
125
|
+
if (knownProfiles) {
|
|
126
|
+
this.knownProfiles = knownProfiles;
|
|
127
|
+
}
|
|
128
|
+
/*if (Array.isArray(knownProfiles)) {
|
|
129
|
+
for (const downlinkConfig of Object.values(knownProfiles)) {
|
|
130
|
+
this.addDownlinkConfigByType(downlinkConfig, this.knownProfiles);
|
|
131
|
+
}
|
|
132
|
+
}*/
|
|
133
|
+
} catch (error) {
|
|
134
|
+
this.adapter.log.warn(`error in ${activeFunction}: ${error}`);
|
|
135
|
+
}
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
/**
|
|
139
|
+
* @param profiles known profiles to write (eg. from lib/module/deviceProfiles)
|
|
140
|
+
*/
|
|
141
|
+
async writeknowsProfiles(profiles) {
|
|
142
|
+
const activeFunction = 'writeknowsProfiles';
|
|
143
|
+
try {
|
|
144
|
+
// Read all files in folder and delete them
|
|
145
|
+
let metadataOfFiles = await this.adapter.readDirAsync(
|
|
146
|
+
this.adapter.namespace,
|
|
147
|
+
this.metafolders.downlink.knownProfiles,
|
|
148
|
+
);
|
|
149
|
+
for (const element of Object.values(metadataOfFiles)) {
|
|
150
|
+
const filepath = `${this.metafolders.downlink.knownProfiles}${element.file}`;
|
|
151
|
+
//delete file from uploadfolder
|
|
152
|
+
this.adapter.delFileAsync(this.adapter.namespace, filepath);
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
// Write knwon profiles (in one file)
|
|
156
|
+
await this.adapter.writeFileAsync(
|
|
157
|
+
this.adapter.namespace,
|
|
158
|
+
`${this.metafolders.downlink.knownProfiles}KnownProfiles (all devicetypes).json`,
|
|
159
|
+
Buffer.from(JSON.stringify(profiles)),
|
|
160
|
+
);
|
|
161
|
+
|
|
162
|
+
// Write for known profile (separated by devicetypes)
|
|
163
|
+
for (const profile of Object.values(profiles)) {
|
|
164
|
+
await this.adapter.writeFileAsync(
|
|
165
|
+
this.adapter.namespace,
|
|
166
|
+
`${this.metafolders.downlink.knownProfiles}${profile.deviceType}.json`,
|
|
167
|
+
Buffer.from(JSON.stringify(profile)),
|
|
168
|
+
);
|
|
169
|
+
}
|
|
170
|
+
} catch (error) {
|
|
171
|
+
this.adapter.log.warn(`error in ${activeFunction}: ${error}`);
|
|
172
|
+
}
|
|
173
|
+
}
|
|
174
|
+
|
|
175
|
+
/**
|
|
176
|
+
* @param downlinkConfig downlinkconfig to write (eg. from system.adapter.instance)
|
|
177
|
+
*/
|
|
178
|
+
async writeCurrentDownlinksconfigs(downlinkConfig) {
|
|
179
|
+
const activeFunction = 'writeDownlinkconfigs';
|
|
180
|
+
try {
|
|
181
|
+
// Read all files in folder and delete them
|
|
182
|
+
let metadataOfFiles = await this.adapter.readDirAsync(
|
|
183
|
+
this.adapter.namespace,
|
|
184
|
+
this.metafolders.downlink.current,
|
|
185
|
+
);
|
|
186
|
+
for (const element of Object.values(metadataOfFiles)) {
|
|
187
|
+
const filepath = `${this.metafolders.downlink.current}${element.file}`;
|
|
188
|
+
//delete file from uploadfolder
|
|
189
|
+
this.adapter.delFileAsync(this.adapter.namespace, filepath);
|
|
190
|
+
}
|
|
191
|
+
|
|
192
|
+
//Write configs to directory
|
|
193
|
+
// Write whole config
|
|
194
|
+
await this.adapter.writeFileAsync(
|
|
195
|
+
this.adapter.namespace,
|
|
196
|
+
`${this.metafolders.downlink.current}CurrentConfigs (all devicetypes).json`,
|
|
197
|
+
Buffer.from(JSON.stringify(downlinkConfig)),
|
|
198
|
+
);
|
|
199
|
+
// Write for the devicetypes
|
|
200
|
+
for (const downlinkconfig of Object.values(downlinkConfig)) {
|
|
201
|
+
await this.adapter.writeFileAsync(
|
|
202
|
+
this.adapter.namespace,
|
|
203
|
+
`${this.metafolders.downlink.current}${downlinkconfig.deviceType}.json`,
|
|
204
|
+
Buffer.from(JSON.stringify(downlinkconfig)),
|
|
205
|
+
);
|
|
206
|
+
}
|
|
207
|
+
} catch (error) {
|
|
208
|
+
this.adapter.log.warn(`error in ${activeFunction}: ${error}`);
|
|
209
|
+
}
|
|
210
|
+
}
|
|
211
|
+
|
|
212
|
+
/**
|
|
213
|
+
* @param folderObject object wich contains the folderstructure in its elements
|
|
214
|
+
*/
|
|
215
|
+
async generateFoldersFromObject(folderObject) {
|
|
216
|
+
const activeFunction = 'generateFoldersFromObject';
|
|
217
|
+
try {
|
|
218
|
+
for (const folder of Object.values(folderObject)) {
|
|
219
|
+
if (typeof folder === 'object') {
|
|
220
|
+
this.generateFoldersFromObject(folder);
|
|
221
|
+
} else {
|
|
222
|
+
await this.adapter.mkdirAsync(this.adapter.namespace, `${folder}`);
|
|
223
|
+
}
|
|
224
|
+
}
|
|
225
|
+
} catch (error) {
|
|
226
|
+
this.adapter.log.warn(`error in ${activeFunction}: ${error}`);
|
|
227
|
+
}
|
|
228
|
+
}
|
|
229
|
+
|
|
230
|
+
/**
|
|
231
|
+
* no Parameters needed
|
|
232
|
+
*/
|
|
233
|
+
async checkForUploads() {
|
|
234
|
+
const activeFunction = 'checkForUploads';
|
|
235
|
+
this.adapter.log.silly(`check the upload folder for files`);
|
|
236
|
+
let activeFile = '';
|
|
237
|
+
try {
|
|
238
|
+
let metadataOfFiles = await this.adapter.readDirAsync(
|
|
239
|
+
this.adapter.namespace,
|
|
240
|
+
this.metafolders.downlink.uploads,
|
|
241
|
+
);
|
|
242
|
+
for (const element of Object.values(metadataOfFiles)) {
|
|
243
|
+
activeFile = element.file;
|
|
244
|
+
const filepath = `${this.metafolders.downlink.uploads}${element.file}`;
|
|
245
|
+
let readedFileobject = await this.adapter.readFileAsync(this.adapter.namespace, filepath);
|
|
246
|
+
const downlinkConfig = JSON.parse(readedFileobject.file);
|
|
247
|
+
if (Array.isArray(downlinkConfig)) {
|
|
248
|
+
for (const config of Object.values(downlinkConfig)) {
|
|
249
|
+
if (this.plausibilityOfDownlinkconfigOk(config)) {
|
|
250
|
+
this.addDownlinkConfigByType(config, this.activeDownlinkConfigs, { countDeviceType: true });
|
|
251
|
+
}
|
|
252
|
+
}
|
|
253
|
+
} else {
|
|
254
|
+
if (this.plausibilityOfDownlinkconfigOk(downlinkConfig)) {
|
|
255
|
+
this.addDownlinkConfigByType(downlinkConfig, this.activeDownlinkConfigs, {
|
|
256
|
+
countDeviceType: true,
|
|
257
|
+
});
|
|
258
|
+
}
|
|
259
|
+
}
|
|
260
|
+
//delete file from uploadfolder
|
|
261
|
+
this.adapter.delFileAsync(this.adapter.namespace, filepath);
|
|
262
|
+
}
|
|
263
|
+
} catch (error) {
|
|
264
|
+
this.adapter.log.warn(
|
|
265
|
+
`error in ${activeFunction} at reading ${activeFile} from downlink uploadpath. Error: ${error}`,
|
|
266
|
+
);
|
|
267
|
+
}
|
|
268
|
+
}
|
|
269
|
+
|
|
270
|
+
/**
|
|
271
|
+
* @param downlinkconfig downlinkconfig to check for plausibility
|
|
272
|
+
*/
|
|
273
|
+
plausibilityOfDownlinkconfigOk(downlinkconfig) {
|
|
274
|
+
const activeFunction = 'checkPlausibilityOfDownlinkconfig';
|
|
275
|
+
try {
|
|
276
|
+
if (downlinkconfig.deviceType && downlinkconfig.downlinkParameter) {
|
|
277
|
+
this.adapter.log.debug(
|
|
278
|
+
`plausibility check for uploaded downlinkconfig of ${downlinkconfig.deviceType} ok.`,
|
|
279
|
+
);
|
|
280
|
+
return true;
|
|
281
|
+
}
|
|
282
|
+
this.adapter.log.debug(
|
|
283
|
+
`plausibility check for uploaded downlinkconfig of ${downlinkconfig.deviceType} not ok.`,
|
|
284
|
+
);
|
|
285
|
+
return false;
|
|
286
|
+
} catch (error) {
|
|
287
|
+
this.adapter.log.warn(`error in ${activeFunction}: ${error}`);
|
|
288
|
+
}
|
|
289
|
+
}
|
|
290
|
+
|
|
99
291
|
/**
|
|
100
292
|
* @param downlinkConfig downlinkconfig to add
|
|
101
293
|
* @param config active downlinkConfig
|
|
294
|
+
* @param options countDeviceType: the deviceType in case of exists}
|
|
102
295
|
*/
|
|
103
|
-
addDownlinkConfigByType(downlinkConfig, config) {
|
|
296
|
+
addDownlinkConfigByType(downlinkConfig, config, options = {}) {
|
|
104
297
|
const activeFunction = 'addDownlinkConfigByType';
|
|
105
298
|
try {
|
|
299
|
+
if (options && options.countDeviceType) {
|
|
300
|
+
downlinkConfig.deviceType = this.addCountToElementname(config, downlinkConfig.deviceType);
|
|
301
|
+
}
|
|
302
|
+
|
|
106
303
|
// Check for device not present
|
|
107
304
|
if (!config[downlinkConfig.deviceType]) {
|
|
108
305
|
// override standard with userconfig
|
|
@@ -143,6 +340,27 @@ class downlinkConfighandlerClass {
|
|
|
143
340
|
}
|
|
144
341
|
}
|
|
145
342
|
|
|
343
|
+
/**
|
|
344
|
+
* @param objectToCHeck object to check and count the elementname
|
|
345
|
+
* @param name string of the name to check
|
|
346
|
+
*/
|
|
347
|
+
addCountToElementname(objectToCHeck, name) {
|
|
348
|
+
const activeFunction = 'addCountToElementname';
|
|
349
|
+
try {
|
|
350
|
+
let count = 0;
|
|
351
|
+
const zeroDiggits = '00';
|
|
352
|
+
let countedName = name;
|
|
353
|
+
while (objectToCHeck[countedName] || count >= 99) {
|
|
354
|
+
count++;
|
|
355
|
+
const countPrefix = (zeroDiggits + count.toString()).slice(-zeroDiggits.length);
|
|
356
|
+
countedName = `${name}_${countPrefix}`;
|
|
357
|
+
}
|
|
358
|
+
return countedName;
|
|
359
|
+
} catch (error) {
|
|
360
|
+
this.adapter.log.warn(`error in ${activeFunction}: ${error}`);
|
|
361
|
+
}
|
|
362
|
+
}
|
|
363
|
+
|
|
146
364
|
/**
|
|
147
365
|
* @param directory directory of the file with the json array
|
|
148
366
|
*/
|
package/main.js
CHANGED
|
@@ -25,7 +25,6 @@ class Lorawan extends utils.Adapter {
|
|
|
25
25
|
// this.on("objectChange", this.onObjectChange.bind(this));
|
|
26
26
|
this.on('message', this.onMessage.bind(this));
|
|
27
27
|
this.on('unload', this.onUnload.bind(this));
|
|
28
|
-
|
|
29
28
|
this.origin = {
|
|
30
29
|
ttn: 'ttn',
|
|
31
30
|
chirpstack: 'chirpstack',
|
|
@@ -68,10 +67,11 @@ class Lorawan extends utils.Adapter {
|
|
|
68
67
|
this.log.silly(
|
|
69
68
|
`the active downlinkconfigs are: ${JSON.stringify(this.downlinkConfighandler.activeDownlinkConfigs)}`,
|
|
70
69
|
);
|
|
71
|
-
|
|
70
|
+
/*
|
|
72
71
|
setTimeout(async () => {
|
|
73
72
|
await this.startSimulation();
|
|
74
73
|
}, 5000);
|
|
74
|
+
*/
|
|
75
75
|
/*this.simulation.timeout = setTimeout(async () => {
|
|
76
76
|
const topic = "application/d63c10b6-9263-4ab3-9299-4308fa19a2ad/device/f1c0ae0e-b4a2-4547-b360-7cfa15e85734/command/down";
|
|
77
77
|
const message = {devEui:"f1c0ae0e-b4a2-4547-b360-7cfa15e85734",confirmed:false,fPort:1,data:"AAA"};
|