@bitpoolos/edge-bacnet 1.6.0 → 1.6.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/CHANGELOG.md CHANGED
@@ -1,5 +1,40 @@
1
1
  # Changelog
2
2
 
3
+ ## [1.6.2] - 07-05-2025
4
+
5
+ Minor feature:
6
+ - Added "Enable device discovery" check box to gateway settings, discovery tab.
7
+ - This check box controls whether on not the auto point discovery and property discovery is enabled.
8
+ - This can be used to turn off unecessary network traffic once you have discovered all of the desired devices and points.
9
+ - IMPORTANT - if you are updating a existing deployment, the new property will be unticked, however new installs / dragging from the pallete will by default have the option checked. So if you want to keep this enabled on an existing deployment, please check the setting.
10
+ - Note - This does not turn off the whoIs task schedule.
11
+ - A user can use Right Click -> Update points on desired deviced in the read node tree if you would like to manually discover devices.
12
+
13
+ Minor update:
14
+ - Adjusted initial whoIs broadcast delay from 5seconds to 15seconds after node-red is started with a deployed gateway. This is to ensure large cached files are completely read and loaded.
15
+
16
+ Bug fixes:
17
+ - Inspector:
18
+ - Table resized to avoid scroll bars
19
+ - Percentage rounding to nearest 2 decimal places rather than whole integer for more detailed data.
20
+ - Loading animation added
21
+ - NAN years ago - removed as invalid time differential
22
+ - Last seen for device points adjusted to be more accurate
23
+
24
+
25
+
26
+ ## [1.6.1] - 14-04-2025
27
+
28
+ Bug fixes:
29
+ - Inspector stats were calculating incorrectly in certain scenarios
30
+ - Inspector downloaded HTML files had incorrect stat percentages
31
+ - Inspector Last_Polled_Time stat was always "UNKNOWN", now shows correct date time.
32
+
33
+ Minor updates:
34
+ - Inspector BACnet main stats now output on msg.type = getBacnetStats inject. Can be set on a scheduled inject.
35
+ - Updated Examples with Inspector
36
+
37
+
3
38
  ## [1.6.0] - 09-04-2025
4
39
 
5
40
  New features:
