iobroker.lorawan 0.0.2
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/LICENSE +21 -0
- package/README.md +46 -0
- package/admin/i18n/de/translations.json +9 -0
- package/admin/i18n/en/translations.json +9 -0
- package/admin/i18n/es/translations.json +9 -0
- package/admin/i18n/fr/translations.json +9 -0
- package/admin/i18n/it/translations.json +9 -0
- package/admin/i18n/nl/translations.json +9 -0
- package/admin/i18n/pl/translations.json +9 -0
- package/admin/i18n/pt/translations.json +9 -0
- package/admin/i18n/ru/translations.json +9 -0
- package/admin/i18n/uk/translations.json +9 -0
- package/admin/i18n/zh-cn/translations.json +9 -0
- package/admin/jsonConfig.json +89 -0
- package/admin/lorawan.png +0 -0
- package/io-package.json +127 -0
- package/lib/adapter-config.d.ts +19 -0
- package/lib/modules/directorieshandler.js +312 -0
- package/lib/modules/messagehandler.js +291 -0
- package/lib/modules/mqttclient.js +52 -0
- package/main.js +208 -0
- package/package.json +78 -0
|
@@ -0,0 +1,312 @@
|
|
|
1
|
+
class messagehandlerClass {
|
|
2
|
+
constructor(adapter) {
|
|
3
|
+
this.adapter = adapter;
|
|
4
|
+
|
|
5
|
+
// used dataentries in directory structurt
|
|
6
|
+
this.elementDeclaration = {
|
|
7
|
+
objectStateName: "objectStateName",
|
|
8
|
+
objectCommonName: "objectCommonName",
|
|
9
|
+
moreDirectories: "moreDirectories"
|
|
10
|
+
};
|
|
11
|
+
|
|
12
|
+
this.searchableAttributeNames = {
|
|
13
|
+
apllicationId: "applicationId",
|
|
14
|
+
deviceUid: "devEui",
|
|
15
|
+
deviceId: "deviceId"
|
|
16
|
+
};
|
|
17
|
+
|
|
18
|
+
this.safeableDirectories = {
|
|
19
|
+
uplinkDecoded: "uplinkDecoded",
|
|
20
|
+
uplinkRaw: "uplinkRaw",
|
|
21
|
+
uplinkRemaining: "uplinkRemaining",
|
|
22
|
+
downlinkRaw: "downlinkRaw",
|
|
23
|
+
downlinkConfiguration: "downlinkConfiguration",
|
|
24
|
+
downlinkControl: "downlinkControl",
|
|
25
|
+
downlinkRemaining: "downlinkRemaining"
|
|
26
|
+
};
|
|
27
|
+
|
|
28
|
+
this.reachableDirectories = {};
|
|
29
|
+
|
|
30
|
+
this.units = {
|
|
31
|
+
"pressure": "mBar",
|
|
32
|
+
"battery": "V",
|
|
33
|
+
"battery_level": "%",
|
|
34
|
+
"humidity": "%",
|
|
35
|
+
"Batterie": "V",
|
|
36
|
+
"Temperatur": "°C",
|
|
37
|
+
"airhumidity": "%",
|
|
38
|
+
"volt": "V",
|
|
39
|
+
"temperatur" : "°C",
|
|
40
|
+
"airtemperature" : "°C"
|
|
41
|
+
};
|
|
42
|
+
|
|
43
|
+
// declare the directory structre
|
|
44
|
+
this.directories = {
|
|
45
|
+
application:{
|
|
46
|
+
objectStateName : (message) =>{
|
|
47
|
+
return this.getAttributValue(message,this.searchableAttributeNames.apllicationId);
|
|
48
|
+
},
|
|
49
|
+
objectCommonName: "application",
|
|
50
|
+
devices:{
|
|
51
|
+
deviceUid:{
|
|
52
|
+
objectStateName : (message) =>{
|
|
53
|
+
return this.getAttributValue(message,this.searchableAttributeNames.deviceUid);
|
|
54
|
+
},
|
|
55
|
+
objectCommonName: "device UID",
|
|
56
|
+
deviceId:{
|
|
57
|
+
objectStateName : (message) =>{
|
|
58
|
+
return this.getAttributValue(message,this.searchableAttributeNames.deviceId);
|
|
59
|
+
},
|
|
60
|
+
objectCommonName: "device ID",
|
|
61
|
+
objectType:"channel",
|
|
62
|
+
uplink:{
|
|
63
|
+
raw:{
|
|
64
|
+
safeDirectory: this.safeableDirectories.uplinkRaw
|
|
65
|
+
},
|
|
66
|
+
decoded:{
|
|
67
|
+
safeDirectory: this.safeableDirectories.uplinkDecoded
|
|
68
|
+
},
|
|
69
|
+
remaining:{
|
|
70
|
+
safeDirectory: this.safeableDirectories.uplinkRemaining
|
|
71
|
+
}
|
|
72
|
+
},
|
|
73
|
+
downlink:{
|
|
74
|
+
raw:{
|
|
75
|
+
safeDirectory: this.safeableDirectories.downlinkRaw
|
|
76
|
+
},
|
|
77
|
+
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
|
+
}
|
|
105
|
+
},
|
|
106
|
+
configuration:{
|
|
107
|
+
safeDirectory: this.safeableDirectories.downlinkConfiguration,
|
|
108
|
+
refreshRate:{
|
|
109
|
+
isState: true,
|
|
110
|
+
stateVal: 2,
|
|
111
|
+
stateCommonType: "number",
|
|
112
|
+
stateFactor: 60,
|
|
113
|
+
stateCommonWrite: true,
|
|
114
|
+
}
|
|
115
|
+
},
|
|
116
|
+
remaining:{
|
|
117
|
+
safeDirectory: this.safeableDirectories.downlinkRemaining
|
|
118
|
+
}
|
|
119
|
+
},
|
|
120
|
+
},
|
|
121
|
+
}
|
|
122
|
+
}
|
|
123
|
+
}
|
|
124
|
+
};
|
|
125
|
+
this.ignoredElementNames ={
|
|
126
|
+
objectStateName: "objectStateName",
|
|
127
|
+
objectCommonName: "objectCommonName",
|
|
128
|
+
objectType: "objectType",
|
|
129
|
+
stateVal: "stateVal",
|
|
130
|
+
stateFactor: "stateFactor",
|
|
131
|
+
stateCommonType: "stateCommonType",
|
|
132
|
+
stateCommonWrite: "stateCommonWrite",
|
|
133
|
+
stateCommonUnit: "stateCommonUnit",
|
|
134
|
+
isState: "isState",
|
|
135
|
+
safeDirectory: "safeDirectory",
|
|
136
|
+
subscribe: "subscribe"
|
|
137
|
+
};
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
/*********************************************************************
|
|
141
|
+
* ************************ Objectstring *****************************
|
|
142
|
+
* ******************************************************************/
|
|
143
|
+
|
|
144
|
+
async generateRekursivObjects(obj,startDirectory,message,options = undefined){
|
|
145
|
+
// just proceed with ojects
|
|
146
|
+
if(typeof obj === "object"){
|
|
147
|
+
// go to every element in the object
|
|
148
|
+
for(const elementName in obj){
|
|
149
|
+
// Check the the elementname is not in ignored object
|
|
150
|
+
// @ts-ignore
|
|
151
|
+
if(!this.ignoredElementNames[elementName] && !options?.ignoredElementNames[elementName]){
|
|
152
|
+
// Check if the element is an object
|
|
153
|
+
if(typeof obj[elementName] === "object" && !obj[elementName].isState){
|
|
154
|
+
// if there is an declared ObjectStateName (must be a function)=> take it
|
|
155
|
+
let objectId = `${startDirectory}.${elementName}`;
|
|
156
|
+
let internalObjectId = elementName;
|
|
157
|
+
if(obj[elementName][this.elementDeclaration.objectStateName]){
|
|
158
|
+
internalObjectId = `${obj[elementName][this.elementDeclaration.objectStateName](message)}`;
|
|
159
|
+
objectId = `${startDirectory}.${internalObjectId}`;
|
|
160
|
+
}
|
|
161
|
+
if(objectId.indexOf(".") === 0){
|
|
162
|
+
objectId = objectId.substring(1,objectId.length);
|
|
163
|
+
}
|
|
164
|
+
if(obj[elementName].safeDirectory){
|
|
165
|
+
if(!this.reachableDirectories){
|
|
166
|
+
this.reachableDirectories = {};
|
|
167
|
+
}
|
|
168
|
+
if(!this.reachableDirectories[this.getObjectDirectory(message,this.searchableAttributeNames.deviceId)]){
|
|
169
|
+
this.reachableDirectories[this.getObjectDirectory(message,this.searchableAttributeNames.deviceId)] = {};
|
|
170
|
+
}
|
|
171
|
+
this.reachableDirectories[this.getObjectDirectory(message,this.searchableAttributeNames.deviceId)][obj[elementName].safeDirectory] = objectId;
|
|
172
|
+
}
|
|
173
|
+
await this.adapter.setObjectNotExistsAsync(objectId,{
|
|
174
|
+
// @ts-ignore
|
|
175
|
+
type: obj[elementName][this.elementDeclaration.objectType]? obj[elementName][this.elementDeclaration.objectType] : "folder",
|
|
176
|
+
common: {
|
|
177
|
+
name: obj[elementName][this.elementDeclaration.objectCommonName]? obj[elementName][this.elementDeclaration.objectCommonName] : ""
|
|
178
|
+
},
|
|
179
|
+
native : {},
|
|
180
|
+
});
|
|
181
|
+
await this.generateRekursivObjects(obj[elementName],objectId,message);
|
|
182
|
+
}
|
|
183
|
+
else{
|
|
184
|
+
let stateCommonType = typeof obj[elementName];
|
|
185
|
+
let stateCommonName = "";
|
|
186
|
+
let stateCommonWrite = false;
|
|
187
|
+
let stateVal = obj[elementName];
|
|
188
|
+
if(obj[elementName].isState){
|
|
189
|
+
stateVal = obj[elementName].stateVal? obj[elementName].stateVal : undefined;
|
|
190
|
+
stateCommonType = obj[elementName].stateCommonType? obj[elementName].stateCommonType : typeof stateVal;
|
|
191
|
+
stateCommonName = obj[elementName].stateCommonName ? obj[elementName].stateCommonName : stateCommonName;
|
|
192
|
+
stateCommonWrite = obj[elementName].stateCommonWrite ? obj[elementName].stateCommonWrite : stateCommonWrite;
|
|
193
|
+
}
|
|
194
|
+
let objectId = `${startDirectory}.${elementName}`;
|
|
195
|
+
let internalObjectId = elementName;
|
|
196
|
+
if(obj[elementName][this.elementDeclaration.objectStateName]){
|
|
197
|
+
internalObjectId = `${obj[elementName][this.elementDeclaration.objectStateName](message)}`;
|
|
198
|
+
objectId = `${startDirectory}.${internalObjectId}`;
|
|
199
|
+
}
|
|
200
|
+
if(objectId.indexOf(".") === 0){
|
|
201
|
+
objectId.substring(1,objectId.length);
|
|
202
|
+
}
|
|
203
|
+
await this.adapter.setObjectNotExistsAsync(objectId,{
|
|
204
|
+
type: "state",
|
|
205
|
+
common: {
|
|
206
|
+
type: stateCommonType,
|
|
207
|
+
name: stateCommonName,
|
|
208
|
+
role: "value",
|
|
209
|
+
read: true,
|
|
210
|
+
unit: obj[elementName].CommonStateUnit? obj[elementName].CommonStateUnit : this.units[internalObjectId]? this.units[internalObjectId] : "",
|
|
211
|
+
write: stateCommonWrite
|
|
212
|
+
},
|
|
213
|
+
native: {},
|
|
214
|
+
});
|
|
215
|
+
if(typeof stateVal === "object"){
|
|
216
|
+
stateVal = JSON.stringify(stateVal);
|
|
217
|
+
}
|
|
218
|
+
await this.adapter.setStateAsync(`${objectId}`,stateVal,true);
|
|
219
|
+
if(obj[elementName].subscribe){
|
|
220
|
+
this.adapter.subscribeStatesAsync(objectId);
|
|
221
|
+
}
|
|
222
|
+
}
|
|
223
|
+
}
|
|
224
|
+
}
|
|
225
|
+
}
|
|
226
|
+
}
|
|
227
|
+
|
|
228
|
+
/*********************************************************************
|
|
229
|
+
* ************************ Objectstring *****************************
|
|
230
|
+
* ******************************************************************/
|
|
231
|
+
|
|
232
|
+
attributPresentInMessage(message,resolvetype){
|
|
233
|
+
// Select search in case of origin
|
|
234
|
+
if(this.adapter.config.ttn){
|
|
235
|
+
return this.attributPresentInTtnMessage(message,resolvetype);
|
|
236
|
+
}
|
|
237
|
+
else if(this.adapter.config.chirpstack){
|
|
238
|
+
// this.handleChirpstack(topic,message);
|
|
239
|
+
}
|
|
240
|
+
}
|
|
241
|
+
|
|
242
|
+
attributPresentInTtnMessage(message,resolvetype){
|
|
243
|
+
switch(resolvetype){
|
|
244
|
+
case this.searchableAttributeNames.apllicationId:
|
|
245
|
+
return message.end_device_ids.application_ids.application_id? true: false;
|
|
246
|
+
|
|
247
|
+
case this.searchableAttributeNames.deviceUid:
|
|
248
|
+
return message.end_device_ids.dev_eui? true: false;
|
|
249
|
+
|
|
250
|
+
case this.searchableAttributeNames.deviceId:
|
|
251
|
+
return message.end_device_ids.device_id? true: false;
|
|
252
|
+
|
|
253
|
+
default:
|
|
254
|
+
this.adapter.log.warn(`No attribute with the name ${resolvetype} found.`);
|
|
255
|
+
return "";
|
|
256
|
+
}
|
|
257
|
+
}
|
|
258
|
+
|
|
259
|
+
getAttributValue(message,resolvetype){
|
|
260
|
+
// Select search in case of origin
|
|
261
|
+
if(this.adapter.config.ttn){
|
|
262
|
+
return this.getTtnAttributValue(message,resolvetype);
|
|
263
|
+
}
|
|
264
|
+
else if(this.adapter.config.chirpstack){
|
|
265
|
+
// this.handleChirpstack(topic,message);
|
|
266
|
+
}
|
|
267
|
+
}
|
|
268
|
+
|
|
269
|
+
getTtnAttributValue(message,resolvetype){
|
|
270
|
+
switch(resolvetype){
|
|
271
|
+
case this.searchableAttributeNames.apllicationId:
|
|
272
|
+
return message.end_device_ids.application_ids.application_id;
|
|
273
|
+
|
|
274
|
+
case this.searchableAttributeNames.deviceUid:
|
|
275
|
+
return message.end_device_ids.dev_eui;
|
|
276
|
+
|
|
277
|
+
case this.searchableAttributeNames.deviceId:
|
|
278
|
+
return message.end_device_ids.device_id;
|
|
279
|
+
|
|
280
|
+
default:
|
|
281
|
+
this.adapter.log.warn(`No attribute with the name ${resolvetype} found.`);
|
|
282
|
+
return "";
|
|
283
|
+
}
|
|
284
|
+
}
|
|
285
|
+
|
|
286
|
+
getObjectDirectory(message,resolvetype){
|
|
287
|
+
// Select search in case of origin
|
|
288
|
+
if(this.adapter.config.ttn){
|
|
289
|
+
return this.getTtnObjectDirectory(message,resolvetype);
|
|
290
|
+
}
|
|
291
|
+
else if(this.adapter.config.chirpstack){
|
|
292
|
+
// this.handleChirpstack(topic,message);
|
|
293
|
+
}
|
|
294
|
+
}
|
|
295
|
+
|
|
296
|
+
getTtnObjectDirectory(message,resolvetype){
|
|
297
|
+
if(typeof message !== "string"){
|
|
298
|
+
switch(resolvetype){
|
|
299
|
+
case this.searchableAttributeNames.deviceId:
|
|
300
|
+
return `${message.end_device_ids.application_ids.application_id}.${message.end_device_ids.dev_eui}.${message.end_device_ids.device_id}`;
|
|
301
|
+
|
|
302
|
+
default:
|
|
303
|
+
return message;
|
|
304
|
+
}
|
|
305
|
+
}
|
|
306
|
+
else{
|
|
307
|
+
return message;
|
|
308
|
+
}
|
|
309
|
+
}
|
|
310
|
+
}
|
|
311
|
+
|
|
312
|
+
module.exports = messagehandlerClass;
|
|
@@ -0,0 +1,291 @@
|
|
|
1
|
+
|
|
2
|
+
const directorieshandlerClass = require("./directorieshandler");
|
|
3
|
+
|
|
4
|
+
class messagehandlerClass {
|
|
5
|
+
constructor(adapter) {
|
|
6
|
+
this.adapter = adapter;
|
|
7
|
+
this.directoryhandler = new directorieshandlerClass(this.adapter);
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
async handleMessage(topic,message){
|
|
11
|
+
// Select datahandling in case of origin
|
|
12
|
+
if(this.adapter.config.ttn){
|
|
13
|
+
await this.handleTtnMessage(topic,message);
|
|
14
|
+
}
|
|
15
|
+
else if(this.adapter.config.chirpstack){
|
|
16
|
+
// await this.handleChirpstackMessage(topic,message);
|
|
17
|
+
}
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
async handleTtnMessage(topic,message){
|
|
21
|
+
|
|
22
|
+
// generate startdorectory of device
|
|
23
|
+
const deviceStartdirectory = this.directoryhandler.getTtnObjectDirectory(message,this.directoryhandler.searchableAttributeNames.deviceId);
|
|
24
|
+
/*********************************************************************
|
|
25
|
+
* ************************ Main directories *************************
|
|
26
|
+
* ******************************************************************/
|
|
27
|
+
/* if(message.end_device_ids.device_id !== "eui-lobaro-modbus"){
|
|
28
|
+
return;
|
|
29
|
+
}*/
|
|
30
|
+
try{
|
|
31
|
+
await this.directoryhandler.generateRekursivObjects(this.directoryhandler.directories,"",message);
|
|
32
|
+
/*********************************************************************
|
|
33
|
+
* ************************* Infodata ********************************
|
|
34
|
+
* ******************************************************************/
|
|
35
|
+
/*
|
|
36
|
+
if(message[this.ttn.dataEntries.uplink_message]){
|
|
37
|
+
if(message[this.ttn.dataEntries.uplink_message][this.ttn.dataEntries.f_port] === this.ttn.writeableData.firmware.port){
|
|
38
|
+
const stringdata = Buffer.from(message[this.ttn.dataEntries.uplink_message][this.ttn.dataEntries.frm_payload], "base64").toString();
|
|
39
|
+
await this.adapter.setStateAsync(`${stateId}.${this.ttn.writeableData.firmware.name}`,stringdata,true);
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
else if(message[this.ttn.dataEntries.downlink_queued]){
|
|
43
|
+
if(message[this.ttn.dataEntries.downlink_queued][this.ttn.dataEntries.f_port] === this.ttn.writeableData.firmware.port){
|
|
44
|
+
await this.adapter.setStateAsync(`${stateId}.${this.ttn.writeableData.firmware.name}`,this.ttn.writeableData.firmware.downlink_queued,true);
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
else if(message[this.ttn.dataEntries.downlink_sent]){
|
|
48
|
+
if(message[this.ttn.dataEntries.downlink_sent][this.ttn.dataEntries.f_port] === this.ttn.writeableData.firmware.port){
|
|
49
|
+
await this.adapter.setStateAsync(`${stateId}.${this.ttn.writeableData.firmware.name}`,this.ttn.writeableData.firmware.downlink_sent,true);
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
*/
|
|
53
|
+
/*********************************************************************
|
|
54
|
+
* ********************** Uplink data ********************************
|
|
55
|
+
* ******************************************************************/
|
|
56
|
+
|
|
57
|
+
// check for uplink message
|
|
58
|
+
if(message.uplink_message){
|
|
59
|
+
|
|
60
|
+
/*********************************************************************
|
|
61
|
+
* ************************ Rawdata json *****************************
|
|
62
|
+
* ******************************************************************/
|
|
63
|
+
|
|
64
|
+
if(this.directoryhandler.reachableDirectories[deviceStartdirectory][this.directoryhandler.safeableDirectories.uplinkRaw]){
|
|
65
|
+
const startDirectory = this.directoryhandler.reachableDirectories[deviceStartdirectory][this.directoryhandler.safeableDirectories.uplinkRaw];
|
|
66
|
+
// write json
|
|
67
|
+
await this.adapter.setObjectNotExistsAsync(`${startDirectory}.json`,{
|
|
68
|
+
type: "state",
|
|
69
|
+
common: {
|
|
70
|
+
name: "last recieved message",
|
|
71
|
+
type: "json",
|
|
72
|
+
role: "value",
|
|
73
|
+
read: true,
|
|
74
|
+
write: false
|
|
75
|
+
},
|
|
76
|
+
native: {},
|
|
77
|
+
});
|
|
78
|
+
await this.adapter.setStateAsync(`${startDirectory}.json`,JSON.stringify(message),true);
|
|
79
|
+
|
|
80
|
+
/*********************************************************************
|
|
81
|
+
* ********************** Rawdata (Base64) ***************************
|
|
82
|
+
* ******************************************************************/
|
|
83
|
+
|
|
84
|
+
// check for frm payload
|
|
85
|
+
if(message.uplink_message.frm_payload){
|
|
86
|
+
// wite base64 data
|
|
87
|
+
await this.adapter.setObjectNotExistsAsync(`${startDirectory}.base64`,{
|
|
88
|
+
type: "state",
|
|
89
|
+
common: {
|
|
90
|
+
name: "last recieved data as base64",
|
|
91
|
+
type: "string",
|
|
92
|
+
role: "value",
|
|
93
|
+
read: true,
|
|
94
|
+
write: false
|
|
95
|
+
},
|
|
96
|
+
native: {},
|
|
97
|
+
});
|
|
98
|
+
const writedata = message.uplink_message.frm_payload;
|
|
99
|
+
await this.adapter.setStateAsync(`${startDirectory}.base64`,writedata,true);
|
|
100
|
+
|
|
101
|
+
// write base64 data in hex data
|
|
102
|
+
await this.adapter.setObjectNotExistsAsync(`${startDirectory}.hex`,{
|
|
103
|
+
type: "state",
|
|
104
|
+
common: {
|
|
105
|
+
name: "last recieved data as hex",
|
|
106
|
+
type: "string",
|
|
107
|
+
role: "value",
|
|
108
|
+
read: true,
|
|
109
|
+
write: false
|
|
110
|
+
},
|
|
111
|
+
native: {},
|
|
112
|
+
});
|
|
113
|
+
const hexdata = Buffer.from(message.uplink_message.frm_payload, "base64").toString("hex");
|
|
114
|
+
await this.adapter.setStateAsync(`${startDirectory}.hex`,hexdata,true);
|
|
115
|
+
|
|
116
|
+
// write base64 data in string data
|
|
117
|
+
await this.adapter.setObjectNotExistsAsync(`${startDirectory}.string`,{
|
|
118
|
+
type: "state",
|
|
119
|
+
common: {
|
|
120
|
+
name: "last recieved data as string",
|
|
121
|
+
type: "string",
|
|
122
|
+
role: "value",
|
|
123
|
+
read: true,
|
|
124
|
+
write: false
|
|
125
|
+
},
|
|
126
|
+
native: {},
|
|
127
|
+
});
|
|
128
|
+
const stringdata = Buffer.from(message.uplink_message.frm_payload, "base64").toString();
|
|
129
|
+
await this.adapter.setStateAsync(`${startDirectory}.string`,stringdata,true);
|
|
130
|
+
}
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
/*********************************************************************
|
|
134
|
+
* ********************** decoded payload ****************************
|
|
135
|
+
* ******************************************************************/
|
|
136
|
+
|
|
137
|
+
if(this.directoryhandler.reachableDirectories[deviceStartdirectory][this.directoryhandler.safeableDirectories.uplinkDecoded]){
|
|
138
|
+
const startDirectory = this.directoryhandler.reachableDirectories[deviceStartdirectory][this.directoryhandler.safeableDirectories.uplinkDecoded];
|
|
139
|
+
await this.directoryhandler.generateRekursivObjects(message.uplink_message.decoded_payload,startDirectory,message);
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
/*********************************************************************
|
|
143
|
+
* ************************* remaining *******************************
|
|
144
|
+
* ******************************************************************/
|
|
145
|
+
|
|
146
|
+
if(this.directoryhandler.reachableDirectories[deviceStartdirectory][this.directoryhandler.safeableDirectories.uplinkRemaining]){
|
|
147
|
+
const startDirectory = this.directoryhandler.reachableDirectories[deviceStartdirectory][this.directoryhandler.safeableDirectories.uplinkRemaining];
|
|
148
|
+
// @ts-ignore
|
|
149
|
+
await this.directoryhandler.generateRekursivObjects(message.uplink_message,startDirectory,message,{ignoredElementNames:{decoded_payload:{},frm_payload:{}}});
|
|
150
|
+
}
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
/*********************************************************************
|
|
154
|
+
* ************************* Downlink data ***************************
|
|
155
|
+
* ******************************************************************/
|
|
156
|
+
|
|
157
|
+
// check for uplink message
|
|
158
|
+
if(message.downlink_queued || message.downlink_sent){
|
|
159
|
+
// Check wich downlink was recieved
|
|
160
|
+
let downlinkType = "downlink_queued";
|
|
161
|
+
if(message.downlink_sent){
|
|
162
|
+
downlinkType = "downlink_sent";
|
|
163
|
+
}
|
|
164
|
+
/*********************************************************************
|
|
165
|
+
* ************************ Rawdata json *****************************
|
|
166
|
+
* ******************************************************************/
|
|
167
|
+
|
|
168
|
+
if(this.directoryhandler.reachableDirectories[deviceStartdirectory][this.directoryhandler.safeableDirectories.downlinkRaw]){
|
|
169
|
+
const startDirectory = this.directoryhandler.reachableDirectories[deviceStartdirectory][this.directoryhandler.safeableDirectories.downlinkRaw];
|
|
170
|
+
// write json
|
|
171
|
+
await this.adapter.setObjectNotExistsAsync(`${startDirectory}.json`,{
|
|
172
|
+
type: "state",
|
|
173
|
+
common: {
|
|
174
|
+
name: "last recieved message",
|
|
175
|
+
type: "json",
|
|
176
|
+
role: "value",
|
|
177
|
+
read: true,
|
|
178
|
+
write: false
|
|
179
|
+
},
|
|
180
|
+
native: {},
|
|
181
|
+
});
|
|
182
|
+
await this.adapter.setStateAsync(`${startDirectory}.json`,JSON.stringify(message),true);
|
|
183
|
+
|
|
184
|
+
/*********************************************************************
|
|
185
|
+
* ********************** Rawdata (Base64) ***************************
|
|
186
|
+
* ******************************************************************/
|
|
187
|
+
|
|
188
|
+
// check for frm payload
|
|
189
|
+
if(message[downlinkType].frm_payload){
|
|
190
|
+
// wite base64 data
|
|
191
|
+
await this.adapter.setObjectNotExistsAsync(`${startDirectory}.base64`,{
|
|
192
|
+
type: "state",
|
|
193
|
+
common: {
|
|
194
|
+
name: "last recieved data as base64",
|
|
195
|
+
type: "string",
|
|
196
|
+
role: "value",
|
|
197
|
+
read: true,
|
|
198
|
+
write: false
|
|
199
|
+
},
|
|
200
|
+
native: {},
|
|
201
|
+
});
|
|
202
|
+
const writedata = message[downlinkType].frm_payload;
|
|
203
|
+
await this.adapter.setStateAsync(`${startDirectory}.base64`,writedata,true);
|
|
204
|
+
|
|
205
|
+
// write base64 data in hex data
|
|
206
|
+
await this.adapter.setObjectNotExistsAsync(`${startDirectory}.hex`,{
|
|
207
|
+
type: "state",
|
|
208
|
+
common: {
|
|
209
|
+
name: "last recieved data as hex",
|
|
210
|
+
type: "string",
|
|
211
|
+
role: "value",
|
|
212
|
+
read: true,
|
|
213
|
+
write: false
|
|
214
|
+
},
|
|
215
|
+
native: {},
|
|
216
|
+
});
|
|
217
|
+
const hexdata = Buffer.from(message[downlinkType].frm_payload,"base64").toString("hex");
|
|
218
|
+
await this.adapter.setStateAsync(`${startDirectory}.hex`,hexdata,true);
|
|
219
|
+
|
|
220
|
+
// write base64 data in string data
|
|
221
|
+
await this.adapter.setObjectNotExistsAsync(`${startDirectory}.string`,{
|
|
222
|
+
type: "state",
|
|
223
|
+
common: {
|
|
224
|
+
name: "last recieved data as string",
|
|
225
|
+
type: "string",
|
|
226
|
+
role: "value",
|
|
227
|
+
read: true,
|
|
228
|
+
write: false
|
|
229
|
+
},
|
|
230
|
+
native: {},
|
|
231
|
+
});
|
|
232
|
+
const stringdata = Buffer.from(message[downlinkType].frm_payload,"base64").toString();
|
|
233
|
+
await this.adapter.setStateAsync(`${startDirectory}.string`,stringdata,true);
|
|
234
|
+
}
|
|
235
|
+
}
|
|
236
|
+
|
|
237
|
+
/*********************************************************************
|
|
238
|
+
* ************************* remaining *******************************
|
|
239
|
+
* ******************************************************************/
|
|
240
|
+
|
|
241
|
+
if(this.directoryhandler.reachableDirectories[deviceStartdirectory][this.directoryhandler.safeableDirectories.downlinkRemaining]){
|
|
242
|
+
const startDirectory = this.directoryhandler.reachableDirectories[deviceStartdirectory][this.directoryhandler.safeableDirectories.downlinkRemaining];
|
|
243
|
+
// @ts-ignore
|
|
244
|
+
await this.directoryhandler.generateRekursivObjects(message[downlinkType],startDirectory,message,{ignoredElementNames:{frm_payload:{}}});
|
|
245
|
+
}
|
|
246
|
+
}
|
|
247
|
+
}
|
|
248
|
+
catch(error){
|
|
249
|
+
this.adapter.log.warn("check: " + error);
|
|
250
|
+
this.adapter.log.warn("Message: " + JSON.stringify(message));
|
|
251
|
+
}
|
|
252
|
+
}
|
|
253
|
+
|
|
254
|
+
|
|
255
|
+
/*********************************************************************
|
|
256
|
+
* *********************** Downlinktopic *****************************
|
|
257
|
+
* ******************************************************************/
|
|
258
|
+
|
|
259
|
+
getDownlinkTopic(id,suffix){
|
|
260
|
+
// Select datahandling in case of origin
|
|
261
|
+
if(this.adapter.config.ttn){
|
|
262
|
+
return this.getTtnDownlinkTopicFromDirektory(id,suffix);
|
|
263
|
+
}
|
|
264
|
+
else if(this.adapter.config.chirpstack){
|
|
265
|
+
// this.handleChirpstack(topic,message);
|
|
266
|
+
}
|
|
267
|
+
}
|
|
268
|
+
|
|
269
|
+
getTtnDownlinkTopicFromDirektory(changeInfo,suffix){
|
|
270
|
+
const topicElements = {
|
|
271
|
+
Version : "v3",
|
|
272
|
+
applicationId : `/${changeInfo.applicationId}`,
|
|
273
|
+
applicationFrom : "@ttn",
|
|
274
|
+
devices : `/devices`,
|
|
275
|
+
dev_uid : `/${changeInfo.dev_uid}`,
|
|
276
|
+
suffix : suffix
|
|
277
|
+
};
|
|
278
|
+
let downlink = "";
|
|
279
|
+
for(const stringelement of Object.values(topicElements)){
|
|
280
|
+
downlink += stringelement;
|
|
281
|
+
}
|
|
282
|
+
return downlink;
|
|
283
|
+
}
|
|
284
|
+
|
|
285
|
+
/*****************************************************************************************************************
|
|
286
|
+
* ***************************************************************************************************************
|
|
287
|
+
* **************************************************************************************************************/
|
|
288
|
+
|
|
289
|
+
}
|
|
290
|
+
|
|
291
|
+
module.exports = messagehandlerClass;
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
const mqtt = require("mqtt");
|
|
2
|
+
|
|
3
|
+
class mqttClientClass {
|
|
4
|
+
constructor(adapter, settings) {
|
|
5
|
+
this.adapter = adapter;
|
|
6
|
+
this.mqttprefix = settings.ssl ? "mqtts://" : "mqtt://";
|
|
7
|
+
this.client = mqtt.connect(`${this.mqttprefix}${settings.ipUrl}`, {
|
|
8
|
+
port: settings.port,
|
|
9
|
+
username: settings.username,
|
|
10
|
+
password: settings.password,
|
|
11
|
+
clientId: `iobroker_${this.adapter.namespace}`,
|
|
12
|
+
});
|
|
13
|
+
this.client.on("connect", () => {
|
|
14
|
+
this.adapter.log.info(`Connection is active.`);
|
|
15
|
+
this.adapter.setState("info.connection", true, true);
|
|
16
|
+
this.client.subscribe("#", (err) => {
|
|
17
|
+
if (err) {
|
|
18
|
+
this.adapter.log.error(`On subscribe: ${err}`);
|
|
19
|
+
}
|
|
20
|
+
});
|
|
21
|
+
});
|
|
22
|
+
this.client.on("disconnect", () => {
|
|
23
|
+
this.adapter.setState("info.connection", false, true);
|
|
24
|
+
this.adapter.log.debug(`disconnected`);
|
|
25
|
+
});
|
|
26
|
+
this.client.on("error", (err) => {
|
|
27
|
+
this.adapter.log.error(`${err}`);
|
|
28
|
+
});
|
|
29
|
+
|
|
30
|
+
this.client.on("close", () => {
|
|
31
|
+
this.adapter.setState("info.connection", false, true);
|
|
32
|
+
this.adapter.log.info(`Connection is closed.`);
|
|
33
|
+
});
|
|
34
|
+
|
|
35
|
+
this.client.on("message", async (topic, message) => {
|
|
36
|
+
// @ts-ignore
|
|
37
|
+
message = JSON.parse(message);
|
|
38
|
+
|
|
39
|
+
await this.adapter.messagehandler.handleMessage(topic, message);
|
|
40
|
+
});
|
|
41
|
+
}
|
|
42
|
+
async publish(topic, message, opt) {
|
|
43
|
+
this.adapter.log.debug(`Publishing topic: ${topic} with message: ${message}.`);
|
|
44
|
+
await this.client.publishAsync(topic, message, opt);
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
destroy(){
|
|
48
|
+
this.client.end();
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
module.exports = mqttClientClass;
|