iobroker.lorawan 0.0.2 → 0.0.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/README.md CHANGED
@@ -19,6 +19,9 @@ Adapter was created in collaboration with Joerg Froehner J-Paul0815@hafenmeister
19
19
  Placeholder for the next version (at the beginning of the line):
20
20
  ### **WORK IN PROGRESS**
21
21
  -->
22
+ ### 0.0.3 (2024-01-14)
23
+ * (BenAhrdt) first config for downlinks inputed
24
+
22
25
  ### 0.0.2 (2024-01-12)
23
26
  * (BenAhrdt) initial release
24
27
 
@@ -1,89 +1,237 @@
1
1
  {
2
2
  "i18n": true,
3
- "type": "panel",
4
- "items": {
5
- "ServersettingsHeader": {
6
- "newLine": true,
7
- "type": "header",
8
- "text": "ServersettingsHeader",
9
- "size": 3
10
- },
11
- "Serverinformation":{
12
- "newLine":true,
13
- "type": "staticText",
14
- "label": "Serverinformation"
15
- },
16
- "ipUrl":{
17
- "newLine": true,
18
- "type": "text",
19
- "label": "ipUrl",
20
- "tooltip": "ipUrlTooltip",
21
- "default": "",
22
- "sm": 4
3
+ "type": "tabs",
4
+ "items":{
5
+ "mainTab":{
6
+ "type": "panel",
7
+ "label": "mainSettings",
8
+ "items": {
9
+ "ServersettingsHeader": {
10
+ "newLine": true,
11
+ "type": "header",
12
+ "text": "ServersettingsHeader",
13
+ "size": 3
14
+ },
15
+ "Serverinformation":{
16
+ "newLine":true,
17
+ "type": "staticText",
18
+ "label": "Serverinformation"
19
+ },
20
+ "ipUrl":{
21
+ "newLine": true,
22
+ "type": "text",
23
+ "label": "ipUrl",
24
+ "tooltip": "ipUrlTooltip",
25
+ "default": "",
26
+ "sm": 4
27
+ },
28
+ "port":{
29
+ "type": "number",
30
+ "label": "port",
31
+ "tooltip": "portTooltip",
32
+ "default": 8883,
33
+ "sm": 1
34
+ },
35
+ "ssl":{
36
+ "type": "checkbox",
37
+ "label": "SSL",
38
+ "tooltip": "sslTooltip",
39
+ "default": true
40
+ },
41
+ "AuthenticationHeader": {
42
+ "newLine": true,
43
+ "type": "header",
44
+ "text": "AuthenticationHeader",
45
+ "size": 3
46
+ },
47
+ "AuthenticationInformation":{
48
+ "newLine":true,
49
+ "type": "staticText",
50
+ "label": "AuthenticationInformation"
51
+ },
52
+ "username":{
53
+ "newLine":true,
54
+ "type": "text",
55
+ "label": "username",
56
+ "tooltip": "usernameTooltip",
57
+ "default": "",
58
+ "sm": 4
59
+ },
60
+ "password":{
61
+ "type": "password",
62
+ "label": "password",
63
+ "tooltip": "passwordTooltip",
64
+ "repeat": true,
65
+ "default": "",
66
+ "sm": 6
67
+ },
68
+ "OriginHeader": {
69
+ "newLine": true,
70
+ "type": "header",
71
+ "text": "OriginHeader",
72
+ "size": 3
73
+ },
74
+ "OriginInformation":{
75
+ "newLine":true,
76
+ "type": "staticText",
77
+ "label": "OriginInformation"
78
+ },
79
+ "ttn":{
80
+ "newLine":true,
81
+ "type": "checkbox",
82
+ "label": "Ttn",
83
+ "tooltip": "ttnTooltip",
84
+ "default": true
85
+ },
86
+ "chirpstack":{
87
+ "type": "checkbox",
88
+ "label": "Chirpstack",
89
+ "tooltip": "chirpstackTooltip",
90
+ "default": false
91
+ }
92
+ }
23
93
  },
24
- "port":{
25
- "type": "number",
26
- "label": "port",
27
- "tooltip": "portTooltip",
28
- "default": 8883,
29
- "sm": 1
30
- },
31
- "ssl":{
32
- "type": "checkbox",
33
- "label": "SSL",
34
- "tooltip": "sslTooltip",
35
- "default": true
36
- },
37
- "AuthenticationHeader": {
38
- "newLine": true,
39
- "type": "header",
40
- "text": "AuthenticationHeader",
41
- "size": 3
42
- },
43
- "AuthenticationInformation":{
44
- "newLine":true,
45
- "type": "staticText",
46
- "label": "AuthenticationInformation"
47
- },
48
- "username":{
49
- "newLine":true,
50
- "type": "text",
51
- "label": "username",
52
- "tooltip": "usernameTooltip",
53
- "default": "",
54
- "sm": 4
55
- },
56
- "password":{
57
- "type": "password",
58
- "label": "password",
59
- "tooltip": "passwordTooltip",
60
- "repeat": true,
61
- "default": "",
62
- "sm": 6
63
- },
64
- "OriginHeader": {
65
- "newLine": true,
66
- "type": "header",
67
- "text": "OriginHeader",
68
- "size": 3
69
- },
70
- "OriginInformation":{
71
- "newLine":true,
72
- "type": "staticText",
73
- "label": "OriginInformation"
74
- },
75
- "ttn":{
76
- "newLine":true,
77
- "type": "checkbox",
78
- "label": "Ttn",
79
- "tooltip": "ttnTooltip",
80
- "default": true
81
- },
82
- "chirpstack":{
83
- "type": "checkbox",
84
- "label": "Chirpstack",
85
- "tooltip": "chirpstackTooltip",
86
- "default": false
94
+ "downlinkConfigTab":{
95
+ "type": "panel",
96
+ "label": "downlinkConfig",
97
+ "items": {
98
+ "downlinkConfigAccordion":{
99
+ "type":"accordion",
100
+ "titleAttr": "name",
101
+ "sm":12,
102
+ "items":[
103
+ {
104
+ "type": "text",
105
+ "attr": "name",
106
+ "label": "name",
107
+ "tooltip": "nameTooltip",
108
+ "default": "",
109
+ "sm":2
110
+ },
111
+ {
112
+ "type": "number",
113
+ "attr": "port",
114
+ "label": "port",
115
+ "tooltip": "portTooltip",
116
+ "default": 2,
117
+ "sm":2
118
+ },
119
+ {
120
+ "type": "text",
121
+ "attr": "priority",
122
+ "label": "priority",
123
+ "tooltip": "priorityTooltip",
124
+ "default": "NORMAL",
125
+ "sm":2
126
+ },
127
+ {
128
+ "type": "select",
129
+ "attr": "type",
130
+ "label": "type",
131
+ "tooltip": "typeTooltip",
132
+ "options": [
133
+ {"label":"boolean","value":"boolean"},
134
+ {"label":"number","value":"number"},
135
+ {"label":"ASCII","value":"ascii"},
136
+ {"label":"String","value":"string"}
137
+ ],
138
+ "default": "boolean",
139
+ "sm":2
140
+ },
141
+ {
142
+ "type": "checkbox",
143
+ "attr": "confirmed",
144
+ "label": "confirmed",
145
+ "tooltip": "confirmedTooltip",
146
+ "default": false,
147
+ "sm":2
148
+ },
149
+ {
150
+ "newLine": true,
151
+ "type": "text",
152
+ "attr": "front",
153
+ "label": "front",
154
+ "tooltip": "frontTooltip",
155
+ "default": "03",
156
+ "hidden": "data.type === 'boolean'",
157
+ "sm":2
158
+ },
159
+ {
160
+ "type": "text",
161
+ "attr": "end",
162
+ "label": "end",
163
+ "tooltip": "endTooltip",
164
+ "default": "11",
165
+ "hidden": "data.type === 'boolean'",
166
+ "sm":2
167
+ },
168
+ {
169
+ "type": "select",
170
+ "attr": "length",
171
+ "label": "length",
172
+ "tooltip": "lengthTooltip",
173
+ "options": [
174
+ {"label":"2","value":2},
175
+ {"label":"4","value":4},
176
+ {"label":"6","value":6},
177
+ {"label":"8","value":8}
178
+ ],
179
+ "default": 2,
180
+ "hidden": "data.type === 'boolean'",
181
+ "sm":2
182
+ },
183
+ {
184
+ "newLine": true,
185
+ "type": "text",
186
+ "attr": "on",
187
+ "label": "on",
188
+ "tooltip": "onTooltip",
189
+ "default": "11",
190
+ "hidden": "data.type !== 'boolean'",
191
+ "sm":2
192
+ },
193
+ {
194
+ "type": "text",
195
+ "attr": "off",
196
+ "label": "off",
197
+ "tooltip": "offTooltip",
198
+ "default": "11",
199
+ "hidden": "data.type !== 'boolean'",
200
+ "sm":2
201
+ },
202
+ {
203
+ "type": "number",
204
+ "attr": "multiplyfaktor",
205
+ "label": "multiplyfaktor",
206
+ "tooltip": "multiplyfaktorTooltip",
207
+ "default": "1",
208
+ "hidden": "data.type !== 'number'",
209
+ "sm":2
210
+ },
211
+ {
212
+ "type": "text",
213
+ "attr": "unit",
214
+ "label": "unit",
215
+ "tooltip": "unitTooltip",
216
+ "default": "",
217
+ "hidden": "data.type === 'boolean' || data.type === 'string'",
218
+ "sm":2
219
+ },
220
+ {
221
+ "type": "autocomplete",
222
+ "attr": "deviceType",
223
+ "label": "deviceType",
224
+ "tooltip": "deviceTypeTooltip",
225
+ "options": [
226
+ {"label":"all","value":"all"}
227
+ ],
228
+ "default": "all",
229
+ "freeSolo": true,
230
+ "sm":2
231
+ }
232
+ ]
233
+ }
234
+ }
87
235
  }
88
236
  }
89
237
  }
package/io-package.json CHANGED
@@ -1,8 +1,21 @@
1
1
  {
2
2
  "common": {
3
3
  "name": "lorawan",
4
- "version": "0.0.2",
4
+ "version": "0.0.3",
5
5
  "news": {
6
+ "0.0.3": {
7
+ "en": "first config for downlinks inputed",
8
+ "de": "erste config für downlinks eingegeben",
9
+ "ru": "первая настройка для входных ссылок",
10
+ "pt": "primeiro config para downlinks inseridos",
11
+ "nl": "eerste configuratie voor downlinks ingevoerd",
12
+ "fr": "première configuration pour les liaisons descendantes entrée",
13
+ "it": "prima configurazione per downlink in ingresso",
14
+ "es": "primer config para downlinks",
15
+ "pl": "pierwszy config dla downlinks nieznany",
16
+ "uk": "перший конфігурація для вхідних посилань",
17
+ "zh-cn": "输入下行链路的第一个配置"
18
+ },
6
19
  "0.0.2": {
7
20
  "en": "initial release",
8
21
  "de": "erstausstrahlung",
@@ -98,7 +111,8 @@
98
111
  "password": "",
99
112
  "ssl": true,
100
113
  "ttn": true,
101
- "chirpstack": false
114
+ "chirpstack": false,
115
+ "downlinkConfigAccordion": {}
102
116
  },
103
117
  "objects": [],
104
118
  "instanceObjects": [
@@ -16,6 +16,7 @@ class messagehandlerClass {
16
16
  };
17
17
 
18
18
  this.safeableDirectories = {
19
+ configuration: "configuration",
19
20
  uplinkDecoded: "uplinkDecoded",
20
21
  uplinkRaw: "uplinkRaw",
21
22
  uplinkRemaining: "uplinkRemaining",
@@ -59,6 +60,14 @@ class messagehandlerClass {
59
60
  },
60
61
  objectCommonName: "device ID",
61
62
  objectType:"channel",
63
+ configuration:{
64
+ safeDirectory: this.safeableDirectories.configuration,
65
+ devicetype:{
66
+ isState: true,
67
+ stateCommonType: "string",
68
+ stateCommonWrite: true,
69
+ }
70
+ },
62
71
  uplink:{
63
72
  raw:{
64
73
  safeDirectory: this.safeableDirectories.uplinkRaw
@@ -75,43 +84,10 @@ class messagehandlerClass {
75
84
  safeDirectory: this.safeableDirectories.downlinkRaw
76
85
  },
77
86
  control:{
78
- safeDirectory: this.safeableDirectories.downlinkControl,
79
- push: {
80
- isState: true,
81
- stateVal: {
82
- downlinks: [{
83
- f_port: 128,
84
- frm_payload: "Pw==",
85
- priority: "NORMAL"
86
- }]
87
- },
88
- stateCommonType: "json",
89
- subscribe: true,
90
- stateCommonWrite: true
91
- },
92
- replace: {
93
- isState: true,
94
- stateVal: {
95
- downlinks: [{
96
- f_port: 128,
97
- frm_payload: "Pw==",
98
- priority: "NORMAL"
99
- }]
100
- },
101
- stateCommonType: "json",
102
- subscribe: true,
103
- stateCommonWrite: true
104
- }
87
+ safeDirectory: this.safeableDirectories.downlinkControl
105
88
  },
106
89
  configuration:{
107
90
  safeDirectory: this.safeableDirectories.downlinkConfiguration,
108
- refreshRate:{
109
- isState: true,
110
- stateVal: 2,
111
- stateCommonType: "number",
112
- stateFactor: 60,
113
- stateCommonWrite: true,
114
- }
115
91
  },
116
92
  remaining:{
117
93
  safeDirectory: this.safeableDirectories.downlinkRemaining
@@ -127,7 +103,6 @@ class messagehandlerClass {
127
103
  objectCommonName: "objectCommonName",
128
104
  objectType: "objectType",
129
105
  stateVal: "stateVal",
130
- stateFactor: "stateFactor",
131
106
  stateCommonType: "stateCommonType",
132
107
  stateCommonWrite: "stateCommonWrite",
133
108
  stateCommonUnit: "stateCommonUnit",
@@ -186,7 +161,7 @@ class messagehandlerClass {
186
161
  let stateCommonWrite = false;
187
162
  let stateVal = obj[elementName];
188
163
  if(obj[elementName].isState){
189
- stateVal = obj[elementName].stateVal? obj[elementName].stateVal : undefined;
164
+ stateVal = obj[elementName].stateVal !== undefined? obj[elementName].stateVal: undefined;
190
165
  stateCommonType = obj[elementName].stateCommonType? obj[elementName].stateCommonType : typeof stateVal;
191
166
  stateCommonName = obj[elementName].stateCommonName ? obj[elementName].stateCommonName : stateCommonName;
192
167
  stateCommonWrite = obj[elementName].stateCommonWrite ? obj[elementName].stateCommonWrite : stateCommonWrite;
@@ -208,6 +183,7 @@ class messagehandlerClass {
208
183
  role: "value",
209
184
  read: true,
210
185
  unit: obj[elementName].CommonStateUnit? obj[elementName].CommonStateUnit : this.units[internalObjectId]? this.units[internalObjectId] : "",
186
+ def: stateCommonType === "boolean"? false : stateCommonType === "number"? 0: "",
211
187
  write: stateCommonWrite
212
188
  },
213
189
  native: {},
@@ -215,7 +191,9 @@ class messagehandlerClass {
215
191
  if(typeof stateVal === "object"){
216
192
  stateVal = JSON.stringify(stateVal);
217
193
  }
218
- await this.adapter.setStateAsync(`${objectId}`,stateVal,true);
194
+ if(stateVal !== undefined){
195
+ await this.adapter.setStateAsync(`${objectId}`,stateVal,true);
196
+ }
219
197
  if(obj[elementName].subscribe){
220
198
  this.adapter.subscribeStatesAsync(objectId);
221
199
  }
@@ -0,0 +1,60 @@
1
+ class downlinkConfigClass {
2
+ constructor(adapter) {
3
+ this.adapter = adapter;
4
+ //this.presentConfigs = {};
5
+
6
+ this.internalDownlinks = [
7
+ {
8
+ name: "push",
9
+ type: "json",
10
+ deviceType: "all"
11
+ },
12
+ {
13
+ name: "replace",
14
+ type: "json",
15
+ deviceType: "all"
16
+ }
17
+ ];
18
+
19
+ this.activeDownlinkConfigs = {};
20
+ }
21
+
22
+ addDownlinkConfigByType(downlinkConfig){
23
+ if(!this.activeDownlinkConfigs[downlinkConfig.deviceType]){
24
+ this.activeDownlinkConfigs[downlinkConfig.deviceType] = {};
25
+ }
26
+ if(!this.activeDownlinkConfigs[downlinkConfig.deviceType][downlinkConfig.name]){
27
+ this.activeDownlinkConfigs[downlinkConfig.deviceType][downlinkConfig.name] = downlinkConfig;
28
+ }
29
+ }
30
+ /*
31
+ addConfigToPresentIdList(downlinkConfig,id){
32
+ // Check if the Config is general, or an model specific.
33
+ this.presentConfigs[id] = downlinkConfig;
34
+ }
35
+ */
36
+ getDownlinkConfig(changeInfo){
37
+ const activeFunction = "getDownlinkConfig";
38
+ try{
39
+ if(this.activeDownlinkConfigs[changeInfo.deviceType] && this.activeDownlinkConfigs[changeInfo.deviceType][changeInfo.changedState])
40
+ {
41
+ return this.activeDownlinkConfigs[changeInfo.deviceType][changeInfo.changedState];
42
+ }
43
+ else{
44
+ changeInfo.deviceType = "all";
45
+ if(this.activeDownlinkConfigs[changeInfo.deviceType] && this.activeDownlinkConfigs[changeInfo.deviceType][changeInfo.changedState])
46
+ {
47
+ return this.activeDownlinkConfigs[changeInfo.deviceType][changeInfo.changedState];
48
+ }
49
+ else{
50
+ this.adapter.log.warn(`${activeFunction}: no downlinkConfig found: deviceType: ${changeInfo.deviceType} - changed state: ${changeInfo.changedState}`);
51
+ }
52
+ }
53
+ }
54
+ catch(error){
55
+ this.adapter.log.error(`error at ${activeFunction}: ` + error);
56
+ }
57
+ }
58
+ }
59
+
60
+ module.exports = downlinkConfigClass;
@@ -1,4 +1,3 @@
1
-
2
1
  const directorieshandlerClass = require("./directorieshandler");
3
2
 
4
3
  class messagehandlerClass {
@@ -7,6 +6,58 @@ class messagehandlerClass {
7
6
  this.directoryhandler = new directorieshandlerClass(this.adapter);
8
7
  }
9
8
 
9
+ getDownlink(downlinkConfig,state){
10
+ // Select datahandling in case of origin
11
+ if(this.adapter.config.ttn){
12
+ return this.getTtnDownlink(downlinkConfig,state);
13
+ }
14
+ else if(this.adapter.config.chirpstack){
15
+ // this.handleChirpstack(topic,message);
16
+ }
17
+ }
18
+
19
+ getTtnDownlink(downlinkConfig,state){
20
+ // declare pyaload variable
21
+ let payloadInHex = "";
22
+ let multipliedVal = 0;
23
+ //Check type
24
+ if(downlinkConfig.type === "boolean"){
25
+ if(state.val){
26
+ payloadInHex = downlinkConfig.on;
27
+ }
28
+ else{
29
+ payloadInHex = downlinkConfig.off;
30
+ }
31
+ }
32
+ else{
33
+ switch(downlinkConfig.type){
34
+ case "number":
35
+ multipliedVal = state.val * downlinkConfig.multiplyfaktor;
36
+ payloadInHex = multipliedVal.toString(16).toUpperCase();
37
+ break;
38
+
39
+ case "ascii":
40
+ case "string":
41
+ payloadInHex = Buffer.from(state.val).toString("hex");
42
+ break;
43
+ }
44
+ const numberOfDiggits = downlinkConfig.length - downlinkConfig.front.length + downlinkConfig.end.length;
45
+ let zeroDiggits = "";
46
+
47
+ for(let index = 1; index <= numberOfDiggits; index++){
48
+ zeroDiggits += "0";
49
+ }
50
+ payloadInHex = (zeroDiggits + payloadInHex).slice(-numberOfDiggits);
51
+ payloadInHex = downlinkConfig.front + payloadInHex + downlinkConfig.end;
52
+ }
53
+
54
+ //convert hex in base64
55
+ const payloadInBase64 = Buffer.from(payloadInHex, "hex").toString("base64");
56
+
57
+ // retun the whole downlink
58
+ return {downlinks:[{f_port:downlinkConfig.port,frm_payload:payloadInBase64,priority:downlinkConfig.priority,confirmed:downlinkConfig.confirmed}]};
59
+ }
60
+
10
61
  async handleMessage(topic,message){
11
62
  // Select datahandling in case of origin
12
63
  if(this.adapter.config.ttn){
@@ -18,7 +69,21 @@ class messagehandlerClass {
18
69
  }
19
70
 
20
71
  async handleTtnMessage(topic,message){
21
-
72
+ /*if(message.uplink_message){
73
+ if(message.end_device_ids.dev_eui === "A84041162183F8FB"){
74
+ this.adapter.log.debug("UPLINK");
75
+ this.adapter.log.debug(JSON.stringify(message.uplink_message.decoded_payload));
76
+ }
77
+ }
78
+ else{
79
+ this.adapter.log.debug("Downlink");
80
+ if(message.downlink_sent){
81
+ this.adapter.log.debug("Gesendet");
82
+ }
83
+ else{
84
+ this.adapter.log.debug("Quittiert");
85
+ }
86
+ }*/
22
87
  // generate startdorectory of device
23
88
  const deviceStartdirectory = this.directoryhandler.getTtnObjectDirectory(message,this.directoryhandler.searchableAttributeNames.deviceId);
24
89
  /*********************************************************************
@@ -244,6 +309,59 @@ class messagehandlerClass {
244
309
  await this.directoryhandler.generateRekursivObjects(message[downlinkType],startDirectory,message,{ignoredElementNames:{frm_payload:{}}});
245
310
  }
246
311
  }
312
+
313
+ /*********************************************************************
314
+ * ************************* downlinks *******************************
315
+ * ******************************************************************/
316
+ /* // general downling => push and replace (json out of data)
317
+ for(const downlink of Object.values(this.adapter.downlinkConfig.internalDownlinks)){
318
+ if(this.directoryhandler.reachableDirectories[deviceStartdirectory][this.directoryhandler.safeableDirectories.downlinkControl]){
319
+ const startDirectory = this.directoryhandler.reachableDirectories[deviceStartdirectory][this.directoryhandler.safeableDirectories.downlinkControl];
320
+
321
+ await this.adapter.setObjectNotExistsAsync(`${startDirectory}.${downlink.name}`,{
322
+ type: "state",
323
+ common: {
324
+ name: "",
325
+ type: "json",
326
+ role: "value",
327
+ read: true,
328
+ write: true
329
+ },
330
+ native: {},
331
+ });
332
+ }
333
+ }
334
+ */
335
+
336
+ // configed and internal downlinks
337
+ this.adapter.log.info("CONFIG: " + JSON.stringify(this.adapter.downlinkConfig.activeDownlinkConfigs));
338
+ for(const downlinkDevices of Object.values(this.adapter.downlinkConfig.activeDownlinkConfigs)){
339
+ for(const downlinkConfig of Object.values(downlinkDevices)){
340
+ if(this.directoryhandler.reachableDirectories[deviceStartdirectory][this.directoryhandler.safeableDirectories.downlinkControl]){
341
+ const startDirectory = this.directoryhandler.reachableDirectories[deviceStartdirectory][this.directoryhandler.safeableDirectories.downlinkControl];
342
+ const changeInfo = await this.adapter.getChangeInfo(`${startDirectory}.${downlinkConfig.name}`);
343
+ if(downlinkConfig.deviceType === "all" || downlinkConfig.deviceType === changeInfo.deviceType){
344
+ let CommonStateType = downlinkConfig.type;
345
+ if(CommonStateType === "ascii"){
346
+ CommonStateType = "string";
347
+ }
348
+ await this.adapter.setObjectNotExistsAsync(`${startDirectory}.${downlinkConfig.name}`,{
349
+ type: "state",
350
+ common: {
351
+ name: "",
352
+ type: CommonStateType,
353
+ role: "value",
354
+ read: true,
355
+ write: true,
356
+ unit: downlinkConfig.unit? downlinkConfig.unit:"",
357
+ def: CommonStateType === "boolean"? false : CommonStateType === "number"? 0: "",
358
+ },
359
+ native: {},
360
+ });
361
+ }
362
+ }
363
+ }
364
+ }
247
365
  }
248
366
  catch(error){
249
367
  this.adapter.log.warn("check: " + error);
@@ -272,7 +390,7 @@ class messagehandlerClass {
272
390
  applicationId : `/${changeInfo.applicationId}`,
273
391
  applicationFrom : "@ttn",
274
392
  devices : `/devices`,
275
- dev_uid : `/${changeInfo.dev_uid}`,
393
+ device_id : `/${changeInfo.device_id}`,
276
394
  suffix : suffix
277
395
  };
278
396
  let downlink = "";
package/main.js CHANGED
@@ -10,6 +10,7 @@
10
10
  const utils = require("@iobroker/adapter-core");
11
11
  const mqttClientClass = require("./lib/modules/mqttclient");
12
12
  const messagehandlerClass = require("./lib/modules/messagehandler");
13
+ const downlinkConfigClass = require("./lib/modules/downlinkConfig");
13
14
 
14
15
  // Load your modules here, e.g.:
15
16
  // const fs = require("fs");
@@ -30,45 +31,116 @@ class Lorawan extends utils.Adapter {
30
31
  // this.on("message", this.onMessage.bind(this));
31
32
  this.on("unload", this.onUnload.bind(this));
32
33
  this.mqttClient = {};
33
- this.changeInfo = {};
34
+ // @ts-ignore
34
35
  }
35
36
 
36
37
  /**
37
38
  * Is called when databases are connected and adapter received configuration.
38
39
  */
39
40
  async onReady() {
40
- /*
41
- let a = {b:"",c:"2"};
42
- a.val = JSON.parse(JSON.stringify(a));
43
- this.log.debug(JSON.stringify(a));
44
- delete a.val;
45
- this.log.debug(JSON.stringify(a));
46
- return;*/
47
- // create new messagehandler
48
- this.messagehandler = new messagehandlerClass(this);
49
-
50
- // Set all mqtt clients
51
- this.mqttClient = new mqttClientClass(this,this.config);
41
+ const activeFunction = "onReady";
42
+ try{
52
43
  /*
53
- // Subscribe all States (given from messagehandler)
54
- this.subscibeableStates = this.messagehandler.getSubscribeableStates(undefined);
55
- if(this.subscibeableStates){
56
- for(const subscibeableState of Object.values(this.subscibeableStates)){
57
- this.subscribeStatesAsync(`*.${subscibeableState}`);
44
+ Definitionen der Umrechnungen:
45
+ dec to hex:
46
+ const decdata = 33;
47
+ const decdatastring = decdata.toString(16);
48
+
49
+ base64 to hex:
50
+ return(Buffer.from(base_64, 'base64').toString("hex"));
51
+
52
+ ascii to hex:
53
+ return (Buffer.from(ascii).toString('hex'));
54
+
55
+ ascii to base64:
56
+ return (Buffer.from(ascii).toString('base64'));
57
+
58
+ base64 to string:
59
+ return(Buffer.from(base_64, 'base64').toString());
60
+
61
+ hex 2 base64:
62
+ return Buffer.from(hex, 'hex').toString('base64')
63
+
64
+ hex 2 number:
65
+ parseInt(hexdata,16);
66
+
67
+
68
+ return Math.abs(dec).toString(16);
69
+ // force 4 Digits
70
+ //return ('0000' + dec.toString(16).toUpperCase()).slice(-4);
71
+ // force 2 Digits
72
+ // return ('00' + dec.toString(16).toUpperCase()).slice(-2);
73
+
74
+ */
75
+ /*
76
+ let a = {b:"",c:"2"};
77
+ a.val = JSON.parse(JSON.stringify(a));
78
+ this.log.debug(JSON.stringify(a));
79
+ delete a.val;
80
+ this.log.debug(JSON.stringify(a));
81
+ return;*/
82
+ //00b1006400018b27104a0000
83
+ // ALEAZAABiycQSgAA
84
+
85
+
86
+ /* const base_64 = "Pw==";
87
+ const hex = Buffer.from(base_64, "base64").toString("hex");
88
+ this.log.debug(hex);
89
+ const newBase64 = Buffer.from(hex, "hex").toString("base64");
90
+ this.log.debug(newBase64);
91
+ return*/
92
+
93
+ // create downlinkConfigs
94
+ this.downlinkConfig = new downlinkConfigClass(this);
95
+
96
+ // create new messagehandler
97
+ this.messagehandler = new messagehandlerClass(this);
98
+
99
+ // Set all mqtt clients
100
+ this.mqttClient = new mqttClientClass(this,this.config);
101
+ /*
102
+ // Subscribe all States (given from messagehandler)
103
+ this.subscibeableStates = this.messagehandler.getSubscribeableStates(undefined);
104
+ if(this.subscibeableStates){
105
+ for(const subscibeableState of Object.values(this.subscibeableStates)){
106
+ this.subscribeStatesAsync(`*.${subscibeableState}`);
107
+ }
58
108
  }
109
+ */
110
+ // this.subscribeStatesAsync(`*.push`);
111
+ // this.subscribeStatesAsync(`*.replace`);
112
+ /*
113
+ setTimeout(() => {
114
+ this.mqttClient[1]?.publish("R/c0619ab24727/keepalive",null);
115
+ }, 1000);*/
116
+ // Reset the connection indicator during startup
117
+ /*setTimeout(() => {
118
+ this.mqttClient?.publish("v3/hafi-ttn-lorawan@ttn/devices/eui-lobaro-modbus/down/push",JSON.stringify({"downlinks":[{"f_port": 128,"frm_payload":"Pw==","priority": "NORMAL"}]}));
119
+ }, 5000);
120
+ this.setState("info.connection", false, true);*/
121
+ //await this.getAdapterObjects();
122
+
123
+ //this.assignAdapterconfigs();
124
+
125
+ this.addAndMergeDownlinks();
126
+
127
+ //Subscribe all configuration and control states
128
+ this.subscribeStatesAsync("*downlink.configuration.*");
129
+ this.subscribeStatesAsync("*downlink.control.*");
130
+ }
131
+ catch(error){
132
+ this.log.error(`error at ${activeFunction}: ` + error);
133
+ }
134
+ }
135
+
136
+ addAndMergeDownlinks(){
137
+ // @ts-ignore
138
+ for(const downlinkConfig of Object.values(this.downlinkConfig.internalDownlinks)){
139
+ this.downlinkConfig?.addDownlinkConfigByType(downlinkConfig);
140
+ }
141
+ for(const downlinkConfig of Object.values(this.config.downlinkConfigAccordion)){
142
+ this.downlinkConfig?.addDownlinkConfigByType(downlinkConfig);
59
143
  }
60
- */
61
- this.subscribeStatesAsync(`*.push`);
62
- this.subscribeStatesAsync(`*.replace`);
63
- /*
64
- setTimeout(() => {
65
- this.mqttClient[1]?.publish("R/c0619ab24727/keepalive",null);
66
- }, 1000);*/
67
- // Reset the connection indicator during startup
68
- /*setTimeout(() => {
69
- this.mqttClient?.publish("v3/hafi-ttn-lorawan@ttn/devices/eui-lobaro-modbus/down/push",JSON.stringify({"downlinks":[{"f_port": 128,"frm_payload":"Pw==","priority": "NORMAL"}]}));
70
- }, 5000);
71
- this.setState("info.connection", false, true);*/
72
144
  }
73
145
 
74
146
  /**
@@ -112,33 +184,50 @@ class Lorawan extends utils.Adapter {
112
184
  * @param {ioBroker.State | null | undefined} state
113
185
  */
114
186
  async onStateChange(id, state) {
115
- if (state) {
116
- // this.log.debug(`state ${id} changed: val: ${state.val} - ack: ${state.ack}`);
117
- // The state was changed => only states with ack = false will be processed, others will be ignored
118
- if(!state.ack){
119
- // get information of the changing state
120
- // @ts-ignore
121
- this.changeInfo = this.getChangeInfo(id);
122
- let appending = "";
123
- if(this.changeInfo.changedState === "push"){
187
+ const activeFunction = "onStateChange";
188
+ try{
189
+ if (state) {
190
+ //this.log.debug(`state ${id} changed: val: ${state.val} - ack: ${state.ack}`);
191
+ // The state was changed => only states with ack = false will be processed, others will be ignored
192
+ if(!state.ack){
193
+ // get information of the changing state
124
194
  // @ts-ignore
125
- appending = "push";
126
- const downlinkTopic = this.messagehandler?.getDownlinkTopic(this.changeInfo,`/down/${appending}`);
127
- //this.sendDownlink(downlinkTopic,JSON.stringify(state.val));
128
- this.sendDownlink(downlinkTopic,state.val);
129
- this.setStateAsync(id,state.val,true);
130
- }
131
- else if(this.changeInfo.changedState === "replace"){
195
+ const changeInfo = await this.getChangeInfo(id);
196
+ let appending = "push";
197
+ // @ts-ignore
198
+ if(changeInfo.changedState === "push"){
199
+ const downlinkTopic = this.messagehandler?.getDownlinkTopic(changeInfo,`/down/${appending}`);
200
+ //this.sendDownlink(downlinkTopic,JSON.stringify(state.val));
201
+ this.sendDownlink(downlinkTopic,state.val);
202
+ this.setStateAsync(id,state.val,true);
203
+ }
132
204
  // @ts-ignore
133
- appending = "replace";
134
- const downlinkTopic = this.messagehandler?.getDownlinkTopic(this.changeInfo,`/down/${appending}`);
135
- this.sendDownlink(downlinkTopic,state.val);
136
- this.setStateAsync(id,state.val,true);
205
+ else if(changeInfo.changedState === "replace"){
206
+ appending = "replace";
207
+ const downlinkTopic = this.messagehandler?.getDownlinkTopic(changeInfo,`/down/${appending}`);
208
+ this.sendDownlink(downlinkTopic,state.val);
209
+ this.setStateAsync(id,state.val,true);
210
+ }
211
+ else{
212
+ const downlinkTopic = this.messagehandler?.getDownlinkTopic(changeInfo,`/down/${appending}`);
213
+ // @ts-ignore
214
+ const downlinkConfig = this.downlinkConfig?.getDownlinkConfig(changeInfo);
215
+ if(downlinkConfig !== undefined){
216
+ const downlink = this.messagehandler?.getDownlink(downlinkConfig,state);
217
+ if(downlink !== undefined){
218
+ this.sendDownlink(downlinkTopic,JSON.stringify(downlink));
219
+ }
220
+ this.setStateAsync(id,state.val,true);
221
+ }
222
+ }
137
223
  }
224
+ } else {
225
+ // The state was deleted
226
+ this.log.info(`state ${id} deleted`);
138
227
  }
139
- } else {
140
- // The state was deleted
141
- this.log.info(`state ${id} deleted`);
228
+ }
229
+ catch(error){
230
+ this.log.error(`error at ${activeFunction}: ` + error);
142
231
  }
143
232
  }
144
233
 
@@ -146,27 +235,48 @@ class Lorawan extends utils.Adapter {
146
235
  this.mqttClient?.publish(topic,message);
147
236
  }
148
237
 
149
- getChangeInfo(id){
150
- // Select datahandling in case of origin
151
- if(this.config.ttn){
152
- return this.getTtnChangeInfo(id);
238
+ async getChangeInfo(id){
239
+ const activeFunction = "getChangeInfo";
240
+ try{
241
+ // Select datahandling in case of origin
242
+ if(this.config.ttn){
243
+ return await this.getTtnChangeInfo(id);
244
+ }
245
+ else if(this.config.chirpstack){
246
+ // this.handleChirpstack(topic,message);
247
+ }
153
248
  }
154
- else if(this.config.chirpstack){
155
- // this.handleChirpstack(topic,message);
249
+ catch(error){
250
+ this.log.error(`error at ${activeFunction}: ` + error);
156
251
  }
157
252
  }
158
253
 
159
- getTtnChangeInfo(id){
160
- id = id.substring(this.namespace.length + 1,id.length);
161
- const idElements = id.split(".");
162
- const changeInfo = {
163
- applicationId : idElements[0],
164
- dev_uid : idElements[3],
165
- device_id : idElements[2],
166
- changedState : idElements[idElements.length - 1],
167
- allElements : idElements
168
- };
169
- return changeInfo;
254
+ async getTtnChangeInfo(id){
255
+ const activeFunction = "getTtnChangeInfo";
256
+ try{
257
+ if(id.indexOf(this.namespace) !== -1){
258
+ id = id.substring(this.namespace.length + 1,id.length);
259
+ }
260
+ const idElements = id.split(".");
261
+ const changeInfo = {
262
+ applicationId : idElements[0],
263
+ dev_uid : idElements[2],
264
+ device_id : idElements[3],
265
+ changedState : idElements[idElements.length - 1],
266
+ deviceType : "",
267
+ allElements : idElements
268
+ };
269
+ const myId = `${changeInfo.applicationId}.devices.${changeInfo.dev_uid}.${changeInfo.device_id}.configuration.devicetype`;
270
+ const deviceTypeIdState = await this.getStateAsync(myId);
271
+ if(deviceTypeIdState){
272
+ // @ts-ignore
273
+ changeInfo.deviceType = deviceTypeIdState.val;
274
+ }
275
+ return changeInfo;
276
+ }
277
+ catch(error){
278
+ this.log.error(`error at ${activeFunction}: ` + error);
279
+ }
170
280
  }
171
281
 
172
282
  /* getStringprefix(source,searchstring,times){
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "iobroker.lorawan",
3
- "version": "0.0.2",
3
+ "version": "0.0.3",
4
4
  "description": "converts the desired lora gateway data to a ioBroker structure",
5
5
  "author": {
6
6
  "name": "BenAhrdt",