package/bacnet_client.js CHANGED
@@ -52,6 +52,7 @@ class BacnetClient extends EventEmitter {
52
52
  that.deviceRetryCount = parseInt(config.retries);
53
53
  that.sanitise_device_schedule = config.sanitise_device_schedule;
54
54
  that.buildTreeException = false;
55
+ that.enable_device_discovery = config.enable_device_discovery;
55
56
 
56
57
  that.readPropertyMultipleOptions = {
57
58
  maxSegments: 112,
@@ -80,11 +81,11 @@ class BacnetClient extends EventEmitter {
80
81
 
81
82
  //query device task
82
83
  const queryDevices = new Task("simple task", () => {
83
- if (!that.pollInProgress) {
84
+ if (!that.pollInProgress && that.enable_device_discovery) {
84
85
  that.queryDevices();
85
86
  }
86
87
 
87
- if (!that.buildJsonInProgress) {
88
+ if (!that.buildJsonInProgress && that.enable_device_discovery) {
88
89
  that.buildJsonTree();
89
90
  }
90
91
  });
@@ -106,10 +107,15 @@ class BacnetClient extends EventEmitter {
106
107
  setTimeout(() => {
107
108
  that.globalWhoIs();
108
109
  setTimeout(() => {
109
- that.queryDevices();
110
- that.buildJsonTree();
110
+ if (!that.pollInProgress && that.enable_device_discovery) {
111
+ that.queryDevices();
112
+ }
113
+
114
+ if (!that.buildJsonInProgress && that.enable_device_discovery) {
115
+ that.buildJsonTree();
116
+ }
111
117
  }, "4000");
112
- }, "5000");
118
+ }, "15000");
113
119
 
114
120
  } catch (e) {
115
121
  that.logOut("Issue initializing client: ", e);
@@ -615,6 +621,7 @@ class BacnetClient extends EventEmitter {
615
621
  that.deviceId = config.deviceId;
616
622
  that.broadCastAddr = config.broadCastAddr;
617
623
  that.device_read_schedule = config.device_read_schedule;
624
+ that.enable_device_discovery = config.enable_device_discovery;
618
625
 
619
626
  if (that.scheduler !== null) {
620
627
  that.scheduler.stop();
@@ -640,11 +647,11 @@ class BacnetClient extends EventEmitter {
640
647
 
641
648
  // //query device task
642
649
  const queryDevices = new Task("simple task", () => {
643
- if (!that.pollInProgress) {
650
+ if (!that.pollInProgress && that.enable_device_discovery) {
644
651
  that.queryDevices();
645
652
  }
646
653
 
647
- if (!that.buildJsonInProgress) {
654
+ if (!that.buildJsonInProgress && that.enable_device_discovery) {
648
655
  that.buildJsonTree();
649
656
  }
650
657
  });
@@ -177,6 +177,7 @@
177
177
  sanitise_device_schedule: { value: "60", required: false },
178
178
  sanitise_device_schedule_value: { value: "1", required: false },
179
179
  sanitise_device_schedule_options: { value: "Hours", required: false },
180
+ enable_device_discovery: { value: true, required: true },
180
181
  },
181
182
  networkInterfaces: [],
182
183
  inputs: 1,
@@ -985,6 +986,11 @@
985
986
  </select>
986
987
  </div>
987
988
 
989
+ <div class="form-row bp-checkbox-row">
990
+ <input type="checkbox" id="node-input-enable_device_discovery" class="bp-checkbox" />
991
+ <label for="node-input-enable_device_discovery"> Enable device discovery </label>
992
+ </div>
993
+
988
994
  <div class="form-row bp-checkbox-row">
989
995
  <input type="checkbox" id="node-input-cacheFileEnabled" class="bp-checkbox" />
990
996
  <label for="node-input-cacheFileEnabled"> Enable cache file </label>
@@ -1100,6 +1106,7 @@
1100
1106
  the this bacnet client will enter into manual discovery mode, where it iterates through types and instnace ranges. This
1101
1107
  range can be used to limit this manual scanning
1102
1108
  </li>
1109
+ <li>Enable device discovery - toggles whether automatic device discovery, object scanning, and tree building is enabled.</li>
1103
1110
  <li>Log Device Found - toggles logging of found devices to the node-red debug tab.</li>
1104
1111
  <li>Log BACnet Errors to Console - toggles logging of BACnet related errors to the node-red console</li>
1105
1112
  </ul>
package/bacnet_gateway.js CHANGED
@@ -37,6 +37,7 @@ module.exports = function (RED) {
37
37
  this.portRangeRegisters = config.portRangeRegisters;
38
38
  this.cacheFileEnabled = config.cacheFileEnabled;
39
39
  this.sanitise_device_schedule = config.sanitise_device_schedule;
40
+ this.enable_device_discovery = config.enable_device_discovery;
40
41
 
41
42
  //client and config store
42
43
  this.bacnetConfig = nodeContext.get("bacnetConfig");
@@ -65,7 +66,8 @@ module.exports = function (RED) {
65
66
  node.retries,
66
67
  node.cacheFileEnabled,
67
68
  node.sanitise_device_schedule,
68
- node.portRangeRegisters.filter((ele) => ele.enabled === true)
69
+ node.portRangeRegisters.filter((ele) => ele.enabled === true),
70
+ node.enable_device_discovery
69
71
  );
70
72
 
71
73
  if (typeof node.bacnetClient !== "undefined") {
@@ -263,7 +265,7 @@ module.exports = function (RED) {
263
265
  } else if (msg.doUpdatePriorityDevices == true && msg.priorityDevices !== null) {
264
266
  node.bacnetClient
265
267
  .updatePriorityQueue(msg.priorityDevices)
266
- .then(function (result) {})
268
+ .then(function (result) { })
267
269
  .catch(function (error) {
268
270
  logOut("Error updating priorityQueue: ", error);
269
271
  });
@@ -277,7 +279,7 @@ module.exports = function (RED) {
277
279
 
278
280
  node.bacnetClient
279
281
  .applyDisplayNames(msg.pointsToRead)
280
- .then(function (result) {})
282
+ .then(function (result) { })
281
283
  .catch(function (error) {
282
284
  logOut("Error in applyDisplayNames: ", error);
283
285
  });
@@ -115,10 +115,6 @@ module.exports = function (RED) {
115
115
  this.batchInterval = setInterval(processBatch, BATCH_PROCESS_INTERVAL);
116
116
  this.syncInterval = setInterval(syncWithFlowContext, SYNC_INTERVAL);
117
117
 
118
- const debouncedGetBacnetStats = debounce((node, context, msg) => {
119
- getModelStats();
120
- }, 3000);
121
-
122
118
  // Function to sync cache to flow context
123
119
  function syncWithFlowContext() {
124
120
  const flow = context.flow;
@@ -187,7 +183,7 @@ module.exports = function (RED) {
187
183
 
188
184
  node.on("input", function (msg, send, done) {
189
185
  if (msg.type === "getBacnetStats") {
190
- processMessage(msg, send, done);
186
+ getPublishedPointsList();
191
187
  } else if (msg.type === "Read") {
192
188
  calculateCombinedReadList(node, msg);
193
189
  if (done) done();
@@ -197,7 +193,6 @@ module.exports = function (RED) {
197
193
  if (done) done();
198
194
  } else if (msg.payload && msg.topic) {
199
195
  //regular bacnet output
200
- debouncedGetBacnetStats(node, context, msg);
201
196
  // Queue the message for batch processing instead of immediate processing
202
197
  messageQueue.push({ msg, send, done });
203
198
  if (messageQueue.length >= MAX_BATCH_SIZE) {
@@ -218,6 +213,7 @@ module.exports = function (RED) {
218
213
  }
219
214
  if (done) done();
220
215
  });
216
+
221
217
  } else if (msg.reset === true) {
222
218
  node.status({ text: "Resetting..." });
223
219
 
@@ -249,17 +245,6 @@ module.exports = function (RED) {
249
245
  }
250
246
  });
251
247
 
252
- // Process individual messages that shouldn't be batched
253
- function processMessage(msg, send, done) {
254
- try {
255
- getBacnetStats(node, context, msg);
256
- if (done) done();
257
- } catch (error) {
258
- console.error("Error processing message:", error);
259
- if (done) done(error);
260
- }
261
- }
262
-
263
248
  // Process a batch of messages efficiently
264
249
  function processBatch() {
265
250
  if (messageQueue.length === 0) return;
@@ -293,6 +278,10 @@ module.exports = function (RED) {
293
278
 
294
279
  // Use cached data instead of flow context
295
280
  const now = new Date();
281
+ const flow = context.flow;
282
+
283
+ // Set the last point pushed time
284
+ flow.set("Last_Point_Pushed_Time", now.toISOString());
296
285
 
297
286
  // Process all messages in the batch
298
287
  messages.forEach((msg) => {
@@ -449,6 +438,13 @@ module.exports = function (RED) {
449
438
 
450
439
  // Update the node status
451
440
  node.status({ text: "Points Online: " + cachedData.onlineCount + "/" + cachedData.totalUniquePolledCount });
441
+
442
+ // Periodically call getModelStats to keep model stats updated
443
+ // Use a debounce pattern to avoid calling it too frequently
444
+ if (!processBatchData.lastModelStatsUpdate || (now - processBatchData.lastModelStatsUpdate) > 3000) {
445
+ getModelStats();
446
+ processBatchData.lastModelStatsUpdate = now;
447
+ }
452
448
  }
453
449
 
454
450
  //API Request Handlers Start
@@ -925,146 +921,6 @@ module.exports = function (RED) {
925
921
  return msg;
926
922
  }
927
923
 
928
- //calculates bacnet stats on correctly injected msg to inspector node
929
- //fired on every valid bacnet output
930
- function getBacnetStats(node, context, msg) {
931
- let flow = context.flow;
932
- let now = new Date(); // Create a new Date object
933
-
934
- // Initialize or retrieve the current list of unique topics and the count from the context context
935
- let uniqueTopics = flow.get("uniqueReadTopics") || [];
936
- let topicStatusMap = flow.get("topicStatusMap") || {}; // Store status per topic
937
- let topicDataMap = flow.get("topicDataMap") || {}; // Store presentValue, timestamp, and last value change time per topic
938
- let totalUniqueCount = flow.get("totalUniquePolledCount") || 0;
939
- let onlineCount = flow.get("onlineCount") || 0;
940
- let offlineCount = flow.get("offlineCount") || 0;
941
-
942
- flow.set("site_Name", node.siteName);
943
-
944
- // Calculate the IP address of the edge device
945
- var IP_Add = getIPAddresses(); // Get the IP address of the current device
946
- var IP_Str = "NA";
947
- if (IP_Add[0]) {
948
- IP_Str = IP_Add[0].replace(/\./g, "");
949
- } // If an IP address of the first network adapter exists remove the full stops
950
-
951
- // Get the current message's topic, status, presentValue, and timestamp
952
- let topic = msg.topic;
953
- let status = msg.payload.status;
954
- let error = msg.payload.error;
955
- let presentValue = msg.payload.presentValue;
956
- let timestamp = msg.payload.timestamp;
957
-
958
- // Extract properties only if they exist
959
- let deviceID = msg.payload.meta?.device?.deviceId;
960
- let objectType = msg.payload.meta?.objectId?.type;
961
- let objectInstance = msg.payload.meta?.objectId?.instance;
962
- let pointName = msg.payload?.objectName;
963
-
964
- if (pointName !== undefined) {
965
- pointName = pointName + "_" + getObjectType(objectType) + "_" + objectInstance;
966
- }
967
-
968
- let displayName = msg.payload?.displayName;
969
- let deviceName = msg.payload.meta?.device?.deviceName;
970
-
971
- let ipAddress =
972
- typeof msg.payload.meta?.device?.address === "object"
973
- ? msg.payload.meta.device.address.address
974
- : msg.payload.meta?.device?.address;
975
-
976
- // Only proceed if the status key exists
977
- if (status !== undefined) {
978
- // Check if the topic is already in the unique topics list
979
- if (!uniqueTopics.includes(topic)) {
980
- uniqueTopics.push(topic);
981
- totalUniqueCount++;
982
- }
983
-
984
- // Update the status in the topicStatusMap
985
- if (topicStatusMap[topic] !== status) {
986
- // Adjust counts based on the previous status
987
- if (topicStatusMap[topic] === "online") {
988
- onlineCount--;
989
- } else if (topicStatusMap[topic] === "offline") {
990
- offlineCount--;
991
- }
992
-
993
- // Update with the new status
994
- topicStatusMap[topic] = status;
995
-
996
- // Adjust counts based on the new status
997
- if (status === "online") {
998
- onlineCount++;
999
- } else if (status === "offline") {
1000
- offlineCount++;
1001
- }
1002
- }
1003
-
1004
- // Create an entry in the data map if it doesn't already exist
1005
- if (!topicDataMap[topic]) {
1006
- topicDataMap[topic] = {
1007
- presentValue: presentValue,
1008
- status: status,
1009
- bacnetLastSeen: timestamp,
1010
- lastCOVTime: now.getTime(), // Initialize last value change time
1011
- };
1012
-
1013
- // Add properties conditionally if they exist or are explicitly set to 0
1014
- if (deviceID !== undefined) topicDataMap[topic].deviceID = deviceID;
1015
- if (objectType !== undefined) topicDataMap[topic].objectType = objectType;
1016
- if (objectInstance !== undefined) topicDataMap[topic].objectInstance = objectInstance;
1017
- if (pointName !== undefined) topicDataMap[topic].pointName = pointName;
1018
- if (displayName !== undefined) topicDataMap[topic].displayName = displayName;
1019
- if (deviceName !== undefined) topicDataMap[topic].deviceName = deviceName;
1020
- if (ipAddress !== undefined) topicDataMap[topic].ipAddress = ipAddress;
1021
- if (error !== undefined) topicDataMap[topic].error = error;
1022
- topicDataMap[topic].key =
1023
- topicDataMap[topic].deviceID + ":" + topicDataMap[topic].objectType + ":" + topicDataMap[topic].objectInstance;
1024
- } else {
1025
- // If the entry already exists
1026
-
1027
- if (presentValue != topicDataMap[topic].presentValue) {
1028
- // If the present value has changed
1029
- topicDataMap[topic].lastCOVTime = now.getTime(); // Update last value change time
1030
- }
1031
-
1032
- topicDataMap[topic].presentValue = presentValue; // Update the existing timestamp and present value
1033
- topicDataMap[topic].bacnetLastSeen = timestamp; // Update timestamp
1034
-
1035
- // Update properties conditionally if they exist or are explicitly set to 0
1036
- if (deviceID !== undefined) topicDataMap[topic].deviceID = deviceID;
1037
- if (objectType !== undefined) topicDataMap[topic].objectType = objectType;
1038
- if (objectInstance !== undefined) topicDataMap[topic].objectInstance = objectInstance;
1039
- if (pointName !== undefined) topicDataMap[topic].pointName = pointName;
1040
- if (displayName !== undefined) topicDataMap[topic].displayName = displayName;
1041
- if (deviceName !== undefined) topicDataMap[topic].deviceName = deviceName;
1042
- if (ipAddress !== undefined) topicDataMap[topic].ipAddress = ipAddress;
1043
- if (error !== undefined) topicDataMap[topic].error = error;
1044
- topicDataMap[topic].key =
1045
- topicDataMap[topic].deviceID + ":" + topicDataMap[topic].objectType + ":" + topicDataMap[topic].objectInstance;
1046
- }
1047
- } else {
1048
- node.warn("Status key is missing for topic: " + topic);
1049
- }
1050
-
1051
- let offlinePercentage = (offlineCount / (onlineCount + offlineCount)) * 100;
1052
-
1053
- node.statBlock.offlinePercentage = offlinePercentage;
1054
-
1055
- // Store the updated unique topics, status map, topic data map, and counts back in the context context
1056
- flow.set("uniqueTopics", uniqueTopics);
1057
- flow.set("topicStatusMap", topicStatusMap);
1058
- flow.set("topicDataMap", topicDataMap);
1059
- flow.set("totalUniquePolledCount", totalUniqueCount);
1060
- flow.set("onlineCount", onlineCount);
1061
- flow.set("offlineCount", offlineCount);
1062
- flow.set("offlinePercentage", offlinePercentage);
1063
-
1064
- // Update the node status
1065
- node.status({ text: "Points Online: " + onlineCount + "/" + totalUniqueCount });
1066
- }
1067
-
1068
924
  function makeGetRequest(route) {
1069
925
  return new Promise((resolve, reject) => {
1070
926
  try {
@@ -3,10 +3,10 @@ const { Worker } = require("worker_threads");
3
3
  const path = require("path");
4
4
 
5
5
  function getRelativeTime(timestamp) {
6
- if (!timestamp) return "N/A";
6
+ if (!timestamp || isNaN(timestamp) || typeof timestamp !== 'number') return "N/A";
7
7
  let now = Date.now();
8
8
  let timeDiff = now - timestamp;
9
- if (timeDiff < 0) return "In the future";
9
+ if (isNaN(timeDiff) || timeDiff < 0) return "Invalid timestamp";
10
10
 
11
11
  let seconds = Math.floor(timeDiff / 1000);
12
12
  let minutes = Math.floor(seconds / 60);
@@ -367,7 +367,7 @@ function processModelStats(data) {
367
367
  }
368
368
 
369
369
  // Update statBlock
370
- statBlock.missing = pointMatchDiscoveryNotPublishingCount;
370
+ statBlock.missing = pointNotInDiscoveryCount;
371
371
  statBlock.warnings = pointMatchDiscoveryNotPublishingCount;
372
372
  statBlock.unmapped = unmappedCount;
373
373
 
package/common.js CHANGED
@@ -65,7 +65,8 @@ class BacnetClientConfig {
65
65
  retries,
66
66
  cacheFileEnabled,
67
67
  sanitise_device_schedule,
68
- portRangeMatrix
68
+ portRangeMatrix,
69
+ enable_device_discovery
69
70
  ) {
70
71
  this.apduTimeout = apduTimeout;
71
72
  this.localIpAdrress = localIpAdrress;
@@ -84,6 +85,7 @@ class BacnetClientConfig {
84
85
  this.cacheFileEnabled = cacheFileEnabled;
85
86
  this.sanitise_device_schedule = sanitise_device_schedule;
86
87
  this.portRangeMatrix = this.generatePortRangeArray(portRangeMatrix);
88
+ this.enable_device_discovery = enable_device_discovery;
87
89
  }
88
90
 
89
91
  generatePortRangeArray(rangeMatrix) {
@@ -255,7 +257,7 @@ async function Store_Config(data) {
255
257
  JSON.parse(tempContent);
256
258
  } catch (verifyError) {
257
259
  console.error("Temporary file validation failed:", verifyError);
258
- await fs2.unlink(tempFile).catch(() => {});
260
+ await fs2.unlink(tempFile).catch(() => { });
259
261
  return false;
260
262
  }
261
263
 
@@ -275,8 +277,8 @@ async function Store_Config(data) {
275
277
 
276
278
  // Cleanup temporary file if it exists
277
279
  try {
278
- await fs2.unlink(tempFile).catch(() => {});
279
- } catch (cleanupError) {}
280
+ await fs2.unlink(tempFile).catch(() => { });
281
+ } catch (cleanupError) { }
280
282
 
281
283
  // If main file is corrupted and backup exists, restore from backup
282
284
  try {
@@ -396,7 +398,7 @@ async function Store_Config_Server(data) {
396
398
  //console.log("Store_Config_Server writeFile error: ", err);
397
399
  }
398
400
  });
399
- } catch (err) {}
401
+ } catch (err) { }
400
402
  }
401
403
 
402
404
  // READ CONFIG SYNC FUNCTION - BACNET SERVER ======================================
@@ -1 +1,170 @@
1
- [{"id":"a19aaf3c40f060de","type":"Bacnet-Gateway","z":"1bc98a48ab9b3af5","name":"","local_device_address":"","apduTimeout":6000,"roundDecimal":2,"local_device_port":47808,"apduSize":"5","maxSegments":"0x50","retries":"5","broadCastAddr":"255.255.255.255","toLogIam":true,"discover_polling_schedule":"120","discover_polling_schedule_value":"2","discover_polling_schedule_options":"Minutes","device_id_range_enabled":false,"device_id_range_start":"","device_id_range_end":"","deviceId":"817001","manual_instance_range_enabled":false,"manual_instance_range_start":"","manual_instance_range_end":"","logErrorToConsole":false,"serverEnabled":false,"device_read_schedule":"60","device_read_schedule_value":"1","device_read_schedule_options":"Minutes","x":660,"y":420,"wires":[["9d8b6ea1f9d11977"]]},{"id":"3e5f5a6efe7bf8cd","type":"Bitpool-Inject","z":"1bc98a48ab9b3af5","name":"Discover","props":[{"p":"payload"},{"p":"topic","vt":"str"}],"repeat":"","crontab":"","once":false,"onceDelay":0.1,"topic":"","payload":"","payloadType":"date","doPoll":false,"doDiscover":true,"x":480,"y":420,"wires":[["a19aaf3c40f060de"]]},{"id":"1774359e5a636b58","type":"Bitpool-Inject","z":"1bc98a48ab9b3af5","name":"Poll","props":[{"p":"payload"},{"p":"topic","vt":"str"}],"repeat":"","crontab":"","once":false,"onceDelay":0.1,"topic":"","payload":"","payloadType":"date","doPoll":true,"doDiscover":false,"x":310,"y":360,"wires":[["bfec7e0b535bef97"]]},{"id":"9d8b6ea1f9d11977","type":"debug","z":"1bc98a48ab9b3af5","name":"debug 1","active":true,"tosidebar":true,"console":false,"tostatus":false,"complete":"true","targetType":"full","statusVal":"","statusType":"auto","x":840,"y":420,"wires":[]},{"id":"bfec7e0b535bef97","type":"Bacnet-Discovery","z":"1bc98a48ab9b3af5","name":"","events":true,"json":false,"mqtt":true,"hiddenDeployToggle":false,"prevHiddenToggleState":false,"roundDecimal":2,"pointsToRead":{},"readDevices":[],"object_property_simplePayload":true,"object_property_fullObject":false,"x":490,"y":360,"wires":[["a19aaf3c40f060de"]]}]
1
+ [
2
+ {
3
+ "id": "ba92a36a18ffdfac",
4
+ "type": "Bacnet-Gateway",
5
+ "z": "f1fc21f44027188c",
6
+ "name": "",
7
+ "local_device_address": "",
8
+ "local_interface_name": "",
9
+ "apduTimeout": 6000,
10
+ "roundDecimal": 2,
11
+ "local_device_port": 47808,
12
+ "apduSize": "5",
13
+ "maxSegments": "0x50",
14
+ "retries": "5",
15
+ "broadCastAddr": "255.255.255.255",
16
+ "toLogIam": false,
17
+ "discover_polling_schedule": "900",
18
+ "discover_polling_schedule_value": "15",
19
+ "discover_polling_schedule_options": "Minutes",
20
+ "deviceId": 817001,
21
+ "logErrorToConsole": false,
22
+ "serverEnabled": true,
23
+ "device_read_schedule": "900",
24
+ "device_read_schedule_value": "15",
25
+ "device_read_schedule_options": "Minutes",
26
+ "deviceRangeRegisters": [
27
+ {
28
+ "enabled": true,
29
+ "start": "0",
30
+ "end": "4194303"
31
+ }
32
+ ],
33
+ "portRangeRegisters": [
34
+ {
35
+ "enabled": true,
36
+ "start": "47808",
37
+ "end": "47808"
38
+ }
39
+ ],
40
+ "cacheFileEnabled": false,
41
+ "sanitise_device_schedule": "3600",
42
+ "sanitise_device_schedule_value": "1",
43
+ "sanitise_device_schedule_options": "Hours",
44
+ "x": 800,
45
+ "y": 460,
46
+ "wires": [
47
+ [
48
+ "0688901dadf6d984"
49
+ ]
50
+ ]
51
+ },
52
+ {
53
+ "id": "c404d53530d50ce9",
54
+ "type": "Bacnet-Discovery",
55
+ "z": "f1fc21f44027188c",
56
+ "name": "",
57
+ "events": true,
58
+ "json": true,
59
+ "mqtt": false,
60
+ "pointJson": false,
61
+ "hiddenDeployToggle": false,
62
+ "prevHiddenToggleState": false,
63
+ "roundDecimal": 2,
64
+ "pointsToRead": {},
65
+ "readDevices": [],
66
+ "object_property_simplePayload": false,
67
+ "object_property_simpleWithStatus": false,
68
+ "object_property_fullObject": true,
69
+ "useDeviceName": true,
70
+ "x": 630,
71
+ "y": 400,
72
+ "wires": [
73
+ [
74
+ "ba92a36a18ffdfac"
75
+ ]
76
+ ]
77
+ },
78
+ {
79
+ "id": "d89267e068e24882",
80
+ "type": "Bitpool-Inject",
81
+ "z": "f1fc21f44027188c",
82
+ "name": "Poll",
83
+ "props": [
84
+ {
85
+ "p": "payload"
86
+ },
87
+ {
88
+ "p": "topic",
89
+ "vt": "str"
90
+ }
91
+ ],
92
+ "repeat": "",
93
+ "crontab": "",
94
+ "once": false,
95
+ "onceDelay": 0.1,
96
+ "topic": "",
97
+ "payload": "",
98
+ "payloadType": "date",
99
+ "doPoll": true,
100
+ "doDiscover": false,
101
+ "json": false,
102
+ "mqtt": false,
103
+ "pointJson": true,
104
+ "object_property_simplePayload": false,
105
+ "object_property_simpleWithStatus": false,
106
+ "object_property_fullObject": true,
107
+ "useDeviceName": true,
108
+ "x": 470,
109
+ "y": 400,
110
+ "wires": [
111
+ [
112
+ "c404d53530d50ce9"
113
+ ]
114
+ ]
115
+ },
116
+ {
117
+ "id": "5f5537b37018ec25",
118
+ "type": "Bitpool-Inject",
119
+ "z": "f1fc21f44027188c",
120
+ "name": "Discover",
121
+ "props": [
122
+ {
123
+ "p": "payload"
124
+ },
125
+ {
126
+ "p": "topic",
127
+ "vt": "str"
128
+ }
129
+ ],
130
+ "repeat": "",
131
+ "crontab": "",
132
+ "once": false,
133
+ "onceDelay": 0.1,
134
+ "topic": "",
135
+ "payload": "",
136
+ "payloadType": "date",
137
+ "doPoll": false,
138
+ "doDiscover": true,
139
+ "json": false,
140
+ "mqtt": false,
141
+ "pointJson": true,
142
+ "object_property_simplePayload": false,
143
+ "object_property_simpleWithStatus": false,
144
+ "object_property_fullObject": true,
145
+ "useDeviceName": true,
146
+ "x": 620,
147
+ "y": 460,
148
+ "wires": [
149
+ [
150
+ "ba92a36a18ffdfac"
151
+ ]
152
+ ]
153
+ },
154
+ {
155
+ "id": "0688901dadf6d984",
156
+ "type": "debug",
157
+ "z": "f1fc21f44027188c",
158
+ "name": "debug 1",
159
+ "active": true,
160
+ "tosidebar": true,
161
+ "console": false,
162
+ "tostatus": false,
163
+ "complete": "false",
164
+ "statusVal": "",
165
+ "statusType": "auto",
166
+ "x": 1000,
167
+ "y": 460,
168
+ "wires": []
169
+ }
170
+ ]