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 +3 -0
- package/admin/jsonConfig.json +231 -83
- package/io-package.json +16 -2
- package/lib/modules/directorieshandler.js +15 -37
- package/lib/modules/downlinkConfig.js +60 -0
- package/lib/modules/messagehandler.js +121 -3
- package/main.js +180 -70
- package/package.json +1 -1
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
|
|
package/admin/jsonConfig.json
CHANGED
|
@@ -1,89 +1,237 @@
|
|
|
1
1
|
{
|
|
2
2
|
"i18n": true,
|
|
3
|
-
"type": "
|
|
4
|
-
"items":
|
|
5
|
-
"
|
|
6
|
-
"
|
|
7
|
-
"
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
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
|
-
"
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
"
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
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.
|
|
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
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
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
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
//
|
|
120
|
-
|
|
121
|
-
|
|
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
|
-
|
|
126
|
-
|
|
127
|
-
//
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
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
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
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
|
-
}
|
|
140
|
-
|
|
141
|
-
this.log.
|
|
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
|
-
|
|
151
|
-
|
|
152
|
-
|
|
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
|
-
|
|
155
|
-
|
|
249
|
+
catch(error){
|
|
250
|
+
this.log.error(`error at ${activeFunction}: ` + error);
|
|
156
251
|
}
|
|
157
252
|
}
|
|
158
253
|
|
|
159
|
-
getTtnChangeInfo(id){
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
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){
|