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 ADDED
@@ -0,0 +1,2 @@
1
+ # Auto detect text files and perform LF normalization
2
+ * text=auto
@@ -0,0 +1,64 @@
1
+ # Contributing to node-red-contrib-lorawan-bacnet
2
+
3
+ ## Prerequisites
4
+
5
+ - Docker installed and running
6
+ - Git installed
7
+ - Terminal access with necessary permissions
8
+
9
+ ## Clone the Repository
10
+
11
+ Start by cloning the repository locally:
12
+
13
+ ```bash
14
+ git clone https://github.com/SylvainMontagny/node-red-contrib-lorawan-bacnet.git
15
+ cd node-red-contrib-lorawan-bacnet
16
+ ```
17
+
18
+ ## Run the Docker Container with the Code Mounted as a Volume
19
+
20
+ Use the `montagny/node-red` image to start a container with your local code mounted:
21
+
22
+ ```bash
23
+ docker run -d \
24
+ --name node-red-lorawan-bacnet \
25
+ -p 1880:1880 \
26
+ -v $(pwd):/data/node-red-contrib-lorawan-bacnet \
27
+ -u root \
28
+ montagny/node-red
29
+ ```
30
+
31
+ > Replace `$(pwd)` with the full path if you’re not running the command from the project directory.
32
+
33
+ ## Link the Palette Locally with `npm link`
34
+
35
+ Once the container is running, open a shell inside:
36
+
37
+ ```bash
38
+ docker exec -it node-red-lorawan-bacnet bash
39
+ ```
40
+
41
+ Inside the container, run the following:
42
+
43
+ ```bash
44
+ npm uninstall @montagny/node-red-contrib-lorawan-bacnet
45
+ npm link /data/node-red-contrib-lorawan-bacnet
46
+ ```
47
+
48
+ This allows Node-RED to use the development version of the palette directly from the mounted volume.
49
+
50
+ ## Restart Node-RED
51
+
52
+ Restart the container from the host machine:
53
+
54
+ ```bash
55
+ docker restart node-red-lorawan-bacnet
56
+ ```
57
+
58
+ ## Testing
59
+
60
+ Once linked, open the Node-RED editor in your browser:
61
+
62
+ [http://localhost:1880](http://localhost:1880)
63
+
64
+ You can now test your changes directly from the editor, you have to restart container to apply changes.
package/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2025 elias-qzo
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/README.md ADDED
@@ -0,0 +1,111 @@
1
+ # LoRaBAC, an open-source LoRaWAN to BACnet interface
2
+ The [LoRaBAC node](https://flows.nodered.org/node/@montagny/node-red-contrib-lorawan-bacnet) is the configuration node of the [LoRaBAC application](https://github.com/SylvainMontagny/LoRaBAC).
3
+
4
+
5
+ - [LoRaBAC, an open-source LoRaWAN to BACnet interface](#lorabac-an-open-source-lorawan-to-bacnet-interface)
6
+ - [**1. Overview**](#1-overview)
7
+ - [**1.1. What is LoRaBAC?**](#11-what-is-lorabac)
8
+ - [**Key Features:**](#key-features)
9
+ - [**1.2. What Makes LoRaBAC Unique?**](#12-what-makes-lorabac-unique)
10
+ - [**Drawbacks:**](#drawbacks)
11
+ - [**1.3. Support**](#13-support)
12
+ - [**2. Prerequisites**](#2-prerequisites)
13
+ - [**2.1. LoRaWAN End-Device**](#21-lorawan-end-device)
14
+ - [**2.2. MQTT Broker**](#22-mqtt-broker)
15
+ - [**2.3. Node-RED Setup**](#23-node-red-setup)
16
+ - [**Additional Packages for ChirpStackV4:**](#additional-packages-for-chirpstackv4)
17
+ - [**Quick Start with Docker:**](#quick-start-with-docker)
18
+ - [**3. Getting Started**](#3-getting-started)
19
+
20
+ ## **1. Overview**
21
+ ### **1.1. What is LoRaBAC?**
22
+ LoRaBAC is open-source application built on **Node-RED**. It allows you to integrate LoRaWAN devices with BACnet controllers, making it ideal for **smart building applications**.
23
+
24
+ ![LoRaBAC](images/lorabac.png)
25
+ #### **Key Features:**
26
+ - **Universal Compatibility:**
27
+ - [x] Works with **all LoRaWAN end-devices** and **gateways**.
28
+ - [x] Supports **ChirpStack**, **Actility**, and **The Things Stack** (The Things Network / The Things Industries) Network Servers.
29
+ - [x] Integrates with **all BACnet controllers** using native BACnet protocols (or **Distech-Controls controllers** when using Rest API).
30
+
31
+ - **Bidirectional Communication:**
32
+ - [x] **Uplink:** Writes LoRaWAN payloads to specific BACnet objects.
33
+ - [x] **Downlink:** Writes BACnet objects to specific LoRaWAN payload.
34
+
35
+ - **Proven Use Cases:**
36
+ - [x] Thermostatic valves
37
+ - [x] Temperature and humidity sensors
38
+ - [x] Air quality sensors
39
+ - [x] Current monitoring sensors
40
+ - [x] Pilot wired electric heater controller
41
+
42
+
43
+ ### **1.2. What Makes LoRaBAC Unique?**
44
+ LoRaBAC is designed with a **different approach** compared to other LoRaWAN-BACnet interfaces. Here’s why it stands out:
45
+
46
+ **Advantages** :
47
+
48
+ 1. **BACnet Client Architecture:**
49
+ - Unlike most interfaces that act as **BACnet servers**, LoRaBAC operates as a **BACnet client**. It only interacts with the controller when a LoRaWAN payload is received, reducing unnecessary traffic.
50
+
51
+ 2. **Flexible Deployment:**
52
+ - LoRaBAC can be installed **anywhere**:
53
+ - On the **LoRaWAN Gateway**
54
+ - Within the **local network**
55
+ - Directly on the **BACnet controller**
56
+
57
+ 3. **Open Source and Free:**
58
+ - LoRaBAC is **free to use** and **open-source** under the **MIT License**, offering full transparency and customization.
59
+
60
+ #### **Drawbacks:**
61
+ 1. **No "Who-is" Service Support:**
62
+ - LoRaBAC does not respond to the **"Who-is" service**, which can be useful for discovering BACnet devices in some setups.
63
+
64
+ 2. **Manual Configuration Required:**
65
+ - Each new LoRaWAN device type requires **manual configuration**. It is not a "Plug and Play" solution.
66
+
67
+ ### **1.3. Support**
68
+ To get support on LoRaWAN or LoRaBAC, please refers to the following ressources, or [reach us](https://www.univ-smb.fr/lorawan/en/contact/) out.
69
+
70
+ :tv: Webinar Replay: [LoRaWAN and BACnet interfaces for Smart Building]()
71
+
72
+ :notebook: Free ebook:[LoRaWAN for beginers books](https://www.univ-smb.fr/lorawan/en/free-book/)
73
+
74
+ :tv: E-learning platform: [LoRaWAN for beginers videos](https://www.udemy.com/course/lora-lorawan-internet-of-things/?referralCode=21DED0F1021F4E261955)
75
+
76
+ :tv: E-learning platform for Advanced users: [LoRaWAN for Advanced users videos](https://www.udemy.com/course/lorawan-for-advanced-users/?referralCode=BA4A670560916E1AED77)
77
+
78
+ :bulb: 2 days training sessions: [LoRaWAN and IoT Training](https://www.univ-smb.fr/lorawan/avada_portfolio/formation-distanciel/)
79
+
80
+ ## **2. Prerequisites**
81
+ ### **2.1. LoRaWAN End-Device**
82
+ To use LoRaBAC you will need:
83
+ - a LoRaWAN **Network Server** (ChirpStack, TTN or Actility).
84
+ - The device **payload decoders** (for uplink).
85
+ - the device **payload encoders** (for downlink, if needed).
86
+
87
+ Payload decoder and encoders should be provided by your device manufacturer, however, in this repository you will find the payload codec of the tested devices.
88
+
89
+
90
+ ### **2.2. MQTT Broker**
91
+ LoRaBAC relies on an **MQTT broker** for communication. You can use:
92
+ - The built-in MQTT broker provided by **ChirpStack**, **Actility**, or **The Things Stack**.
93
+ - Your own **custom MQTT broker**.
94
+
95
+
96
+ ### **2.3. Node-RED Setup**
97
+ LoRaBAC is a **Node-RED flow**, so you need a **Node-RED instance** to run it. Ensure the following packages are installed:
98
+
99
+ - @montagny/node-red-contrib-lorawan-bacnet
100
+
101
+ #### **Additional Packages for ChirpStackV4:**
102
+ If you’re using **ChirpStackV4** and want to enable the **"Flush Downlink Queue"** feature, install:
103
+ - `@grpc/grpc-js`
104
+ - `@chirpstack/chirpstack-api`
105
+
106
+ #### **Quick Start with Docker:**
107
+ A pre-configured **Node-RED Docker image** is available on [Docker Hub](https://hub.docker.com/r/montagny/node-red/tags). It includes all required packages for easy deployment.
108
+
109
+
110
+ ## **3. Getting Started**
111
+ Please, follow the information given in the [LoRaBAC application](https://github.com/SylvainMontagny/LoRaBAC).
@@ -0,0 +1,401 @@
1
+ [
2
+ {
3
+ "id": "bacnet-server-example-flow",
4
+ "type": "tab",
5
+ "label": "BACnet Server Example",
6
+ "disabled": false,
7
+ "info": "# BACnet Server Example\n\nThis flow demonstrates how to use the BACnet Server functionality to expose Node-RED as a BACnet device on the network.\n\n## Components\n\n1. **BACnet Server** (configuration node): Manages the BACnet device identity and handles Who-Is/I-Am discovery.\n\n2. **BACnet Points**: Individual objects exposed to BACnet clients:\n - Temperature (Analog Value): Simulated temperature sensor\n - Humidity (Analog Value): Simulated humidity sensor\n - Fan Status (Binary Value): Controllable fan state\n - Operating Mode (Multi-State Value): HVAC mode selector\n\n3. **Simulators**: Inject nodes that simulate sensor data updates.\n\n4. **Output handlers**: Process writes from external BACnet clients.\n\n## Testing\n\n1. Install YABE (Yet Another BACnet Explorer) or a similar BACnet client\n2. Deploy this flow\n3. Scan the network - the Node-RED device should appear\n4. Read the exposed points\n5. Write to writable points and observe the debug output"
8
+ },
9
+ {
10
+ "id": "bacnet-server-config-1",
11
+ "type": "bacnet-server",
12
+ "name": "Node-RED BACnet Device",
13
+ "deviceId": "123456",
14
+ "deviceName": "Node-RED-BACnet",
15
+ "vendorId": "999",
16
+ "port": "47808",
17
+ "interface": "",
18
+ "broadcastAddress": "255.255.255.255"
19
+ },
20
+ {
21
+ "id": "bacnet-point-temp",
22
+ "type": "bacnet-point",
23
+ "z": "bacnet-server-example-flow",
24
+ "name": "Temperature",
25
+ "server": "bacnet-server-config-1",
26
+ "objectType": "analogValue",
27
+ "instanceNumber": "1",
28
+ "objectName": "Room_Temperature",
29
+ "initialValue": "21.5",
30
+ "units": "62",
31
+ "description": "Room temperature sensor",
32
+ "writable": false,
33
+ "usePriorityArray": false,
34
+ "relinquishDefault": "0",
35
+ "covIncrement": "0.5",
36
+ "numberOfStates": "3",
37
+ "stateText": "",
38
+ "outputOnRead": true,
39
+ "outputOnWrite": false,
40
+ "outputOnCov": false,
41
+ "x": 430,
42
+ "y": 100,
43
+ "wires": [
44
+ ["debug-read"]
45
+ ]
46
+ },
47
+ {
48
+ "id": "bacnet-point-humidity",
49
+ "type": "bacnet-point",
50
+ "z": "bacnet-server-example-flow",
51
+ "name": "Humidity",
52
+ "server": "bacnet-server-config-1",
53
+ "objectType": "analogValue",
54
+ "instanceNumber": "2",
55
+ "objectName": "Room_Humidity",
56
+ "initialValue": "45",
57
+ "units": "29",
58
+ "description": "Room humidity sensor",
59
+ "writable": false,
60
+ "usePriorityArray": false,
61
+ "relinquishDefault": "0",
62
+ "covIncrement": "1",
63
+ "numberOfStates": "3",
64
+ "stateText": "",
65
+ "outputOnRead": true,
66
+ "outputOnWrite": false,
67
+ "outputOnCov": false,
68
+ "x": 430,
69
+ "y": 160,
70
+ "wires": [
71
+ ["debug-read"]
72
+ ]
73
+ },
74
+ {
75
+ "id": "bacnet-point-fan",
76
+ "type": "bacnet-point",
77
+ "z": "bacnet-server-example-flow",
78
+ "name": "Fan Status",
79
+ "server": "bacnet-server-config-1",
80
+ "objectType": "binaryValue",
81
+ "instanceNumber": "1",
82
+ "objectName": "Fan_Status",
83
+ "initialValue": "0",
84
+ "units": "",
85
+ "description": "Fan on/off control",
86
+ "writable": true,
87
+ "usePriorityArray": true,
88
+ "relinquishDefault": "0",
89
+ "covIncrement": "0",
90
+ "numberOfStates": "3",
91
+ "stateText": "",
92
+ "outputOnRead": false,
93
+ "outputOnWrite": true,
94
+ "outputOnCov": false,
95
+ "x": 430,
96
+ "y": 240,
97
+ "wires": [
98
+ ["debug-write", "fan-control-handler"]
99
+ ]
100
+ },
101
+ {
102
+ "id": "bacnet-point-mode",
103
+ "type": "bacnet-point",
104
+ "z": "bacnet-server-example-flow",
105
+ "name": "Operating Mode",
106
+ "server": "bacnet-server-config-1",
107
+ "objectType": "multiStateValue",
108
+ "instanceNumber": "1",
109
+ "objectName": "HVAC_Mode",
110
+ "initialValue": "1",
111
+ "units": "",
112
+ "description": "HVAC operating mode",
113
+ "writable": true,
114
+ "usePriorityArray": false,
115
+ "relinquishDefault": "1",
116
+ "covIncrement": "0",
117
+ "numberOfStates": "4",
118
+ "stateText": "Off, Heating, Cooling, Auto",
119
+ "outputOnRead": false,
120
+ "outputOnWrite": true,
121
+ "outputOnCov": false,
122
+ "x": 430,
123
+ "y": 300,
124
+ "wires": [
125
+ ["debug-write", "mode-change-handler"]
126
+ ]
127
+ },
128
+ {
129
+ "id": "bacnet-point-setpoint",
130
+ "type": "bacnet-point",
131
+ "z": "bacnet-server-example-flow",
132
+ "name": "Temperature Setpoint",
133
+ "server": "bacnet-server-config-1",
134
+ "objectType": "analogValue",
135
+ "instanceNumber": "3",
136
+ "objectName": "Temp_Setpoint",
137
+ "initialValue": "22",
138
+ "units": "62",
139
+ "description": "Temperature setpoint",
140
+ "writable": true,
141
+ "usePriorityArray": true,
142
+ "relinquishDefault": "21",
143
+ "covIncrement": "0.5",
144
+ "numberOfStates": "3",
145
+ "stateText": "",
146
+ "outputOnRead": false,
147
+ "outputOnWrite": true,
148
+ "outputOnCov": false,
149
+ "x": 430,
150
+ "y": 360,
151
+ "wires": [
152
+ ["debug-write"]
153
+ ]
154
+ },
155
+ {
156
+ "id": "inject-temp-simulator",
157
+ "type": "inject",
158
+ "z": "bacnet-server-example-flow",
159
+ "name": "Simulate Temperature",
160
+ "props": [
161
+ {
162
+ "p": "payload"
163
+ }
164
+ ],
165
+ "repeat": "10",
166
+ "crontab": "",
167
+ "once": true,
168
+ "onceDelay": "1",
169
+ "topic": "",
170
+ "payload": "$floor($random() * 100) / 10 + 18",
171
+ "payloadType": "jsonata",
172
+ "x": 180,
173
+ "y": 100,
174
+ "wires": [
175
+ ["bacnet-point-temp"]
176
+ ]
177
+ },
178
+ {
179
+ "id": "inject-humidity-simulator",
180
+ "type": "inject",
181
+ "z": "bacnet-server-example-flow",
182
+ "name": "Simulate Humidity",
183
+ "props": [
184
+ {
185
+ "p": "payload"
186
+ }
187
+ ],
188
+ "repeat": "15",
189
+ "crontab": "",
190
+ "once": true,
191
+ "onceDelay": "2",
192
+ "topic": "",
193
+ "payload": "$floor($random() * 30) + 35",
194
+ "payloadType": "jsonata",
195
+ "x": 180,
196
+ "y": 160,
197
+ "wires": [
198
+ ["bacnet-point-humidity"]
199
+ ]
200
+ },
201
+ {
202
+ "id": "debug-read",
203
+ "type": "debug",
204
+ "z": "bacnet-server-example-flow",
205
+ "name": "Read Events",
206
+ "active": true,
207
+ "tosidebar": true,
208
+ "console": false,
209
+ "tostatus": true,
210
+ "complete": "true",
211
+ "targetType": "full",
212
+ "statusVal": "payload",
213
+ "statusType": "auto",
214
+ "x": 650,
215
+ "y": 130,
216
+ "wires": []
217
+ },
218
+ {
219
+ "id": "debug-write",
220
+ "type": "debug",
221
+ "z": "bacnet-server-example-flow",
222
+ "name": "Write Events",
223
+ "active": true,
224
+ "tosidebar": true,
225
+ "console": false,
226
+ "tostatus": true,
227
+ "complete": "true",
228
+ "targetType": "full",
229
+ "statusVal": "payload",
230
+ "statusType": "auto",
231
+ "x": 650,
232
+ "y": 270,
233
+ "wires": []
234
+ },
235
+ {
236
+ "id": "fan-control-handler",
237
+ "type": "function",
238
+ "z": "bacnet-server-example-flow",
239
+ "name": "Fan Control Handler",
240
+ "func": "// Handle fan control command from BACnet client\nif (msg.bacnet && msg.bacnet.event === 'write') {\n const fanState = msg.payload === 1 ? 'ON' : 'OFF';\n node.status({\n fill: msg.payload === 1 ? 'green' : 'grey',\n shape: 'dot',\n text: `Fan ${fanState} (Priority: ${msg.bacnet.priority})`\n });\n \n // Here you could send a command to actual hardware\n // e.g., via MQTT, GPIO, or other Node-RED nodes\n msg.fanCommand = {\n action: fanState,\n priority: msg.bacnet.priority,\n source: msg.bacnet.source,\n timestamp: new Date().toISOString()\n };\n \n return msg;\n}\nreturn null;",
241
+ "outputs": 1,
242
+ "timeout": "",
243
+ "noerr": 0,
244
+ "initialize": "",
245
+ "finalize": "",
246
+ "libs": [],
247
+ "x": 670,
248
+ "y": 330,
249
+ "wires": [
250
+ ["debug-fan-action"]
251
+ ]
252
+ },
253
+ {
254
+ "id": "mode-change-handler",
255
+ "type": "function",
256
+ "z": "bacnet-server-example-flow",
257
+ "name": "Mode Change Handler",
258
+ "func": "// Handle mode change from BACnet client\nif (msg.bacnet && msg.bacnet.event === 'write') {\n const modes = ['Off', 'Heating', 'Cooling', 'Auto'];\n const modeName = modes[msg.payload - 1] || 'Unknown';\n \n node.status({\n fill: 'blue',\n shape: 'dot',\n text: `Mode: ${modeName}`\n });\n \n msg.modeChange = {\n mode: msg.payload,\n modeName: modeName,\n source: msg.bacnet.source,\n timestamp: new Date().toISOString()\n };\n \n return msg;\n}\nreturn null;",
259
+ "outputs": 1,
260
+ "timeout": "",
261
+ "noerr": 0,
262
+ "initialize": "",
263
+ "finalize": "",
264
+ "libs": [],
265
+ "x": 680,
266
+ "y": 390,
267
+ "wires": [
268
+ ["debug-mode-action"]
269
+ ]
270
+ },
271
+ {
272
+ "id": "debug-fan-action",
273
+ "type": "debug",
274
+ "z": "bacnet-server-example-flow",
275
+ "name": "Fan Action",
276
+ "active": true,
277
+ "tosidebar": true,
278
+ "console": false,
279
+ "tostatus": false,
280
+ "complete": "fanCommand",
281
+ "targetType": "msg",
282
+ "statusVal": "",
283
+ "statusType": "auto",
284
+ "x": 890,
285
+ "y": 330,
286
+ "wires": []
287
+ },
288
+ {
289
+ "id": "debug-mode-action",
290
+ "type": "debug",
291
+ "z": "bacnet-server-example-flow",
292
+ "name": "Mode Action",
293
+ "active": true,
294
+ "tosidebar": true,
295
+ "console": false,
296
+ "tostatus": false,
297
+ "complete": "modeChange",
298
+ "targetType": "msg",
299
+ "statusVal": "",
300
+ "statusType": "auto",
301
+ "x": 890,
302
+ "y": 390,
303
+ "wires": []
304
+ },
305
+ {
306
+ "id": "comment-header",
307
+ "type": "comment",
308
+ "z": "bacnet-server-example-flow",
309
+ "name": "BACnet Server Example - Expose Node-RED as a BACnet Device",
310
+ "info": "This flow demonstrates the BACnet Server functionality.\n\nThe bacnet-server configuration node creates a BACnet device\nthat responds to Who-Is requests and allows external clients\nto read and write the exposed points.\n\nUse YABE or another BACnet client to test.",
311
+ "x": 280,
312
+ "y": 40,
313
+ "wires": []
314
+ },
315
+ {
316
+ "id": "comment-sensors",
317
+ "type": "comment",
318
+ "z": "bacnet-server-example-flow",
319
+ "name": "Sensor Simulation (Read-Only Points)",
320
+ "info": "",
321
+ "x": 190,
322
+ "y": 60,
323
+ "wires": []
324
+ },
325
+ {
326
+ "id": "comment-controls",
327
+ "type": "comment",
328
+ "z": "bacnet-server-example-flow",
329
+ "name": "Control Points (Writable)",
330
+ "info": "",
331
+ "x": 150,
332
+ "y": 200,
333
+ "wires": []
334
+ },
335
+ {
336
+ "id": "inject-manual-fan-on",
337
+ "type": "inject",
338
+ "z": "bacnet-server-example-flow",
339
+ "name": "Fan ON (Priority 16)",
340
+ "props": [
341
+ {
342
+ "p": "payload"
343
+ }
344
+ ],
345
+ "repeat": "",
346
+ "crontab": "",
347
+ "once": false,
348
+ "onceDelay": 0.1,
349
+ "topic": "",
350
+ "payload": "1",
351
+ "payloadType": "num",
352
+ "x": 180,
353
+ "y": 240,
354
+ "wires": [
355
+ ["bacnet-point-fan"]
356
+ ]
357
+ },
358
+ {
359
+ "id": "inject-manual-fan-off",
360
+ "type": "inject",
361
+ "z": "bacnet-server-example-flow",
362
+ "name": "Fan OFF (Priority 16)",
363
+ "props": [
364
+ {
365
+ "p": "payload"
366
+ }
367
+ ],
368
+ "repeat": "",
369
+ "crontab": "",
370
+ "once": false,
371
+ "onceDelay": 0.1,
372
+ "topic": "",
373
+ "payload": "0",
374
+ "payloadType": "num",
375
+ "x": 180,
376
+ "y": 280,
377
+ "wires": [
378
+ ["bacnet-point-fan"]
379
+ ]
380
+ },
381
+ {
382
+ "id": "comment-integration",
383
+ "type": "comment",
384
+ "z": "bacnet-server-example-flow",
385
+ "name": "Integration Example: LoRaWAN -> BACnet",
386
+ "info": "Connect LoRaBAC output to bacnet-point input\nto expose LoRaWAN sensor data as BACnet points.",
387
+ "x": 200,
388
+ "y": 440,
389
+ "wires": []
390
+ },
391
+ {
392
+ "id": "example-lorawan-input",
393
+ "type": "comment",
394
+ "z": "bacnet-server-example-flow",
395
+ "name": "[LoRaBAC] -> [Function: Extract Temp] -> [bacnet-point: Temperature]",
396
+ "info": "",
397
+ "x": 260,
398
+ "y": 480,
399
+ "wires": []
400
+ }
401
+ ]