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 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",
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
- // Add standard downlink config if devices not present
60
- const internalDownlinks = this.getJsonArrayFromDirectoryfiles(
61
- `${this.adapter.adapterDir}${this.deviceProfilesPath}`,
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"};
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "iobroker.lorawan",
3
- "version": "1.6.4",
3
+ "version": "1.6.5",
4
4
  "description": "converts the desired lora gateway data to a ioBroker structure",
5
5
  "author": {
6
6
  "name": "BenAhrdt",