node-red-contrib-lorawan-bacnet-server 1.2.0
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.
Potentially problematic release.
This version of node-red-contrib-lorawan-bacnet-server might be problematic. Click here for more details.
- package/.gitattributes +2 -0
- package/CONTRIBUTING.md +64 -0
- package/LICENSE +21 -0
- package/README.md +111 -0
- package/examples/BACnet-Server.json +401 -0
- package/examples/LoRaBAC.json +1152 -0
- package/images/BACnetObjectListExample.png +0 -0
- package/images/controllerSetpointExample3.png +0 -0
- package/images/deviceListExample.png +0 -0
- package/images/deviceListExample3.png +0 -0
- package/images/lorabac.png +0 -0
- package/images/objectConfigurationExample2.png +0 -0
- package/images/objectListExample2.png +0 -0
- package/images/objectListExample3.png +0 -0
- package/images/valveSetpointExample3.png +0 -0
- package/images/valveTemperatureExample3.png +0 -0
- package/nodes/bacnet-point/bacnet-point.html +403 -0
- package/nodes/bacnet-point/bacnet-point.js +293 -0
- package/nodes/bacnet-server/bacnet-server.html +138 -0
- package/nodes/bacnet-server/bacnet-server.js +817 -0
- package/nodes/lorabac/lorabac.html +1588 -0
- package/nodes/lorabac/lorabac.js +652 -0
- package/package.json +39 -0
|
@@ -0,0 +1,1152 @@
|
|
|
1
|
+
[
|
|
2
|
+
{
|
|
3
|
+
"id": "d5185b34c44ae683",
|
|
4
|
+
"type": "tab",
|
|
5
|
+
"label": "LoRaBAC",
|
|
6
|
+
"disabled": false,
|
|
7
|
+
"info": "",
|
|
8
|
+
"env": []
|
|
9
|
+
},
|
|
10
|
+
{
|
|
11
|
+
"id": "e5621a7de149225c",
|
|
12
|
+
"type": "subflow",
|
|
13
|
+
"name": "Rest API Read downlink/ Write uplink",
|
|
14
|
+
"info": "",
|
|
15
|
+
"category": "",
|
|
16
|
+
"in": [
|
|
17
|
+
{
|
|
18
|
+
"x": 300,
|
|
19
|
+
"y": 360,
|
|
20
|
+
"wires": [
|
|
21
|
+
{
|
|
22
|
+
"id": "8823ed2c61e64b0f"
|
|
23
|
+
},
|
|
24
|
+
{
|
|
25
|
+
"id": "580fb662e924a681"
|
|
26
|
+
}
|
|
27
|
+
]
|
|
28
|
+
}
|
|
29
|
+
],
|
|
30
|
+
"out": [
|
|
31
|
+
{
|
|
32
|
+
"x": 1540,
|
|
33
|
+
"y": 460,
|
|
34
|
+
"wires": [
|
|
35
|
+
{
|
|
36
|
+
"id": "f8a6a26c3a4274bf",
|
|
37
|
+
"port": 0
|
|
38
|
+
}
|
|
39
|
+
]
|
|
40
|
+
}
|
|
41
|
+
],
|
|
42
|
+
"env": [
|
|
43
|
+
{
|
|
44
|
+
"name": "dataDirection",
|
|
45
|
+
"type": "str",
|
|
46
|
+
"value": "uplink",
|
|
47
|
+
"ui": {
|
|
48
|
+
"icon": "font-awesome/fa-location-arrow",
|
|
49
|
+
"label": {
|
|
50
|
+
"en-US": "Direction"
|
|
51
|
+
},
|
|
52
|
+
"type": "select",
|
|
53
|
+
"opts": {
|
|
54
|
+
"opts": [
|
|
55
|
+
{
|
|
56
|
+
"l": {
|
|
57
|
+
"en-US": "Uplink"
|
|
58
|
+
},
|
|
59
|
+
"v": "uplink"
|
|
60
|
+
},
|
|
61
|
+
{
|
|
62
|
+
"l": {
|
|
63
|
+
"en-US": "Downlink"
|
|
64
|
+
},
|
|
65
|
+
"v": "downlink"
|
|
66
|
+
}
|
|
67
|
+
]
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
}
|
|
71
|
+
],
|
|
72
|
+
"meta": {},
|
|
73
|
+
"color": "#DDAA99",
|
|
74
|
+
"status": {
|
|
75
|
+
"x": 660,
|
|
76
|
+
"y": 580,
|
|
77
|
+
"wires": [
|
|
78
|
+
{
|
|
79
|
+
"id": "c5511cd0867d06e2",
|
|
80
|
+
"port": 0
|
|
81
|
+
}
|
|
82
|
+
]
|
|
83
|
+
}
|
|
84
|
+
},
|
|
85
|
+
{
|
|
86
|
+
"id": "b3c9c070e14e7188",
|
|
87
|
+
"type": "group",
|
|
88
|
+
"z": "e5621a7de149225c",
|
|
89
|
+
"name": "Read/Write BACnet Objects",
|
|
90
|
+
"style": {
|
|
91
|
+
"stroke": "#ff0000",
|
|
92
|
+
"fill": "#ffbfbf",
|
|
93
|
+
"label": true,
|
|
94
|
+
"color": "#000000"
|
|
95
|
+
},
|
|
96
|
+
"nodes": [
|
|
97
|
+
"884f11face673e2c",
|
|
98
|
+
"8823ed2c61e64b0f"
|
|
99
|
+
],
|
|
100
|
+
"x": 394,
|
|
101
|
+
"y": 319,
|
|
102
|
+
"w": 492,
|
|
103
|
+
"h": 82
|
|
104
|
+
},
|
|
105
|
+
{
|
|
106
|
+
"id": "58e5886e40ebacd1",
|
|
107
|
+
"type": "group",
|
|
108
|
+
"z": "e5621a7de149225c",
|
|
109
|
+
"name": "Create missing BACnet Objects",
|
|
110
|
+
"style": {
|
|
111
|
+
"stroke": "#ff0000",
|
|
112
|
+
"fill": "#ffbfbf",
|
|
113
|
+
"label": true,
|
|
114
|
+
"color": "#000000"
|
|
115
|
+
},
|
|
116
|
+
"nodes": [
|
|
117
|
+
"8e36bdc54932c785",
|
|
118
|
+
"4671414315b782ca",
|
|
119
|
+
"11c02edca91f42f3",
|
|
120
|
+
"0a876338e2be6dd8",
|
|
121
|
+
"5e76761149f4280e",
|
|
122
|
+
"d04fc338048162ec",
|
|
123
|
+
"d7fb76702f1ebdab"
|
|
124
|
+
],
|
|
125
|
+
"x": 1154,
|
|
126
|
+
"y": 219,
|
|
127
|
+
"w": 762,
|
|
128
|
+
"h": 142
|
|
129
|
+
},
|
|
130
|
+
{
|
|
131
|
+
"id": "cfdfc86845dc9bb9",
|
|
132
|
+
"type": "group",
|
|
133
|
+
"z": "d5185b34c44ae683",
|
|
134
|
+
"name": "Uplink",
|
|
135
|
+
"style": {
|
|
136
|
+
"label": true,
|
|
137
|
+
"color": "#000000",
|
|
138
|
+
"fill": "#e3f3d3"
|
|
139
|
+
},
|
|
140
|
+
"nodes": [
|
|
141
|
+
"6004507d410bec08",
|
|
142
|
+
"5338fce50ecc57d9",
|
|
143
|
+
"cfaebde9dbc1901d",
|
|
144
|
+
"2694240cdee88a83"
|
|
145
|
+
],
|
|
146
|
+
"x": 14,
|
|
147
|
+
"y": 219,
|
|
148
|
+
"w": 612,
|
|
149
|
+
"h": 162
|
|
150
|
+
},
|
|
151
|
+
{
|
|
152
|
+
"id": "90c82936d1f21eda",
|
|
153
|
+
"type": "group",
|
|
154
|
+
"z": "d5185b34c44ae683",
|
|
155
|
+
"name": "Downlink",
|
|
156
|
+
"style": {
|
|
157
|
+
"fill": "#e3f3d3",
|
|
158
|
+
"label": true,
|
|
159
|
+
"color": "#000000"
|
|
160
|
+
},
|
|
161
|
+
"nodes": [
|
|
162
|
+
"4fbd348dfae7d9ce",
|
|
163
|
+
"8ef7bc0b865b1230",
|
|
164
|
+
"a6ff13a9089b4272",
|
|
165
|
+
"adb0c33fcb917696"
|
|
166
|
+
],
|
|
167
|
+
"x": 634,
|
|
168
|
+
"y": 219,
|
|
169
|
+
"w": 712,
|
|
170
|
+
"h": 162
|
|
171
|
+
},
|
|
172
|
+
{
|
|
173
|
+
"id": "c5511cd0867d06e2",
|
|
174
|
+
"type": "junction",
|
|
175
|
+
"z": "e5621a7de149225c",
|
|
176
|
+
"x": 600,
|
|
177
|
+
"y": 580,
|
|
178
|
+
"wires": [
|
|
179
|
+
[]
|
|
180
|
+
]
|
|
181
|
+
},
|
|
182
|
+
{
|
|
183
|
+
"id": "f8a6a26c3a4274bf",
|
|
184
|
+
"type": "junction",
|
|
185
|
+
"z": "e5621a7de149225c",
|
|
186
|
+
"x": 1460,
|
|
187
|
+
"y": 460,
|
|
188
|
+
"wires": [
|
|
189
|
+
[
|
|
190
|
+
"a1b40b26fe6dd41f"
|
|
191
|
+
]
|
|
192
|
+
]
|
|
193
|
+
},
|
|
194
|
+
{
|
|
195
|
+
"id": "914dac3cf2194eb6",
|
|
196
|
+
"type": "mqtt-broker",
|
|
197
|
+
"name": "The Things Network",
|
|
198
|
+
"broker": "eu1.cloud.thethings.network",
|
|
199
|
+
"port": 1883,
|
|
200
|
+
"clientid": "",
|
|
201
|
+
"autoConnect": true,
|
|
202
|
+
"usetls": false,
|
|
203
|
+
"protocolVersion": 4,
|
|
204
|
+
"keepalive": 60,
|
|
205
|
+
"cleansession": true,
|
|
206
|
+
"autoUnsubscribe": true,
|
|
207
|
+
"birthTopic": "",
|
|
208
|
+
"birthQos": "0",
|
|
209
|
+
"birthRetain": "false",
|
|
210
|
+
"birthPayload": "",
|
|
211
|
+
"birthMsg": {},
|
|
212
|
+
"closeTopic": "",
|
|
213
|
+
"closeQos": "0",
|
|
214
|
+
"closeRetain": "false",
|
|
215
|
+
"closePayload": "",
|
|
216
|
+
"closeMsg": {},
|
|
217
|
+
"willTopic": "",
|
|
218
|
+
"willQos": "0",
|
|
219
|
+
"willRetain": "false",
|
|
220
|
+
"willPayload": "",
|
|
221
|
+
"willMsg": {},
|
|
222
|
+
"userProps": "",
|
|
223
|
+
"sessionExpiry": ""
|
|
224
|
+
},
|
|
225
|
+
{
|
|
226
|
+
"id": "74dfa9d48cf1b5c8",
|
|
227
|
+
"type": "mqtt-broker",
|
|
228
|
+
"name": "mosquitto local (docker)",
|
|
229
|
+
"broker": "mosquitto",
|
|
230
|
+
"port": 1883,
|
|
231
|
+
"clientid": "",
|
|
232
|
+
"autoConnect": true,
|
|
233
|
+
"usetls": false,
|
|
234
|
+
"protocolVersion": 4,
|
|
235
|
+
"keepalive": 60,
|
|
236
|
+
"cleansession": true,
|
|
237
|
+
"autoUnsubscribe": true,
|
|
238
|
+
"birthTopic": "",
|
|
239
|
+
"birthQos": "0",
|
|
240
|
+
"birthRetain": "false",
|
|
241
|
+
"birthPayload": "",
|
|
242
|
+
"birthMsg": {},
|
|
243
|
+
"closeTopic": "",
|
|
244
|
+
"closeQos": "0",
|
|
245
|
+
"closeRetain": "false",
|
|
246
|
+
"closePayload": "",
|
|
247
|
+
"closeMsg": {},
|
|
248
|
+
"willTopic": "",
|
|
249
|
+
"willQos": "0",
|
|
250
|
+
"willRetain": "false",
|
|
251
|
+
"willPayload": "",
|
|
252
|
+
"willMsg": {},
|
|
253
|
+
"userProps": "",
|
|
254
|
+
"sessionExpiry": ""
|
|
255
|
+
},
|
|
256
|
+
{
|
|
257
|
+
"id": "5f754ddd5c5bce24",
|
|
258
|
+
"type": "mqtt-broker",
|
|
259
|
+
"name": "The Things Stack",
|
|
260
|
+
"broker": "stack",
|
|
261
|
+
"port": "1883",
|
|
262
|
+
"tls": "",
|
|
263
|
+
"clientid": "",
|
|
264
|
+
"autoConnect": true,
|
|
265
|
+
"usetls": false,
|
|
266
|
+
"protocolVersion": "4",
|
|
267
|
+
"keepalive": "60",
|
|
268
|
+
"cleansession": true,
|
|
269
|
+
"autoUnsubscribe": true,
|
|
270
|
+
"birthTopic": "",
|
|
271
|
+
"birthQos": "0",
|
|
272
|
+
"birthRetain": "false",
|
|
273
|
+
"birthPayload": "",
|
|
274
|
+
"birthMsg": {},
|
|
275
|
+
"closeTopic": "",
|
|
276
|
+
"closeQos": "0",
|
|
277
|
+
"closeRetain": "false",
|
|
278
|
+
"closePayload": "",
|
|
279
|
+
"closeMsg": {},
|
|
280
|
+
"willTopic": "",
|
|
281
|
+
"willQos": "0",
|
|
282
|
+
"willRetain": "false",
|
|
283
|
+
"willPayload": "",
|
|
284
|
+
"willMsg": {},
|
|
285
|
+
"userProps": "",
|
|
286
|
+
"sessionExpiry": ""
|
|
287
|
+
},
|
|
288
|
+
{
|
|
289
|
+
"id": "2418490b3512393e",
|
|
290
|
+
"type": "tls-config",
|
|
291
|
+
"name": "",
|
|
292
|
+
"cert": "",
|
|
293
|
+
"key": "",
|
|
294
|
+
"ca": "",
|
|
295
|
+
"certname": "",
|
|
296
|
+
"keyname": "",
|
|
297
|
+
"caname": "",
|
|
298
|
+
"servername": "",
|
|
299
|
+
"verifyservercert": false,
|
|
300
|
+
"alpnprotocol": ""
|
|
301
|
+
},
|
|
302
|
+
{
|
|
303
|
+
"id": "d6dd3de173c23998",
|
|
304
|
+
"type": "mqtt-broker",
|
|
305
|
+
"name": "Chirpstack-Cloud",
|
|
306
|
+
"broker": "chirpstack.univ-lorawan.fr",
|
|
307
|
+
"port": "8883",
|
|
308
|
+
"tls": "2418490b3512393e",
|
|
309
|
+
"clientid": "",
|
|
310
|
+
"autoConnect": true,
|
|
311
|
+
"usetls": true,
|
|
312
|
+
"protocolVersion": "4",
|
|
313
|
+
"keepalive": "60",
|
|
314
|
+
"cleansession": true,
|
|
315
|
+
"autoUnsubscribe": true,
|
|
316
|
+
"birthTopic": "",
|
|
317
|
+
"birthQos": "0",
|
|
318
|
+
"birthRetain": "false",
|
|
319
|
+
"birthPayload": "",
|
|
320
|
+
"birthMsg": {},
|
|
321
|
+
"closeTopic": "",
|
|
322
|
+
"closeQos": "0",
|
|
323
|
+
"closeRetain": "false",
|
|
324
|
+
"closePayload": "",
|
|
325
|
+
"closeMsg": {},
|
|
326
|
+
"willTopic": "",
|
|
327
|
+
"willQos": "0",
|
|
328
|
+
"willRetain": "false",
|
|
329
|
+
"willPayload": "",
|
|
330
|
+
"willMsg": {},
|
|
331
|
+
"userProps": "",
|
|
332
|
+
"sessionExpiry": ""
|
|
333
|
+
},
|
|
334
|
+
{
|
|
335
|
+
"id": "fa7f1bf4647d5456",
|
|
336
|
+
"type": "function",
|
|
337
|
+
"z": "e5621a7de149225c",
|
|
338
|
+
"name": "BACnet Object Exist ?",
|
|
339
|
+
"func": "let device = msg.device;\nconst debug = flow.get(\"$parent.g_debug\");\n\nswitch (msg.statusCode) {\n ////////////////////////////////////////////////// \n // Case 200 : \"Success\" > Stops here OR continue to read Downlink Objects. \n // Case 200 : \"Object does not exist\" > Create Objects\n //////////////////////////////////////////////////\n case 200:\n if (msg.payload.includes(\"Unknown Object\")) {\n debug(device, \"creation\", `${device.identity.deviceName} : Some BACnet objects don't exist`);\n return [{ device: device }, null]; // Create Downlink Objects\n }\n else {\n switch (env.get(\"dataDirection\")) {\n case \"uplink\":\n debug(device, \"up\", `${device.identity.deviceName} (RestAPI) : Write Uplink Objects`);\n\n const dataDirection = Object.values(device.bacnet.objects).map(obj => obj.dataDirection);\n\n if (dataDirection.some(direction => { return direction === \"downlink\" })) {\n return [null, { device: device }]; // Continue to read downlink Objects\n }\n else {\n debug(device, \"txTime\", `${device.identity.deviceName} (${device.controller.protocol}) : TX time = ${Date.now() - device.transmitTime} ms`);\n return [null, null]; // Stops here\n }\n break;\n case \"downlink\":\n debug(device, \"down\", `${device.identity.deviceName} (RestAPI) : Read Downlink Objects`);\n return [null, { device: device, payload: JSON.parse(msg.payload) }];\n break;\n default:\n \n }\n \n }\n\n case 400:\n node.error(\"Error : Bad HTTP Request\");\n if (msg.payload.includes(\"write-access-denied\")) {\n node.error(\"Error : Trying to write a Read Only object (analogInput)\");\n }\n return [null, null];\n\n case 401:\n node.error(\"Error : Can't connect to controller : Authorization error\");\n return [null, null];\n\n case 500:\n node.error(\"Error : Server Error 500\");\n return [null, null];\n\n case 404:\n node.error(\"Error : 404\");\n return [null, null];\n\n case \"ETIMEDOUT\":\n node.error(\"Error : Can't connect to controller : TimeOut\");\n return [null, null];\n\n case \"UNABLE_TO_VERIFY_LEAF_SIGNATURE\":\n node.error(\"Error : You forgot to enable the TLS config in your HTTP node\");\n return [null, null];\n\n}\n",
|
|
340
|
+
"outputs": 2,
|
|
341
|
+
"timeout": 0,
|
|
342
|
+
"noerr": 0,
|
|
343
|
+
"initialize": "",
|
|
344
|
+
"finalize": "",
|
|
345
|
+
"libs": [],
|
|
346
|
+
"x": 1000,
|
|
347
|
+
"y": 360,
|
|
348
|
+
"wires": [
|
|
349
|
+
[
|
|
350
|
+
"8e36bdc54932c785",
|
|
351
|
+
"780a20b5b444c3d7"
|
|
352
|
+
],
|
|
353
|
+
[
|
|
354
|
+
"8118aa09800ae8ee"
|
|
355
|
+
]
|
|
356
|
+
],
|
|
357
|
+
"icon": "node-red/switch.svg"
|
|
358
|
+
},
|
|
359
|
+
{
|
|
360
|
+
"id": "884f11face673e2c",
|
|
361
|
+
"type": "http request",
|
|
362
|
+
"z": "e5621a7de149225c",
|
|
363
|
+
"g": "b3c9c070e14e7188",
|
|
364
|
+
"name": "HTTP REQUEST",
|
|
365
|
+
"method": "use",
|
|
366
|
+
"ret": "txt",
|
|
367
|
+
"paytoqs": "ignore",
|
|
368
|
+
"url": "",
|
|
369
|
+
"tls": "2418490b3512393e",
|
|
370
|
+
"persist": false,
|
|
371
|
+
"proxy": "",
|
|
372
|
+
"insecureHTTPParser": false,
|
|
373
|
+
"authType": "",
|
|
374
|
+
"senderr": false,
|
|
375
|
+
"headers": [],
|
|
376
|
+
"x": 770,
|
|
377
|
+
"y": 360,
|
|
378
|
+
"wires": [
|
|
379
|
+
[
|
|
380
|
+
"fa7f1bf4647d5456"
|
|
381
|
+
]
|
|
382
|
+
]
|
|
383
|
+
},
|
|
384
|
+
{
|
|
385
|
+
"id": "8823ed2c61e64b0f",
|
|
386
|
+
"type": "function",
|
|
387
|
+
"z": "e5621a7de149225c",
|
|
388
|
+
"g": "b3c9c070e14e7188",
|
|
389
|
+
"name": "READ/WRITE Objects",
|
|
390
|
+
"func": "\nlet device = msg.device;\nlet dataDirection = env.get('dataDirection')\nlet bacnetObjects = device?.bacnet?.objects;\n\nif(device?.controller?.protocol != \"restAPIBacnet\") return null;\n\nswitch (device?.controller?.model) {\n\n ///////////////////////////////////////////////////////////\n ////// Distech Controls Eclypse APEX\n ////// https://www.postman.com/distech/distech-ecy-v2-public/request/3qk28wy/write-property-multiple\n ///////////////////////////////////////////////////////////\n case \"distechControlsV2\":\n /********* HTTP Request Write Properties\n {\n \"method\": \"POST\",\n \"url\": \"https://@IP/api/rest/v2/services/bacnet/local/objects/write-property-multiple\",\n \"headers\": {Authorization: httpAuthentication,\n ContentType: \"application/json\"},\n \n \"payload\": {\n \"encode\": \"text\",\n \"property-references\": [\n {\n \"type\": \"analogValue\",\n \"instance\": y,\n \"property\": \"presentValue\",\n \"value\" : \"xx\"\n },\n {\n \"type\": \"analogValue\",\n \"instance\": y,\n \"property\": \"presentValue\",\n \"value\" : \"xx\"\n },\n ...\n ]\n },\n \"requestTimeout\" : xxx (ms)\n }\n */\n\n let property_references = [];\n for (let object in bacnetObjects) {\n if (bacnetObjects[object].dataDirection == dataDirection) {\n let temp = {}\n switch (dataDirection) {\n case \"uplink\":\n temp = '{ \"type\": \"' + bacnetObjects[object].objectType + '\", \"instance\": ' + bacnetObjects[object].instanceNum + ', \"property\": \"presentValue\", \"value\": ' + bacnetObjects[object].value + ' }';\n property_references.push(JSON.parse(temp));\n break;\n case \"downlink\":\n temp = '{ \"type\": \"' + bacnetObjects[object].objectType + '\", \"instance\": ' + bacnetObjects[object].instanceNum + ', \"property\": \"presentValue\"}';\n property_references.push(JSON.parse(temp));\n break;\n default:\n \n }\n }\n }\n\n // Return HTTP Request\n switch (dataDirection) {\n case \"uplink\":\n return {\n \"method\": \"POST\",\n \"url\": \"https://\" + device.controller.ipAddress + \"/api/rest/v2/services/bacnet/local/objects/write-property-multiple\",\n \"headers\": {\n Authorization: device.controller.httpAuthentication,\n ContentType: \"application/json\"\n },\n \"payload\": {\n \"encode\": \"text\",\n \"property-references\": property_references\n },\n \"requestTimeout\": flow.get('$parent.g_httpRequestTimeOut'),\n \"device\": device\n }\n break;\n case \"downlink\":\n return {\n \"method\": \"POST\",\n \"url\": \"https://\" + device.controller.ipAddress + \"/api/rest/v2/services/bacnet/local/objects/read-property-multiple\",\n \"headers\": {\n Authorization: device.controller.httpAuthentication,\n ContentType: \"application/json\"\n },\n \"payload\": {\n \"encode\": \"text\",\n \"property-references\": property_references\n },\n \"requestTimeout\": flow.get('$parent.g_httpRequestTimeOut'),\n \"device\": device\n }\n break;\n default:\n return\n break;\n }\n \n\n\n ///////////////////////////////////////////////////////////\n ////// XXXXX Controller\n ////// URL to the API documentation\n ///////////////////////////////////////////////////////////\n case \"anotherController\":\n\n break;\n default:\n \n return null;\n break;\n}\n\n\n\n",
|
|
391
|
+
"outputs": 1,
|
|
392
|
+
"timeout": 0,
|
|
393
|
+
"noerr": 0,
|
|
394
|
+
"initialize": "",
|
|
395
|
+
"finalize": "",
|
|
396
|
+
"libs": [],
|
|
397
|
+
"x": 520,
|
|
398
|
+
"y": 360,
|
|
399
|
+
"wires": [
|
|
400
|
+
[
|
|
401
|
+
"884f11face673e2c"
|
|
402
|
+
]
|
|
403
|
+
]
|
|
404
|
+
},
|
|
405
|
+
{
|
|
406
|
+
"id": "8e36bdc54932c785",
|
|
407
|
+
"type": "function",
|
|
408
|
+
"z": "e5621a7de149225c",
|
|
409
|
+
"g": "58e5886e40ebacd1",
|
|
410
|
+
"name": "CREATE Objects",
|
|
411
|
+
"func": "\nlet device = msg.device;\nlet bacnetObjects = device.bacnet.objects;\n\nswitch (device.controller.model) {\n\n ///////////////////////////////////////////////////////////\n ////// Distech Controls Eclypse APEX\n ////// https://www.postman.com/distech/distech-ecy-v2-public/request/57jbx8w/create-objects-multiple\n ///////////////////////////////////////////////////////////\n case \"distechControlsV2\":\n\n /********** Objects creation on the controller\n {\n \"method\": \"POST\",\n \"url\": \"https://\" + flow.get('$parent.g_controllerIP') +\"/api/rest/v2/batch\",\n \"headers\": {Authorization: flow.get('$parent.g_httpAuthentication'),\n ContentType: \"application/json\"}\n \"payload\":{\n \"requests\": [\n {\n \"id\": \"1\",\n \"method\": \"POST\",\n \"url\": \"/api/rest/v2/services/bacnet/local/objects/add\",\n \"body\": {\n \"object-type\": \"AnalogValue\",\n \"instance-number\": 10010,\n \"name\": \"apiAVTest10\"\n }\n },\n {\n \"id\": \"2\",\n \"method\": \"POST\",\n \"url\": \"/api/rest/v2/services/bacnet/local/objects/add\",\n \"body\": {\n \"object-type\": \"BinaryValue\",\n \"instance-number\": 10010,\n \"name\": \"apiBVTest10\"\n }\n },\n ...\n ]\n },\n \"requestTimeout\" : xxx (ms)\n }\n */\n\n\n let requests = [], i = 1;\n\n for (let object in bacnetObjects) {\n let temp = '{ \"id\": \"' + (i++) + '\", \"method\": \"POST\", \"url\": \"/api/rest/v2/services/bacnet/local/objects/add\", \"body\": { \"object-type\": \"' + bacnetObjects[object].objectType + '\", \"instance-number\": ' + bacnetObjects[object].instanceNum + ', \"name\": \"' + bacnetObjects[object].objectName + '\" } }';\n requests.push(JSON.parse(temp));\n }\n\n // Return HTTP Request\n return {\n \"method\": \"POST\",\n \"url\": \"https://\" + device.controller.ipAddress + \"/api/rest/v2/batch\",\n \"headers\": {\n Authorization: device.controller.httpAuthentication,\n ContentType: \"application/json\"\n },\n \"payload\": { \"requests\": requests },\n \"requestTimeout\": flow.get('$parent.g_httpRequestTimeOut'),\n \"device\": device\n }\n\n ///////////////////////////////////////////////////////////\n ////// XXXXX Controller\n ////// URL to the API documentation\n ///////////////////////////////////////////////////////////\n case \"anotherController\":\n\n \n}",
|
|
412
|
+
"outputs": 1,
|
|
413
|
+
"timeout": 0,
|
|
414
|
+
"noerr": 0,
|
|
415
|
+
"initialize": "",
|
|
416
|
+
"finalize": "",
|
|
417
|
+
"libs": [],
|
|
418
|
+
"x": 1270,
|
|
419
|
+
"y": 260,
|
|
420
|
+
"wires": [
|
|
421
|
+
[
|
|
422
|
+
"4671414315b782ca"
|
|
423
|
+
]
|
|
424
|
+
]
|
|
425
|
+
},
|
|
426
|
+
{
|
|
427
|
+
"id": "4671414315b782ca",
|
|
428
|
+
"type": "http request",
|
|
429
|
+
"z": "e5621a7de149225c",
|
|
430
|
+
"g": "58e5886e40ebacd1",
|
|
431
|
+
"name": "HTTP REQUEST",
|
|
432
|
+
"method": "use",
|
|
433
|
+
"ret": "txt",
|
|
434
|
+
"paytoqs": "ignore",
|
|
435
|
+
"url": "",
|
|
436
|
+
"tls": "2418490b3512393e",
|
|
437
|
+
"persist": false,
|
|
438
|
+
"proxy": "",
|
|
439
|
+
"insecureHTTPParser": false,
|
|
440
|
+
"authType": "",
|
|
441
|
+
"senderr": false,
|
|
442
|
+
"headers": [],
|
|
443
|
+
"x": 1550,
|
|
444
|
+
"y": 260,
|
|
445
|
+
"wires": [
|
|
446
|
+
[
|
|
447
|
+
"11c02edca91f42f3",
|
|
448
|
+
"5e76761149f4280e"
|
|
449
|
+
]
|
|
450
|
+
]
|
|
451
|
+
},
|
|
452
|
+
{
|
|
453
|
+
"id": "11c02edca91f42f3",
|
|
454
|
+
"type": "function",
|
|
455
|
+
"z": "e5621a7de149225c",
|
|
456
|
+
"g": "58e5886e40ebacd1",
|
|
457
|
+
"name": "READ/WRITE Objects",
|
|
458
|
+
"func": "\nlet device = msg.device;\nlet previousValues = flow.get(\"$parent.g_previousValues\");\nlet dataDirection = env.get('dataDirection')\nlet bacnetObjects = device.bacnet.objects;\n\nswitch (device.controller.model) {\n\n ///////////////////////////////////////////////////////////\n ////// Distech Controls Eclypse APEX\n ////// https://www.postman.com/distech/distech-ecy-v2-public/request/3qk28wy/write-property-multiple\n ///////////////////////////////////////////////////////////\n case \"distechControlsV2\":\n /********* HTTP Request Write Properties\n {\n \"method\": \"POST\",\n \"url\": \"https://@IP/api/rest/v2/services/bacnet/local/objects/write-property-multiple\",\n \"headers\": {Authorization: httpAuthentication,\n ContentType: \"application/json\"},\n \n \"payload\": {\n \"encode\": \"text\",\n \"property-references\": [\n {\n \"type\": \"analogValue\",\n \"instance\": y,\n \"property\": \"presentValue\",\n \"value\" : \"xx\"\n },\n {\n \"type\": \"analogValue\",\n \"instance\": y,\n \"property\": \"presentValue\",\n \"value\" : \"xx\"\n },\n ...\n ]\n },\n \"requestTimeout\" : xxx (ms)\n }\n */\n\n let property_references = [];\n for (let object in bacnetObjects) {\n let temp = {}\n switch (bacnetObjects[object].dataDirection) {\n case \"uplink\":\n temp = '{ \"type\": \"' + bacnetObjects[object].objectType + '\", \"instance\": ' + bacnetObjects[object].instanceNum + ', \"property\": \"presentValue\", \"value\": ' + bacnetObjects[object].value + ' }';\n property_references.push(JSON.parse(temp));\n break;\n case \"downlink\":\n // if it exist take the previous value of the downlink BACnet object\n if (previousValues.hasOwnProperty(device.identity.deviceName)){\n temp = '{ \"type\": \"' + bacnetObjects[object].objectType + '\", \"instance\": ' + bacnetObjects[object].instanceNum + ', \"property\": \"presentValue\", \"value\": ' + previousValues[device.identity.deviceName].bacnet.objects[object].value + ' }';\n property_references.push(JSON.parse(temp));\n }else{\n temp = '{ \"type\": \"' + bacnetObjects[object].objectType + '\", \"instance\": ' + bacnetObjects[object].instanceNum + ', \"property\": \"presentValue\", \"value\": ' + bacnetObjects[object].value + ' }';\n property_references.push(JSON.parse(temp));\n }\n\n break;\n default:\n }\n \n }\n\n // Return HTTP Request\n return {\n \"method\": \"POST\",\n \"url\": \"https://\" + device.controller.ipAddress + \"/api/rest/v2/services/bacnet/local/objects/write-property-multiple\",\n \"headers\": {\n Authorization: device.controller.httpAuthentication,\n ContentType: \"application/json\"\n },\n \"payload\": {\n \"encode\": \"text\",\n \"property-references\": property_references\n },\n \"requestTimeout\": global.get('g_httpRequestTimeOut'),\n \"device\": device\n }\n\n ///////////////////////////////////////////////////////////\n ////// XXXXX Controller\n ////// URL to the API documentation\n ///////////////////////////////////////////////////////////\n case \"anotherController\":\n\n\n}\n\n\n\n",
|
|
459
|
+
"outputs": 1,
|
|
460
|
+
"timeout": 0,
|
|
461
|
+
"noerr": 0,
|
|
462
|
+
"initialize": "",
|
|
463
|
+
"finalize": "",
|
|
464
|
+
"libs": [],
|
|
465
|
+
"x": 1280,
|
|
466
|
+
"y": 320,
|
|
467
|
+
"wires": [
|
|
468
|
+
[
|
|
469
|
+
"0a876338e2be6dd8"
|
|
470
|
+
]
|
|
471
|
+
]
|
|
472
|
+
},
|
|
473
|
+
{
|
|
474
|
+
"id": "0a876338e2be6dd8",
|
|
475
|
+
"type": "http request",
|
|
476
|
+
"z": "e5621a7de149225c",
|
|
477
|
+
"g": "58e5886e40ebacd1",
|
|
478
|
+
"name": "HTTP REQUEST",
|
|
479
|
+
"method": "use",
|
|
480
|
+
"ret": "txt",
|
|
481
|
+
"paytoqs": "ignore",
|
|
482
|
+
"url": "",
|
|
483
|
+
"tls": "2418490b3512393e",
|
|
484
|
+
"persist": false,
|
|
485
|
+
"proxy": "",
|
|
486
|
+
"insecureHTTPParser": false,
|
|
487
|
+
"authType": "",
|
|
488
|
+
"senderr": false,
|
|
489
|
+
"headers": [],
|
|
490
|
+
"x": 1550,
|
|
491
|
+
"y": 320,
|
|
492
|
+
"wires": [
|
|
493
|
+
[
|
|
494
|
+
"d04fc338048162ec"
|
|
495
|
+
]
|
|
496
|
+
]
|
|
497
|
+
},
|
|
498
|
+
{
|
|
499
|
+
"id": "5e76761149f4280e",
|
|
500
|
+
"type": "function",
|
|
501
|
+
"z": "e5621a7de149225c",
|
|
502
|
+
"g": "58e5886e40ebacd1",
|
|
503
|
+
"name": "Creation results",
|
|
504
|
+
"func": "let device = msg.device;\nconst debug = flow.get('$parent.g_debug');\n\nswitch (msg.statusCode) {\n ////////////////////////////////////////////////// \n // Case 200 : \"Success\" > Objects have been created\n //////////////////////////////////////////////////\n case 200:\n if (msg.payload.includes(\"\\\"status\\\":200\")) {\n debug(device, \"creation\", `${device.identity.deviceName} (RestAPI) : Some BACnet objects have been created`);\n }\n if (msg.payload.includes(\"Instance already exists\") || msg.payload.includes(\"Object with same name already exists\")) {\n flow.set('g_errorObjectCreation', flow.get('g_errorObjectCreation') + 1);\n node.error(`${device.identity.deviceName} : Some BACnet objects already existed`);\n }\n break;\n\n case 400:\n node.error(\"Error : Bad HTTP Request\");\n break;\n\n case 401:\n node.error(\"Error : Can't connect to controller : Authorization error\");\n break;\n\n case 500:\n node.error(\"Error : Server Error 500\");\n break;\n\n case 404:\n node.error(\"Error : 404\");\n break;\n\n\n case \"ETIMEDOUT\":\n node.error(\"Error : Can't connect to controller : TimeOut\");\n break;\n\n case \"UNABLE_TO_VERIFY_LEAF_SIGNATURE\":\n node.error(\"Error : You forgot to enable the TLS config in your HTTP node\");\n break;\n\n}\n\n\n",
|
|
505
|
+
"outputs": 1,
|
|
506
|
+
"timeout": 0,
|
|
507
|
+
"noerr": 0,
|
|
508
|
+
"initialize": "// Le code ajouté ici sera exécuté une fois\n// à chaque démarrage du noeud.\nglobal.set('g_errorObjectCreation', 0);",
|
|
509
|
+
"finalize": "",
|
|
510
|
+
"libs": [],
|
|
511
|
+
"x": 1780,
|
|
512
|
+
"y": 260,
|
|
513
|
+
"wires": [
|
|
514
|
+
[]
|
|
515
|
+
],
|
|
516
|
+
"icon": "node-red/alert.svg"
|
|
517
|
+
},
|
|
518
|
+
{
|
|
519
|
+
"id": "d04fc338048162ec",
|
|
520
|
+
"type": "function",
|
|
521
|
+
"z": "e5621a7de149225c",
|
|
522
|
+
"g": "58e5886e40ebacd1",
|
|
523
|
+
"name": "Debug Write",
|
|
524
|
+
"func": "let device = msg.device;\nconst debug = flow.get('$parent.g_debug');\n\ndebug(device, \"txTime\", `${device.identity.deviceName} (${device.controller.protocol}) : TX time = ${Date.now() - device.transmitTime} ms`); \n\nreturn msg;",
|
|
525
|
+
"outputs": 1,
|
|
526
|
+
"timeout": 0,
|
|
527
|
+
"noerr": 0,
|
|
528
|
+
"initialize": "",
|
|
529
|
+
"finalize": "",
|
|
530
|
+
"libs": [],
|
|
531
|
+
"x": 1770,
|
|
532
|
+
"y": 320,
|
|
533
|
+
"wires": [
|
|
534
|
+
[
|
|
535
|
+
"d7fb76702f1ebdab"
|
|
536
|
+
]
|
|
537
|
+
]
|
|
538
|
+
},
|
|
539
|
+
{
|
|
540
|
+
"id": "0ad4d2e61e8cd612",
|
|
541
|
+
"type": "change",
|
|
542
|
+
"z": "e5621a7de149225c",
|
|
543
|
+
"name": "Read/Write in process",
|
|
544
|
+
"rules": [
|
|
545
|
+
{
|
|
546
|
+
"t": "delete",
|
|
547
|
+
"p": "payload",
|
|
548
|
+
"pt": "msg"
|
|
549
|
+
},
|
|
550
|
+
{
|
|
551
|
+
"t": "set",
|
|
552
|
+
"p": "payload.text",
|
|
553
|
+
"pt": "msg",
|
|
554
|
+
"to": "$env('dataDirection') & \" Read/Write in process...\"",
|
|
555
|
+
"tot": "jsonata"
|
|
556
|
+
},
|
|
557
|
+
{
|
|
558
|
+
"t": "set",
|
|
559
|
+
"p": "payload.fill",
|
|
560
|
+
"pt": "msg",
|
|
561
|
+
"to": "yellow",
|
|
562
|
+
"tot": "str"
|
|
563
|
+
},
|
|
564
|
+
{
|
|
565
|
+
"t": "set",
|
|
566
|
+
"p": "payload.shape",
|
|
567
|
+
"pt": "msg",
|
|
568
|
+
"to": "ring",
|
|
569
|
+
"tot": "str"
|
|
570
|
+
}
|
|
571
|
+
],
|
|
572
|
+
"action": "",
|
|
573
|
+
"property": "",
|
|
574
|
+
"from": "",
|
|
575
|
+
"to": "",
|
|
576
|
+
"reg": false,
|
|
577
|
+
"x": 460,
|
|
578
|
+
"y": 600,
|
|
579
|
+
"wires": [
|
|
580
|
+
[
|
|
581
|
+
"c5511cd0867d06e2"
|
|
582
|
+
]
|
|
583
|
+
]
|
|
584
|
+
},
|
|
585
|
+
{
|
|
586
|
+
"id": "a3a2696eda710a5e",
|
|
587
|
+
"type": "change",
|
|
588
|
+
"z": "e5621a7de149225c",
|
|
589
|
+
"name": "Read/Write complete",
|
|
590
|
+
"rules": [
|
|
591
|
+
{
|
|
592
|
+
"t": "delete",
|
|
593
|
+
"p": "payload",
|
|
594
|
+
"pt": "msg"
|
|
595
|
+
},
|
|
596
|
+
{
|
|
597
|
+
"t": "set",
|
|
598
|
+
"p": "payload.text",
|
|
599
|
+
"pt": "msg",
|
|
600
|
+
"to": "$env('dataDirection') & \" Read/Write complete\"",
|
|
601
|
+
"tot": "jsonata"
|
|
602
|
+
},
|
|
603
|
+
{
|
|
604
|
+
"t": "set",
|
|
605
|
+
"p": "payload.fill",
|
|
606
|
+
"pt": "msg",
|
|
607
|
+
"to": "green",
|
|
608
|
+
"tot": "str"
|
|
609
|
+
},
|
|
610
|
+
{
|
|
611
|
+
"t": "set",
|
|
612
|
+
"p": "payload.shape",
|
|
613
|
+
"pt": "msg",
|
|
614
|
+
"to": "dot",
|
|
615
|
+
"tot": "str"
|
|
616
|
+
}
|
|
617
|
+
],
|
|
618
|
+
"action": "",
|
|
619
|
+
"property": "",
|
|
620
|
+
"from": "",
|
|
621
|
+
"to": "",
|
|
622
|
+
"reg": false,
|
|
623
|
+
"x": 460,
|
|
624
|
+
"y": 560,
|
|
625
|
+
"wires": [
|
|
626
|
+
[
|
|
627
|
+
"c5511cd0867d06e2"
|
|
628
|
+
]
|
|
629
|
+
]
|
|
630
|
+
},
|
|
631
|
+
{
|
|
632
|
+
"id": "ed3e8b9be7a13e70",
|
|
633
|
+
"type": "change",
|
|
634
|
+
"z": "e5621a7de149225c",
|
|
635
|
+
"name": "Object creation complete",
|
|
636
|
+
"rules": [
|
|
637
|
+
{
|
|
638
|
+
"t": "delete",
|
|
639
|
+
"p": "payload",
|
|
640
|
+
"pt": "msg"
|
|
641
|
+
},
|
|
642
|
+
{
|
|
643
|
+
"t": "set",
|
|
644
|
+
"p": "payload.text",
|
|
645
|
+
"pt": "msg",
|
|
646
|
+
"to": "$env('dataDirection') & \" Object creation complete\"",
|
|
647
|
+
"tot": "jsonata"
|
|
648
|
+
},
|
|
649
|
+
{
|
|
650
|
+
"t": "set",
|
|
651
|
+
"p": "payload.fill",
|
|
652
|
+
"pt": "msg",
|
|
653
|
+
"to": "green",
|
|
654
|
+
"tot": "str"
|
|
655
|
+
},
|
|
656
|
+
{
|
|
657
|
+
"t": "set",
|
|
658
|
+
"p": "payload.shape",
|
|
659
|
+
"pt": "msg",
|
|
660
|
+
"to": "dot",
|
|
661
|
+
"tot": "str"
|
|
662
|
+
}
|
|
663
|
+
],
|
|
664
|
+
"action": "",
|
|
665
|
+
"property": "",
|
|
666
|
+
"from": "",
|
|
667
|
+
"to": "",
|
|
668
|
+
"reg": false,
|
|
669
|
+
"x": 470,
|
|
670
|
+
"y": 640,
|
|
671
|
+
"wires": [
|
|
672
|
+
[
|
|
673
|
+
"c5511cd0867d06e2"
|
|
674
|
+
]
|
|
675
|
+
]
|
|
676
|
+
},
|
|
677
|
+
{
|
|
678
|
+
"id": "e3be43afc71d8df9",
|
|
679
|
+
"type": "change",
|
|
680
|
+
"z": "e5621a7de149225c",
|
|
681
|
+
"name": "Object creation in process",
|
|
682
|
+
"rules": [
|
|
683
|
+
{
|
|
684
|
+
"t": "delete",
|
|
685
|
+
"p": "payload",
|
|
686
|
+
"pt": "msg"
|
|
687
|
+
},
|
|
688
|
+
{
|
|
689
|
+
"t": "set",
|
|
690
|
+
"p": "payload.text",
|
|
691
|
+
"pt": "msg",
|
|
692
|
+
"to": "$env('dataDirection') & \" Object creation in process...\"",
|
|
693
|
+
"tot": "jsonata"
|
|
694
|
+
},
|
|
695
|
+
{
|
|
696
|
+
"t": "set",
|
|
697
|
+
"p": "payload.fill",
|
|
698
|
+
"pt": "msg",
|
|
699
|
+
"to": "yellow",
|
|
700
|
+
"tot": "str"
|
|
701
|
+
},
|
|
702
|
+
{
|
|
703
|
+
"t": "set",
|
|
704
|
+
"p": "payload.shape",
|
|
705
|
+
"pt": "msg",
|
|
706
|
+
"to": "ring",
|
|
707
|
+
"tot": "str"
|
|
708
|
+
}
|
|
709
|
+
],
|
|
710
|
+
"action": "",
|
|
711
|
+
"property": "",
|
|
712
|
+
"from": "",
|
|
713
|
+
"to": "",
|
|
714
|
+
"reg": false,
|
|
715
|
+
"x": 470,
|
|
716
|
+
"y": 520,
|
|
717
|
+
"wires": [
|
|
718
|
+
[
|
|
719
|
+
"c5511cd0867d06e2"
|
|
720
|
+
]
|
|
721
|
+
]
|
|
722
|
+
},
|
|
723
|
+
{
|
|
724
|
+
"id": "d7fb76702f1ebdab",
|
|
725
|
+
"type": "link out",
|
|
726
|
+
"z": "e5621a7de149225c",
|
|
727
|
+
"g": "58e5886e40ebacd1",
|
|
728
|
+
"name": "link out Object creation complete",
|
|
729
|
+
"mode": "link",
|
|
730
|
+
"links": [
|
|
731
|
+
"9fdaa35d3e506094"
|
|
732
|
+
],
|
|
733
|
+
"x": 1875,
|
|
734
|
+
"y": 320,
|
|
735
|
+
"wires": []
|
|
736
|
+
},
|
|
737
|
+
{
|
|
738
|
+
"id": "9fdaa35d3e506094",
|
|
739
|
+
"type": "link in",
|
|
740
|
+
"z": "e5621a7de149225c",
|
|
741
|
+
"name": "link in Object creation complete",
|
|
742
|
+
"links": [
|
|
743
|
+
"d7fb76702f1ebdab"
|
|
744
|
+
],
|
|
745
|
+
"x": 295,
|
|
746
|
+
"y": 640,
|
|
747
|
+
"wires": [
|
|
748
|
+
[
|
|
749
|
+
"ed3e8b9be7a13e70"
|
|
750
|
+
]
|
|
751
|
+
]
|
|
752
|
+
},
|
|
753
|
+
{
|
|
754
|
+
"id": "408508b0ed73706f",
|
|
755
|
+
"type": "link in",
|
|
756
|
+
"z": "e5621a7de149225c",
|
|
757
|
+
"name": "link in Read/Write in progress",
|
|
758
|
+
"links": [
|
|
759
|
+
"7d4514aea698cd4b"
|
|
760
|
+
],
|
|
761
|
+
"x": 295,
|
|
762
|
+
"y": 600,
|
|
763
|
+
"wires": [
|
|
764
|
+
[
|
|
765
|
+
"0ad4d2e61e8cd612"
|
|
766
|
+
]
|
|
767
|
+
]
|
|
768
|
+
},
|
|
769
|
+
{
|
|
770
|
+
"id": "452d87a8fe8e5e5b",
|
|
771
|
+
"type": "link in",
|
|
772
|
+
"z": "e5621a7de149225c",
|
|
773
|
+
"name": "link in Read/Write complete",
|
|
774
|
+
"links": [
|
|
775
|
+
"a1b40b26fe6dd41f"
|
|
776
|
+
],
|
|
777
|
+
"x": 295,
|
|
778
|
+
"y": 560,
|
|
779
|
+
"wires": [
|
|
780
|
+
[
|
|
781
|
+
"a3a2696eda710a5e"
|
|
782
|
+
]
|
|
783
|
+
]
|
|
784
|
+
},
|
|
785
|
+
{
|
|
786
|
+
"id": "b6703631b9a583d8",
|
|
787
|
+
"type": "link in",
|
|
788
|
+
"z": "e5621a7de149225c",
|
|
789
|
+
"name": "link in object creation in process",
|
|
790
|
+
"links": [
|
|
791
|
+
"780a20b5b444c3d7"
|
|
792
|
+
],
|
|
793
|
+
"x": 295,
|
|
794
|
+
"y": 520,
|
|
795
|
+
"wires": [
|
|
796
|
+
[
|
|
797
|
+
"e3be43afc71d8df9"
|
|
798
|
+
]
|
|
799
|
+
]
|
|
800
|
+
},
|
|
801
|
+
{
|
|
802
|
+
"id": "780a20b5b444c3d7",
|
|
803
|
+
"type": "link out",
|
|
804
|
+
"z": "e5621a7de149225c",
|
|
805
|
+
"name": "link out object creation",
|
|
806
|
+
"mode": "link",
|
|
807
|
+
"links": [
|
|
808
|
+
"b6703631b9a583d8"
|
|
809
|
+
],
|
|
810
|
+
"x": 1015,
|
|
811
|
+
"y": 260,
|
|
812
|
+
"wires": []
|
|
813
|
+
},
|
|
814
|
+
{
|
|
815
|
+
"id": "7d4514aea698cd4b",
|
|
816
|
+
"type": "link out",
|
|
817
|
+
"z": "e5621a7de149225c",
|
|
818
|
+
"name": "link out Read/Write in progress",
|
|
819
|
+
"mode": "link",
|
|
820
|
+
"links": [
|
|
821
|
+
"408508b0ed73706f"
|
|
822
|
+
],
|
|
823
|
+
"x": 535,
|
|
824
|
+
"y": 420,
|
|
825
|
+
"wires": []
|
|
826
|
+
},
|
|
827
|
+
{
|
|
828
|
+
"id": "6c31111be68ac559",
|
|
829
|
+
"type": "function",
|
|
830
|
+
"z": "e5621a7de149225c",
|
|
831
|
+
"name": "Store Downlink object",
|
|
832
|
+
"func": "/////////////////////////////////////////////////////////////////////\n///////////////// Store Downlink Objects //////////////\n/////////////////////////////////////////////////////////////////////\n/* This function stores the downlink data from the controller */\n\nlet device = msg.device;\nlet bacnetObjects = device.bacnet.objects;\n\n// For InfluxDB support\ndevice.influxdb.source = \"downlink\";\n\nswitch (device.controller.model) {\n case \"distechControlsV2\":\n let donwlinkObjects = msg.payload;\n\n for (let i = 0; i < donwlinkObjects.results.length; i++) {\n Object.values(bacnetObjects).forEach(obj => {\n if (donwlinkObjects.results[i].type == obj.objectType && donwlinkObjects.results[i].instance == obj.instanceNum) {\n if (obj.objectType == \"analogValue\") obj.value = Number(donwlinkObjects.results[i].value);\n if (obj.objectType == \"binaryValue\") obj.value = donwlinkObjects.results[i].value;\n }\n });\n }\n\n return {\n device: device\n };\n\n\n\n ///////////////////////////////////////////////////////////\n ////// XXXXX Controller\n ////// URL to the API documentation\n ///////////////////////////////////////////////////////////\n case \"anotherController\":\n\n}\n",
|
|
833
|
+
"outputs": 1,
|
|
834
|
+
"timeout": 0,
|
|
835
|
+
"noerr": 0,
|
|
836
|
+
"initialize": "",
|
|
837
|
+
"finalize": "",
|
|
838
|
+
"libs": [],
|
|
839
|
+
"x": 1320,
|
|
840
|
+
"y": 420,
|
|
841
|
+
"wires": [
|
|
842
|
+
[
|
|
843
|
+
"f8a6a26c3a4274bf"
|
|
844
|
+
]
|
|
845
|
+
]
|
|
846
|
+
},
|
|
847
|
+
{
|
|
848
|
+
"id": "a1b40b26fe6dd41f",
|
|
849
|
+
"type": "link out",
|
|
850
|
+
"z": "e5621a7de149225c",
|
|
851
|
+
"name": "link out Read/Write complete",
|
|
852
|
+
"mode": "link",
|
|
853
|
+
"links": [
|
|
854
|
+
"452d87a8fe8e5e5b"
|
|
855
|
+
],
|
|
856
|
+
"x": 1535,
|
|
857
|
+
"y": 500,
|
|
858
|
+
"wires": []
|
|
859
|
+
},
|
|
860
|
+
{
|
|
861
|
+
"id": "8118aa09800ae8ee",
|
|
862
|
+
"type": "switch",
|
|
863
|
+
"z": "e5621a7de149225c",
|
|
864
|
+
"name": "",
|
|
865
|
+
"property": "dataDirection",
|
|
866
|
+
"propertyType": "env",
|
|
867
|
+
"rules": [
|
|
868
|
+
{
|
|
869
|
+
"t": "eq",
|
|
870
|
+
"v": "downlink",
|
|
871
|
+
"vt": "str"
|
|
872
|
+
},
|
|
873
|
+
{
|
|
874
|
+
"t": "eq",
|
|
875
|
+
"v": "uplink",
|
|
876
|
+
"vt": "str"
|
|
877
|
+
}
|
|
878
|
+
],
|
|
879
|
+
"checkall": "true",
|
|
880
|
+
"repair": false,
|
|
881
|
+
"outputs": 2,
|
|
882
|
+
"x": 1175,
|
|
883
|
+
"y": 460,
|
|
884
|
+
"wires": [
|
|
885
|
+
[
|
|
886
|
+
"6c31111be68ac559"
|
|
887
|
+
],
|
|
888
|
+
[
|
|
889
|
+
"f8a6a26c3a4274bf"
|
|
890
|
+
]
|
|
891
|
+
],
|
|
892
|
+
"outputLabels": [
|
|
893
|
+
"downlink",
|
|
894
|
+
"uplink"
|
|
895
|
+
],
|
|
896
|
+
"l": false
|
|
897
|
+
},
|
|
898
|
+
{
|
|
899
|
+
"id": "580fb662e924a681",
|
|
900
|
+
"type": "switch",
|
|
901
|
+
"z": "e5621a7de149225c",
|
|
902
|
+
"name": "",
|
|
903
|
+
"property": "device.controller.protocol",
|
|
904
|
+
"propertyType": "msg",
|
|
905
|
+
"rules": [
|
|
906
|
+
{
|
|
907
|
+
"t": "eq",
|
|
908
|
+
"v": "RestAPIBacnet",
|
|
909
|
+
"vt": "str"
|
|
910
|
+
}
|
|
911
|
+
],
|
|
912
|
+
"checkall": "true",
|
|
913
|
+
"repair": false,
|
|
914
|
+
"outputs": 1,
|
|
915
|
+
"x": 450,
|
|
916
|
+
"y": 420,
|
|
917
|
+
"wires": [
|
|
918
|
+
[
|
|
919
|
+
"7d4514aea698cd4b"
|
|
920
|
+
]
|
|
921
|
+
]
|
|
922
|
+
},
|
|
923
|
+
{
|
|
924
|
+
"id": "adb0c33fcb917696",
|
|
925
|
+
"type": "mqtt out",
|
|
926
|
+
"z": "d5185b34c44ae683",
|
|
927
|
+
"g": "90c82936d1f21eda",
|
|
928
|
+
"name": "MQTT Publisher",
|
|
929
|
+
"topic": "",
|
|
930
|
+
"qos": "",
|
|
931
|
+
"retain": "",
|
|
932
|
+
"respTopic": "",
|
|
933
|
+
"contentType": "",
|
|
934
|
+
"userProps": "",
|
|
935
|
+
"correl": "",
|
|
936
|
+
"expiry": "",
|
|
937
|
+
"broker": "d6dd3de173c23998",
|
|
938
|
+
"x": 1240,
|
|
939
|
+
"y": 300,
|
|
940
|
+
"wires": []
|
|
941
|
+
},
|
|
942
|
+
{
|
|
943
|
+
"id": "4fbd348dfae7d9ce",
|
|
944
|
+
"type": "function",
|
|
945
|
+
"z": "d5185b34c44ae683",
|
|
946
|
+
"g": "90c82936d1f21eda",
|
|
947
|
+
"name": "prepare downlink",
|
|
948
|
+
"func": "///////////////////////////////////////////////////////////\n////// This part is device dependant\n////// The configuration depends on the downlink strategy\n///////////////////////////////////////////////////////////\n\nlet device = msg.device;\n\nvar staticDownlinkObjects = device.lorawan.defaultValuesForDownlink ?? null ;\n\nlet bacnetObjects = device.bacnet.objects;\nconst debug = flow.get(\"g_debug\");\nlet downlinkLowPriorityObject = 0, downlinkHighPriorityObject = 0; \nlet previousValues = flow.get(\"g_previousValues\");\nlet previousBacnetObject = previousValues[device.identity.deviceName].bacnet.objects;\nlet payload={};\n\nfunction downlinkPayloadCreation(downlinkObjectToSend) {\n //Creation of the downlink payload\n for (let obj in bacnetObjects){\n if (bacnetObjects[obj].dataDirection === \"downlink\" && bacnetObjects[obj].downlinkPort == bacnetObjects[downlinkObjectToSend].downlinkPort){\n let temp = \"{ \\\"\" + obj + \"\\\" : \" + JSON.stringify(bacnetObjects[obj].value) + \" }\"; \n payload = { ...payload, ...JSON.parse(temp) }\n }\n }\n // Chek if there are other values to add to the payload\n if (device.lorawan.hasOwnProperty(\"defaultValuesForDownlink\") ){\n node.warn(\"my warning\");\n if (device.lorawan.defaultValuesForDownlink.hasOwnProperty(\"fPort_\" + bacnetObjects[downlinkObjectToSend].downlinkPort)){\n for (let obj in staticDownlinkObjects[\"fPort_\"+ bacnetObjects[downlinkObjectToSend].downlinkPort]){\n let temp = \"{ \\\"\" + obj + \"\\\" : \" + JSON.stringify(staticDownlinkObjects[\"fPort_\"+ bacnetObjects[downlinkObjectToSend].downlinkPort][obj]) + \" }\"; \n payload = { ...payload, ...JSON.parse(temp) }\n \n }\n }\n }\n msg.device.lorawan.downlinkPort = bacnetObjects[downlinkObjectToSend].downlinkPort\n \n}\n\nfor (let object in bacnetObjects) {\n\n if (bacnetObjects[object].dataDirection === \"downlink\") {\n \n switch (bacnetObjects[object].downlinkPortPriority) {\n case \"high\":\n switch (bacnetObjects[object].downlinkStrategy) {\n case \"onChangeOfThisValue\":\n if (bacnetObjects[object].value != previousBacnetObject[object].value) {\n node.status({ fill: \"yellow\", shape: \"dot\", text: \"Downlink high priority COV\" });\n debug(device, \"down\", `${device.identity.deviceName} (${device.controller.protocol}) : Downlink scheduled : Previous value ${previousBacnetObject[object].value} != ${object} ${bacnetObjects[object].value}`);\n //Creation of the downlink payload\n downlinkPayloadCreation(object)\n }\n break;\n case \"onChangeOfThisValueWithinRange\":\n if (bacnetObjects[object].value != previousBacnetObject[object].value && bacnetObjects[object].value <= bacnetObjects[object].range[1] && bacnetObjects[object].value >= bacnetObjects[object].range[0]) {\n node.status({ fill: \"yellow\", shape: \"dot\", text: \"Downlink high priority COVWR\" });\n debug(device, \"down\", `${device.identity.deviceName} (${device.controller.protocol}) : Downlink scheduled : Previous value ${previousBacnetObject[object].value} != ${object} ${bacnetObjects[object].value}`);\n //Creation of the downlink payload\n downlinkPayloadCreation(object)\n }\n break;\n case \"compareToUplinkObjectWithinRange\":\n if (bacnetObjects[object].value != bacnetObjects[bacnetObjects[object].uplinkToCompareWith].value && bacnetObjects[object].value <= bacnetObjects[object].range[1] && bacnetObjects[object].value >= bacnetObjects[object].range[0]) {\n node.status({ fill: \"yellow\", shape: \"dot\", text: \"Downlink high priority CUVWR\" });\n debug(device, \"down\", `${device.identity.deviceName} (${device.controller.protocol}) : Downlink scheduled : ${bacnetObjects[object].uplinkToCompareWith} ${bacnetObjects[bacnetObjects[object].uplinkToCompareWith].value} != ${object} ${bacnetObjects[object].value}`);\n //Creation of the downlink payload\n downlinkPayloadCreation(object)\n }\n break;\n case \"compareToUplinkObject\":\n if (bacnetObjects[object].value != bacnetObjects[bacnetObjects[object].uplinkToCompareWith].value) {\n node.status({ fill: \"yellow\", shape: \"dot\", text: \"Downlink high priority CUV\" });\n debug(device, \"down\", `${device.identity.deviceName} (${device.controller.protocol}) : Downlink scheduled : ${bacnetObjects[object].uplinkToCompareWith} ${bacnetObjects[bacnetObjects[object].uplinkToCompareWith].value} != ${object} ${bacnetObjects[object].value}`);\n //Creation of the downlink payload\n downlinkPayloadCreation(object)\n }\n break;\n default:\n \n }\n \n break;\n case \"low\":\n //In case of low priority downlink the object name is kept till the end of the for loop \n // to be sur that there is not any high priority downlink to send \n switch (bacnetObjects[object].downlinkStrategy) {\n case \"onChangeOfThisValue\":\n if (bacnetObjects[object].value != previousBacnetObject[object].value) {\n downlinkLowPriorityObject = object;\n }\n break;\n case \"onChangeOfThisValueWithinRange\":\n if (bacnetObjects[object].value != previousBacnetObject[object].value && bacnetObjects[object].value <= bacnetObjects[object].range[1] && bacnetObjects[object].value >= bacnetObjects[object].range[0]) {\n downlinkLowPriorityObject = object;\n }\n break;\n case \"compareToUplinkObjectWithinRange\":\n if (bacnetObjects[object].value != bacnetObjects[bacnetObjects[object].uplinkToCompareWith]?.value && bacnetObjects[object].value <= bacnetObjects[object].range[1] && bacnetObjects[object].value >= bacnetObjects[object].range[0]) {\n downlinkLowPriorityObject = object;\n }\n break;\n case \"compareToUplinkObject\":\n if (bacnetObjects[object].value != bacnetObjects[bacnetObjects[object].uplinkToCompareWith].value) {\n downlinkLowPriorityObject = object;\n }\n break;\n default:\n \n }\n break;\n default:\n\n }\n }\n if (Object.keys(payload).length !== 0){\n break;\n }\n}\nif (downlinkLowPriorityObject != 0 && Object.keys(payload).length === 0) {\n node.status({ fill: \"yellow\", shape: \"dot\", text: \"Downlink low priority\" });\n switch (bacnetObjects[downlinkLowPriorityObject].downlinkStrategy) {\n case \"onChangeOfThisValue\":\n debug(device, \"down\", `${device.identity.deviceName} (${device.controller.protocol}) : Downlink scheduled : Previous value ${previousBacnetObject[downlinkLowPriorityObject].value} != ${downlinkLowPriorityObject} ${bacnetObjects[downlinkLowPriorityObject].value}`);\n break;\n case \"onChangeOfThisValueWithinRange\":\n debug(device, \"down\", `${device.identity.deviceName} (${device.controller.protocol}) : Downlink scheduled : Previous value ${previousBacnetObject[downlinkLowPriorityObject].value} != ${downlinkLowPriorityObject} ${bacnetObjects[downlinkLowPriorityObject].value}`);\n break;\n case \"compareToUplinkObjectWithinRange\":\n debug(device, \"down\", `${device.identity.deviceName} (${device.controller.protocol}) : Downlink scheduled : ${bacnetObjects[downlinkLowPriorityObject].uplinkToCompareWith} ${bacnetObjects[bacnetObjects[downlinkLowPriorityObject].uplinkToCompareWith].value} != ${downlinkLowPriorityObject} ${bacnetObjects[downlinkLowPriorityObject].value}`);\n break;\n case \"compareToUplinkObject\":\n debug(device, \"down\", `${device.identity.deviceName} (${device.controller.protocol}) : Downlink scheduled : ${bacnetObjects[downlinkLowPriorityObject].uplinkToCompareWith} ${bacnetObjects[bacnetObjects[downlinkLowPriorityObject].uplinkToCompareWith].value} != ${downlinkLowPriorityObject} ${bacnetObjects[downlinkLowPriorityObject].value}`);\n break;\n default:\n \n }\n //Creation of the dowlink payload\n downlinkPayloadCreation(downlinkLowPriorityObject) \n}else if (Object.keys(payload).length === 0){\n\n node.status({fill: \"green\", shape: \"dot\" ,text: \"No downlink\"});\n return null;\n}\n\n//Update the previous values\nfor (let object in payload) {\n previousBacnetObject[object].value = payload[object] ;\n}\n\n\n//Create the downlink payload\nlet downlinkJson = {};\n// Modify the downlink object according to the lorawanPayloadName.\n\nfor (let object in payload) {\n // Don't do any changes if lorawanPayloadName is the same as the BACnet Object name.\n if (!Object.keys(device.bacnet.objects).some(element => element == device.bacnet.objects[object].lorawanPayloadName)) {\n payload[device.bacnet.objects[object].lorawanPayloadName] = payload[object];\n delete payload[object];\n }\n}\n\n\n//////////////////////////////////////////////////////////////////////////\n// The Things Stack Network Server \n/////////////////////////////////////////////////////////////////////////\nif (device.lorawan.networkServer == \"tts\") {\n downlinkJson = {\n \"topic\": device.mqtt.topicDownlink + (device.lorawan.flushDownlinkQueue ? \"/replace\" : \"/push\"),\n \"payload\": {\n \"downlinks\": [\n {\n \"f_port\": device.lorawan.downlinkPort,\n \"decoded_payload\": payload,\n \"priority\": \"NORMAL\"\n },\n ]\n }\n }\n debug(device, \"txTime\", `${device.identity.deviceName} (${device.controller.protocol}) : TX time = ${Date.now() - device.transmitTime} ms`);\n return downlinkJson;\n}\n/*\n//////////////////////////////////////////////////////////////////////////\n// helium Network Server \n/////////////////////////////////////////////////////////////////////////\nif (device.lorawan.networkServer == \"helium\") {\n downlinkJson = {\n \"topic\": device.mqtt.topicDownlink,\n \"payload\": {\n \"payload_raw\": \"SGVsbG8sIHdvcmxkIQ==\",\n \"port\": device.lorawan.downlinkPort,\n \"confirmed\": false\n }\n }\n debug(device, \"txTime\", `${device.identity.deviceName} (${device.controller.protocol}) : TX time = ${Date.now() - device.transmitTime} ms`);\n return downlinkJson;\n}\n*/\n//////////////////////////////////////////////////////////////////////////\n// Chipstack Network Server \n/////////////////////////////////////////////////////////////////////////\n// 1. Flush the downlink Queue\n\nif (device.lorawan.networkServer == \"chirpstack\") {\n if (device.lorawan.flushDownlinkQueue == true) {\n node.warn(device);\n debug(device, \"downlink\", device.identity.deviceName + \" flush downlink queue\");\n //We include flow value from libraries\n var grpc = grpcJs;\n var device_grpc = chirpstack_device_grpc;\n var device_pb = chirpstack_device_pb;\n\n // This must point to the ChirpStack API interface.\n const server = device.lorawan.chirpstack.serverAddress + \":\" + device.lorawan.chirpstack.grpcPort;\n // The DevEUI for which we want to enqueue the downlink.\n const devEui = device.identity.devEUI;\n \n // The API token (can be obtained through the ChirpStack web-interface).\n const apiToken = device.lorawan.chirpstack.grpcApiKey;\n\n // Create the client for the DeviceService.\n const deviceService = new device_grpc.DeviceServiceClient(\n server,\n grpc.credentials.createInsecure(),\n );\n\n // Create the Metadata object.\n const metadata = new grpc.Metadata();\n metadata.set(\"authorization\", \"Bearer \" + apiToken);\n\n //Flush downlink queue request\n const flushReq = new device_pb.FlushDeviceQueueRequest();\n flushReq.setDevEui(devEui);\n\n //Send the request\n deviceService.flushQueue(flushReq, metadata, (err, resp) => {\n if (err !== null) {\n node.error(`Can't flush ChirpStack downlink queue : ${err}`);\n }\n });\n }\n\n // 2. Prepare downlink JSON\n downlinkJson = {\n \"topic\": device.mqtt.topicDownlink,\n \"payload\": {\n \"devEui\": device.identity.devEUI,\n \"confirmed\": false,\n \"fPort\": device.lorawan.downlinkPort,\n \"object\": payload\n }\n }\n debug(device, \"txTime\", `${device.identity.deviceName} (${device.controller.protocol}) : TX time = ${Date.now() - device.transmitTime} ms`); \n return downlinkJson;\n}\n\n\n//////////////////////////////////////////////////////////////////////////\n// Actility Network Server \n/////////////////////////////////////////////////////////////////////////\nif (device.lorawan.networkServer == \"actility\") {\n downlinkJson = {\n \"topic\": device.mqtt.topicDownlink,\n \"payload\": {\n \"DevEUI_downlink\": {\n \"DevEUI\": device.identity.devEUI,\n \"FPort\": device.lorawan.downlinkPort,\n \"payload\": payload,\n \"FlushDownlinkQueue\": String(+device.lorawan.flushDownlinkQueue),\n \"DriverCfg\": {\n \"app\": {\n \"pId\": device.lorawan.actility.driver.pId,\n \"mId\": device.lorawan.actility.driver.mId,\n \"ver\": device.lorawan.actility.driver.ver\n }\n }\n }\n }\n }\n debug(device, \"txTime\", `${device.identity.deviceName} (${device.controller.protocol}) : TX time = ${Date.now() - device.transmitTime} ms`);\n return downlinkJson;\n}\n\n\n\n\n",
|
|
949
|
+
"outputs": 1,
|
|
950
|
+
"timeout": 0,
|
|
951
|
+
"noerr": 0,
|
|
952
|
+
"initialize": "",
|
|
953
|
+
"finalize": "",
|
|
954
|
+
"libs": [
|
|
955
|
+
{
|
|
956
|
+
"var": "grpcJs",
|
|
957
|
+
"module": "@grpc/grpc-js"
|
|
958
|
+
},
|
|
959
|
+
{
|
|
960
|
+
"var": "chirpstack_device_pb",
|
|
961
|
+
"module": "@chirpstack/chirpstack-api/api/device_pb"
|
|
962
|
+
},
|
|
963
|
+
{
|
|
964
|
+
"var": "chirpstack_device_grpc",
|
|
965
|
+
"module": "@chirpstack/chirpstack-api/api/device_grpc_pb"
|
|
966
|
+
}
|
|
967
|
+
],
|
|
968
|
+
"x": 1010,
|
|
969
|
+
"y": 300,
|
|
970
|
+
"wires": [
|
|
971
|
+
[
|
|
972
|
+
"1109c524e8f3621b",
|
|
973
|
+
"adb0c33fcb917696"
|
|
974
|
+
]
|
|
975
|
+
],
|
|
976
|
+
"icon": "node-red/cog.svg"
|
|
977
|
+
},
|
|
978
|
+
{
|
|
979
|
+
"id": "1109c524e8f3621b",
|
|
980
|
+
"type": "debug",
|
|
981
|
+
"z": "d5185b34c44ae683",
|
|
982
|
+
"name": "Debug Downlink message",
|
|
983
|
+
"active": false,
|
|
984
|
+
"tosidebar": true,
|
|
985
|
+
"console": false,
|
|
986
|
+
"tostatus": false,
|
|
987
|
+
"complete": "true",
|
|
988
|
+
"targetType": "full",
|
|
989
|
+
"statusVal": "",
|
|
990
|
+
"statusType": "auto",
|
|
991
|
+
"x": 1236,
|
|
992
|
+
"y": 400,
|
|
993
|
+
"wires": []
|
|
994
|
+
},
|
|
995
|
+
{
|
|
996
|
+
"id": "6004507d410bec08",
|
|
997
|
+
"type": "function",
|
|
998
|
+
"z": "d5185b34c44ae683",
|
|
999
|
+
"g": "cfdfc86845dc9bb9",
|
|
1000
|
+
"name": "WRITE uplink objects",
|
|
1001
|
+
"func": "let client = new nodeBacnet();\nlet device = msg.device;\nlet debug = flow.get(\"g_debug\");\nlet temp;\nlet cnt = 0;\nmsg.payload = [];\n\n// If the device controller protocol is not \"bacnet\" there is no need to be here \nif(device?.controller?.protocol != \"bacnet\") return null;\n\nlet bacnetObject = device.bacnet.objects;\nreturn new Promise((resolve, reject) => {\n for (let object in bacnetObject) {\n if (bacnetObject[object].dataDirection === \"uplink\") {\n cnt++;\n const param1 = { \n type: bacnetObject[object].objectType, \n instance: bacnetObject[object].instanceNum\n }; \n const param3 = [{ \n type: ((bacnetObject[object].objectType == 2) ? 4 : 0), \n value: bacnetObject[object].value \n }];\n // Read of the uplink bacnet objects\n client.writeProperty(device.controller.ipAddress, param1, 85, param3, (err, value) => {\n if(err){\n node.status({fill:\"red\",shape:\"dot\",text:\"BACnet \"+ err});\n node.error(\"Error writing bacnet objects : \" + err, {\n errorType: \"nativeBACnet\",\n error: err,\n\n });\n reject(err);\n } else {\n \n msg.payload.push(value);\n debug(device, \"up\",`${device.identity.deviceName} (${device.controller.protocol}) : Write Uplink Objects`)\n node.status({fill:\"green\",shape:\"dot\",text:\"Native BACnet\"});\n\n if (msg.payload.length === cnt){\n resolve(msg);\n }\n }\n });\n\n }\n }\n}).finally((msg) => {\nreturn msg\n}).catch((err) => {\nnode.error(err);\n});",
|
|
1002
|
+
"outputs": 1,
|
|
1003
|
+
"timeout": 0,
|
|
1004
|
+
"noerr": 0,
|
|
1005
|
+
"initialize": "",
|
|
1006
|
+
"finalize": "",
|
|
1007
|
+
"libs": [
|
|
1008
|
+
{
|
|
1009
|
+
"var": "nodeBacnet",
|
|
1010
|
+
"module": "node-bacnet"
|
|
1011
|
+
}
|
|
1012
|
+
],
|
|
1013
|
+
"x": 500,
|
|
1014
|
+
"y": 340,
|
|
1015
|
+
"wires": [
|
|
1016
|
+
[
|
|
1017
|
+
"8ef7bc0b865b1230"
|
|
1018
|
+
]
|
|
1019
|
+
]
|
|
1020
|
+
},
|
|
1021
|
+
{
|
|
1022
|
+
"id": "8ef7bc0b865b1230",
|
|
1023
|
+
"type": "function",
|
|
1024
|
+
"z": "d5185b34c44ae683",
|
|
1025
|
+
"g": "90c82936d1f21eda",
|
|
1026
|
+
"name": "READ downlink objects",
|
|
1027
|
+
"func": "let client = new nodeBacnet();\nlet device = msg.device;\nlet bacnetObject = device.bacnet.objects;\nlet debug = flow.get(\"g_debug\");\nlet requestArray = [];\n\nif (device.controller.protocol != \"bacnet\") return null;\n\n// Build the request array\nfor (let object in bacnetObject) {\n if (bacnetObject[object].dataDirection === \"downlink\") {\n let temp = JSON.parse('{\"objectId\": { \"type\":' + bacnetObject[object].objectType + ', \"instance\":' + bacnetObject[object].instanceNum + '},\"properties\": [ {\"id\": 85} ] }');\n requestArray.push(temp);\n }\n}\n\n// Use a Promise to manage the asynchronous function\nreturn new Promise((resolve, reject) => {\n client.readPropertyMultiple(device.controller.ipAddress, requestArray, (err, value) => {\n if (err) {\n node.error(err);\n node.status({ fill: \"red\", shape: \"dot\", text: \"BACnet \" + err });\n reject(err); // reject the promise in case of error\n } else if (value) {\n msg.payload = value;\n debug(device, \"down\", `${device.identity.deviceName} (${device.controller.protocol}) : Read downlink Objects`);\n node.status({ fill: \"green\", shape: \"dot\", text: \"Native BACnet\" });\n resolve(msg); // resolve the promise with the node message\n }\n });\n}).then((msg) => {\n // once the promise has been resolved\n let values = msg.payload.values || [];\n // store the values in the device objects value property\n for (let i = 0; i < values.length; i++) {\n Object.values(bacnetObject).forEach(obj => {\n if (values[i].objectId.type == obj.objectType && values[i].objectId.instance == obj.instanceNum) {\n obj.value = values[i].values[0].value[0].value;\n }\n });\n }\n return {\n \"device\":device\n };\n}).catch((err) => {\n node.status({ fill: \"red\", shape: \"dot\", text: err });\n node.error(\"Error reading bacnet objects\", {\n errorType: \"nativeBACnet\",\n error: err,\n\n });\n return null;\n});",
|
|
1028
|
+
"outputs": 1,
|
|
1029
|
+
"timeout": 0,
|
|
1030
|
+
"noerr": 0,
|
|
1031
|
+
"initialize": "",
|
|
1032
|
+
"finalize": "",
|
|
1033
|
+
"libs": [
|
|
1034
|
+
{
|
|
1035
|
+
"var": "nodeBacnet",
|
|
1036
|
+
"module": "node-bacnet"
|
|
1037
|
+
}
|
|
1038
|
+
],
|
|
1039
|
+
"x": 770,
|
|
1040
|
+
"y": 340,
|
|
1041
|
+
"wires": [
|
|
1042
|
+
[
|
|
1043
|
+
"4fbd348dfae7d9ce"
|
|
1044
|
+
]
|
|
1045
|
+
]
|
|
1046
|
+
},
|
|
1047
|
+
{
|
|
1048
|
+
"id": "4f48d4661bbf9c4a",
|
|
1049
|
+
"type": "debug",
|
|
1050
|
+
"z": "d5185b34c44ae683",
|
|
1051
|
+
"name": "debug MQTT uplink",
|
|
1052
|
+
"active": false,
|
|
1053
|
+
"tosidebar": true,
|
|
1054
|
+
"console": false,
|
|
1055
|
+
"tostatus": false,
|
|
1056
|
+
"complete": "true",
|
|
1057
|
+
"targetType": "full",
|
|
1058
|
+
"statusVal": "",
|
|
1059
|
+
"statusType": "auto",
|
|
1060
|
+
"x": 323,
|
|
1061
|
+
"y": 400,
|
|
1062
|
+
"wires": []
|
|
1063
|
+
},
|
|
1064
|
+
{
|
|
1065
|
+
"id": "cfaebde9dbc1901d",
|
|
1066
|
+
"type": "mqtt in",
|
|
1067
|
+
"z": "d5185b34c44ae683",
|
|
1068
|
+
"g": "cfdfc86845dc9bb9",
|
|
1069
|
+
"name": "MQTT Subscriber",
|
|
1070
|
+
"topic": "application/+/device/+/event/up",
|
|
1071
|
+
"qos": "0",
|
|
1072
|
+
"datatype": "auto-detect",
|
|
1073
|
+
"broker": "d6dd3de173c23998",
|
|
1074
|
+
"nl": false,
|
|
1075
|
+
"rap": true,
|
|
1076
|
+
"rh": 0,
|
|
1077
|
+
"inputs": 0,
|
|
1078
|
+
"x": 120,
|
|
1079
|
+
"y": 300,
|
|
1080
|
+
"wires": [
|
|
1081
|
+
[
|
|
1082
|
+
"4f48d4661bbf9c4a",
|
|
1083
|
+
"2694240cdee88a83"
|
|
1084
|
+
]
|
|
1085
|
+
],
|
|
1086
|
+
"info": "\r\n# MQTT Subscriber node\r\n\r\n - How to configure this node\r\n -\r\n The configuration off this node depend of which LNS or MQTT broker you use.\r\n\r\n - For The Things Network\r\n - \r\n With **TTN LNS** you have to use the following topic :\r\n ***`v3/{application_id}@ttn/devices/+/up`*** \\\r\n Replace `{application_id}` by your application id in TTN.\\\r\n And don't forget the `@ttn`\r\n - For The Things Stack\r\n - \r\n With **TTS LNS** you have to use the following topic :\r\n ***`v3/{application_id}/devices/+/up`*** \\\r\n Replace `{application_id}` by your application id in TTS.\r\n\r\n - For Chirpstack\r\n - \r\n With **Chirpstack LNS** you have to use the following topic :\r\n ***`application/{application_id}/device/+/event/up`*** \\\r\n Replace `{application_id}` by your application id (it's the number, not the name !).\r\n - For Actility\r\n - \r\n if you use **Actility LNS** see the [documentation](https://docs.thingpark.com/thingpark-x/latest/Connector/BROKER_MQTT/) for using actillity broker\r\n\r\n - For actility-USMB Broker : ***`univ-smb/devices/+/uplink`***\r\n - For HiveMQ Broker : ***`mqtt/things/+/uplink`***\r\n \r\n - For other LNS or MQTT broker\r\n - \r\n If you use any other LNS or MQTT broker please refer to their documentation to configure this MQTT node."
|
|
1087
|
+
},
|
|
1088
|
+
{
|
|
1089
|
+
"id": "a6ff13a9089b4272",
|
|
1090
|
+
"type": "subflow:e5621a7de149225c",
|
|
1091
|
+
"z": "d5185b34c44ae683",
|
|
1092
|
+
"g": "90c82936d1f21eda",
|
|
1093
|
+
"name": "Rest API Read downlink",
|
|
1094
|
+
"env": [
|
|
1095
|
+
{
|
|
1096
|
+
"name": "dataDirection",
|
|
1097
|
+
"value": "downlink",
|
|
1098
|
+
"type": "str"
|
|
1099
|
+
}
|
|
1100
|
+
],
|
|
1101
|
+
"x": 770,
|
|
1102
|
+
"y": 260,
|
|
1103
|
+
"wires": [
|
|
1104
|
+
[
|
|
1105
|
+
"4fbd348dfae7d9ce"
|
|
1106
|
+
]
|
|
1107
|
+
]
|
|
1108
|
+
},
|
|
1109
|
+
{
|
|
1110
|
+
"id": "5338fce50ecc57d9",
|
|
1111
|
+
"type": "subflow:e5621a7de149225c",
|
|
1112
|
+
"z": "d5185b34c44ae683",
|
|
1113
|
+
"g": "cfdfc86845dc9bb9",
|
|
1114
|
+
"name": "Rest API Write uplink",
|
|
1115
|
+
"x": 500,
|
|
1116
|
+
"y": 260,
|
|
1117
|
+
"wires": [
|
|
1118
|
+
[
|
|
1119
|
+
"a6ff13a9089b4272"
|
|
1120
|
+
]
|
|
1121
|
+
]
|
|
1122
|
+
},
|
|
1123
|
+
{
|
|
1124
|
+
"id": "2694240cdee88a83",
|
|
1125
|
+
"type": "LoRaBAC",
|
|
1126
|
+
"z": "d5185b34c44ae683",
|
|
1127
|
+
"g": "cfdfc86845dc9bb9",
|
|
1128
|
+
"name": "LoRaBAC",
|
|
1129
|
+
"globalConfig": {
|
|
1130
|
+
"ipAddress": "",
|
|
1131
|
+
"networkServer": "tts",
|
|
1132
|
+
"grpcApiKey": "",
|
|
1133
|
+
"serverAddress": "",
|
|
1134
|
+
"grpcPort": "8080",
|
|
1135
|
+
"protocol": "bacnet",
|
|
1136
|
+
"model": "distechControlsV2",
|
|
1137
|
+
"bacnetLogin": "",
|
|
1138
|
+
"bacnetPassword": ""
|
|
1139
|
+
},
|
|
1140
|
+
"deviceCount": 1,
|
|
1141
|
+
"arrayDeviceList": [],
|
|
1142
|
+
"deviceList": {},
|
|
1143
|
+
"x": 300,
|
|
1144
|
+
"y": 300,
|
|
1145
|
+
"wires": [
|
|
1146
|
+
[
|
|
1147
|
+
"5338fce50ecc57d9",
|
|
1148
|
+
"6004507d410bec08"
|
|
1149
|
+
]
|
|
1150
|
+
]
|
|
1151
|
+
}
|
|
1152
|
+
]
|