@bitpoolos/edge-bacnet 1.6.2 → 1.6.4

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,39 +1,80 @@
1
1
  # Changelog
2
2
 
3
+ ## [1.6.4] - 02-09-2025
4
+
5
+ Minor feature:
6
+
7
+ - Small update to file read / write logic for database files. Some deployments require a different storage location outside of the default. If a nodejs environment variable of BACNET_STORAGE_PATH is defined and set for that deployment, it will prefix that location to the file path, otherwise functionality will remain the same.
8
+
9
+ Bug fix:
10
+
11
+ - Some mstp devices were not showing in device list UI tree.
12
+
13
+ ## [1.6.3] - 01-06-2025
14
+
15
+ Minor feature:
16
+
17
+ - Device List - new right click option to MSTP NET folders - Update All Devices. Specifically updates the mstp device listed in that selected network
18
+ - Inspector - Added statistic percentages as MQTT output in the Inspector node
19
+ - Inspector - Added online / offline stats and Total points to read as status on node
20
+ - Test Functions - Outputs results to both node-red console and node-red debug window now
21
+
22
+ Minor update / refactor:
23
+
24
+ - Datamodel - Importing and Exporting datamodel displays process status and related stats (file size etc) while importing or exporting, informing the user of current state
25
+ - Device List - UI tree no longer stored in datamodel, as it is generated dynamically on a schedule. This significantly reduces datamodel file size, read / write time, system start up processes.
26
+ - Datamodel - writes to file system for persistance and backup was being executed more often than needed. Process schedule is now on a larger interval, and write algorithm refactored and optimized.
27
+ - Device List - right click -> Set Point Name feature refactored, due to many scenarios where it wasnt executing as expected.
28
+ - BACnet read output - error and status field setting optimized
29
+ - Inspector - updated ObjectType column values to show object type enum string instead of integer
30
+
31
+ Bug fixes:
32
+
33
+ - Inspector - incorrect percentages, statistics and values in the statistics bar. Fixed and tested to represent site status more accurately
34
+ - Inpsector - not flagging offline points in error statistic and table filter. Now correctly identifies "offline" points as an Error type
35
+ - BACnet read output - was not updating error and status correctly for full object payloads.
36
+ - BACnet Server - was not outputting sucessfull write update MQTT msg after node-red deploy.
37
+
38
+ Updating:
39
+
40
+ - There shouldnt be a need for users to remove nodes or do any specific action when updating to this version, however backing up a datamodel export is advised.
41
+
3
42
  ## [1.6.2] - 07-05-2025
4
43
 
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.
44
+ Minor feature:
45
+
46
+ - Added "Enable device discovery" check box to gateway settings, discovery tab.
47
+ - This check box controls whether on not the auto point discovery and property discovery is enabled.
48
+ - This can be used to turn off unecessary network traffic once you have discovered all of the desired devices and points.
49
+ - 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.
50
+ - Note - This does not turn off the whoIs task schedule.
51
+ - 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
52
 
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.
53
+ Minor update:
15
54
 
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
55
+ - 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.
23
56
 
24
-
57
+ Bug fixes:
58
+
59
+ - Inspector:
60
+ - Table resized to avoid scroll bars
61
+ - Percentage rounding to nearest 2 decimal places rather than whole integer for more detailed data.
62
+ - Loading animation added
63
+ - NAN years ago - removed as invalid time differential
64
+ - Last seen for device points adjusted to be more accurate
25
65
 
26
66
  ## [1.6.1] - 14-04-2025
27
67
 
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.
68
+ Bug fixes:
69
+
70
+ - Inspector stats were calculating incorrectly in certain scenarios
71
+ - Inspector downloaded HTML files had incorrect stat percentages
72
+ - Inspector Last_Polled_Time stat was always "UNKNOWN", now shows correct date time.
32
73
 
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
74
+ Minor updates:
36
75
 
76
+ - Inspector BACnet main stats now output on msg.type = getBacnetStats inject. Can be set on a scheduled inject.
77
+ - Updated Examples with Inspector
37
78
 
38
79
  ## [1.6.0] - 09-04-2025
39
80
 
@@ -49,93 +90,99 @@ New features:
49
90
  - API routes available:
50
91
 
51
92
  - /inspector
93
+
52
94
  - view UI in web browser
53
95
 
54
96
  - /inspector-downloadhtml
97
+
55
98
  - downloads the html of the inspector in its current state
56
99
 
57
100
  - /inspector-downloadhtml?filter=tableKey&value=value1,value2
58
- - downloads the html of the inspector, but with a applied filter to the table.
59
- - tableKey in the above example can be any of the table columns, only 1 tableKey may be filtered on:
101
+
102
+ - downloads the html of the inspector, but with a applied filter to the table.
103
+ - tableKey in the above example can be any of the table columns, only 1 tableKey may be filtered on:
60
104
  deviceID, objectType, objectInstance, presentValue, dataModelStatus, pointName, discoveredBACnetPointName, displayName, deviceName, ipAddress, area, key, topic, lastSeen, error
61
105
  - value=value1,value2 etc can be any value that the tableKey can contain. This parameter can accept many comma separated values
62
- - an example filter request may look like:
106
+ - an example filter request may look like:
63
107
  /inspector-downloadhtml?filter=dataModelStatus&value=error,missing
64
108
 
65
109
  - /getModelStats
110
+
66
111
  - returns JSON data with analysis of the BACnet model
67
112
  - contains point status, metrics, and detailed information
68
113
 
69
114
  - /pointstoread
115
+
70
116
  - downloads CSV file with all points in the read list
71
117
  - format: [siteName]_PointsToRead_[timestamp].csv
72
118
 
73
119
  - /getpointerrors
74
120
  - downloads CSV file with all points that have errors
75
121
  - format: [siteName]_PointErrors_[timestamp].csv
76
-
77
122
  - /getmodelstatscsv
78
123
  - downloads CSV file with all model stats data in CSV format
79
124
  - format: [siteName]_ModelStats_[timestamp].csv
80
-
81
125
  - /publishedpointslist
126
+
82
127
  - downloads CSV file with all published points and their current values
83
128
  - format: [siteName]_PublishedPointsList_[timestamp].csv
84
129
  - outputs mqtt topic and payloads with statistics about the current state of the bacnet network
85
130
  - output topics:
86
- EDGE_DEVICE_{IP_ID}/STATUS/LAST_POINT_PUSHED_TIME
87
- payload: Timestamp of when the last point was pushed (ISO string)
88
- EDGE_DEVICE_{IP_ID}/STATUS/LAST_STAT_CALC_TIME
89
- payload: Current timestamp (ISO string)
90
- EDGE_DEVICE_{IP_ID}/STATUS/UPTIME
91
- payload: System uptime formatted as string (e.g., "Uptime: 3 days, 5 hours, 12 minutes, 45 seconds")
92
- EDGE_DEVICE_{IP_ID}/STATUS/ONLINE_POINTS
93
- payload: Number of online points
94
- EDGE_DEVICE_{IP_ID}/STATUS/OFFLINE_POINTS
95
- payload: Number of offline points
96
- EDGE_DEVICE_{IP_ID}/STATUS/TOTAL_POLLED_POINTS
97
- payload: Total number of polled points
98
- EDGE_DEVICE_{IP_ID}/STATUS/AVERAGE_TIME_SINCE_COV_IN_SECONDS
99
- payload: Average time since last change of value in seconds
100
- EDGE_DEVICE_{IP_ID}/STATUS/TOTAL_POINTS_TO_READ
101
- payload: Total number of points to read
102
- EDGE_DEVICE_{IP_ID}/STATUS/DISCOVERED_POINT_COUNT
103
- payload: Number of discovered points
104
- EDGE_DEVICE_{IP_ID}/STATUS/DISCOVERED_DEVICE_COUNT
105
- payload: Number of discovered devices
106
-
107
- where {IP_ID} is the IP address of the device with periods removed (e.g., 192.168.1.100 becomes 192168110).
108
- each of these topics includes the site name as a tag in the message metadata with format geoAddr={siteName}.
109
-
110
- - Additional input options:
131
+ EDGE*DEVICE*{IP*ID}/STATUS/LAST_POINT_PUSHED_TIME
132
+ payload: Timestamp of when the last point was pushed (ISO string)
133
+ EDGE_DEVICE*{IP*ID}/STATUS/LAST_STAT_CALC_TIME
134
+ payload: Current timestamp (ISO string)
135
+ EDGE_DEVICE*{IP*ID}/STATUS/UPTIME
136
+ payload: System uptime formatted as string (e.g., "Uptime: 3 days, 5 hours, 12 minutes, 45 seconds")
137
+ EDGE_DEVICE*{IP*ID}/STATUS/ONLINE_POINTS
138
+ payload: Number of online points
139
+ EDGE_DEVICE*{IP*ID}/STATUS/OFFLINE_POINTS
140
+ payload: Number of offline points
141
+ EDGE_DEVICE*{IP*ID}/STATUS/TOTAL_POLLED_POINTS
142
+ payload: Total number of polled points
143
+ EDGE_DEVICE*{IP*ID}/STATUS/AVERAGE_TIME_SINCE_COV_IN_SECONDS
144
+ payload: Average time since last change of value in seconds
145
+ EDGE_DEVICE*{IP*ID}/STATUS/TOTAL_POINTS_TO_READ
146
+ payload: Total number of points to read
147
+ EDGE_DEVICE*{IP*ID}/STATUS/DISCOVERED_POINT_COUNT
148
+ payload: Number of discovered points
149
+ EDGE_DEVICE*{IP_ID}/STATUS/DISCOVERED_DEVICE_COUNT
150
+ payload: Number of discovered devices
151
+
152
+ where {IP_ID} is the IP address of the device with periods removed (e.g., 192.168.1.100 becomes 192168110).
153
+ each of these topics includes the site name as a tag in the message metadata with format geoAddr={siteName}.
154
+
155
+ - Additional input options:
156
+
111
157
  - reset - resets the complete data model used for all of the inspector analytics
158
+
112
159
  - msg input format: msg.reset = true
113
160
 
114
161
  - sendMqttStats - outputs additional mqtt statistics
115
- - msg input format: msg.type = sendMqttStats
116
- - output topics:
117
- EDGE_DEVICE_{siteName}/BACNETSTATS/ok
162
+
163
+ - msg input format: msg.type = sendMqttStats
164
+ - output topics:
165
+ EDGE*DEVICE*{siteName}/BACNETSTATS/ok
118
166
  payload: Number of points with OK status
119
- EDGE_DEVICE_{siteName}/BACNETSTATS/error
167
+ EDGE*DEVICE*{siteName}/BACNETSTATS/error
120
168
  payload: Number of points with error status
121
- EDGE_DEVICE_{siteName}/BACNETSTATS/missing
169
+ EDGE*DEVICE*{siteName}/BACNETSTATS/missing
122
170
  payload: Number of missing points
123
- EDGE_DEVICE_{siteName}/BACNETSTATS/warnings
171
+ EDGE*DEVICE*{siteName}/BACNETSTATS/warnings
124
172
  payload: Number of points with warnings
125
- EDGE_DEVICE_{siteName}/BACNETSTATS/moved
173
+ EDGE*DEVICE*{siteName}/BACNETSTATS/moved
126
174
  payload: Number of points that have moved (e.g changed object instance)
127
- EDGE_DEVICE_{siteName}/BACNETSTATS/deviceIdChange
175
+ EDGE*DEVICE*{siteName}/BACNETSTATS/deviceIdChange
128
176
  payload: Number of points with changed device IDs
129
- EDGE_DEVICE_{siteName}/BACNETSTATS/deviceIdConflict
177
+ EDGE*DEVICE*{siteName}/BACNETSTATS/deviceIdConflict
130
178
  payload: Number of points with conflicting device IDs
131
- EDGE_DEVICE_{siteName}/BACNETSTATS/unmapped
179
+ EDGE*DEVICE*{siteName}/BACNETSTATS/unmapped
132
180
  payload: Number of unmapped points
133
- EDGE_DEVICE_{siteName}/BACNETSTATS/offlinePercentage
181
+ EDGE*DEVICE*{siteName}/BACNETSTATS/offlinePercentage
134
182
  payload: Percentage of points that are offline
135
183
 
136
184
  where {siteName} is the site name configured in the inspector node.
137
185
 
138
-
139
186
  - Right click -> Update Point on a individual point in the device tree. (Read node UI)
140
187
 
141
188
  - Added programmatic reinitialize/clear points on BACnet server via injecting { msg.reinitializeBacnetServer: true } into the gateway node. Optionally can include a msg.responseTopic string to get a confrimation published to that topic output from the gateway node on sucessfull reinitialize.
@@ -158,6 +205,7 @@ New features:
158
205
  Refactor:
159
206
 
160
207
  - Reading and Writing to the cache file of the datamodel.
208
+
161
209
  - write operations are now locked to 1 operation at a time
162
210
  - a rolling secondary backup file is now created, which can be used in case of corruption of the primary file
163
211
 
package/bacnet_client.js CHANGED
@@ -11,7 +11,6 @@ const {
11
11
  parseBacnetError,
12
12
  getBacnetErrorString,
13
13
  Read_Config_Async,
14
- Read_Config_Sync,
15
14
  isNumber,
16
15
  decodeBitArray,
17
16
  } = require("./common");
@@ -28,6 +27,7 @@ class BacnetClient extends EventEmitter {
28
27
  that.config = config;
29
28
  that.deviceList = [];
30
29
  that.networkTree = {};
30
+ that.renderList = [];
31
31
  that.lastWhoIs = null;
32
32
  that.client = null;
33
33
  that.lastNetworkPoll = null;
@@ -41,7 +41,6 @@ class BacnetClient extends EventEmitter {
41
41
  that.portRangeMatrix = config.portRangeMatrix;
42
42
 
43
43
  try {
44
-
45
44
  that.roundDecimal = config.roundDecimal;
46
45
  that.apduSize = config.apduSize;
47
46
  that.maxSegments = config.maxSegments;
@@ -116,13 +115,11 @@ class BacnetClient extends EventEmitter {
116
115
  }
117
116
  }, "4000");
118
117
  }, "15000");
119
-
120
118
  } catch (e) {
121
119
  that.logOut("Issue initializing client: ", e);
122
120
  }
123
121
 
124
122
  try {
125
-
126
123
  //who is callback
127
124
  that.client.on("iAm", (device) => {
128
125
  if (device.address !== that.config.localIpAdrress) {
@@ -201,7 +198,8 @@ class BacnetClient extends EventEmitter {
201
198
  const cachedData = await Read_Config_Async();
202
199
  const parsedData = JSON.parse(cachedData);
203
200
  if (parsedData && typeof parsedData == "object") {
204
- if (parsedData.renderList) that.renderList = parsedData.renderList;
201
+ // renderList is no longer cached - will be rebuilt by tree builder
202
+ // if (parsedData.renderList) that.renderList = parsedData.renderList;
205
203
  if (parsedData.deviceList) {
206
204
  parsedData.deviceList.forEach(function (device) {
207
205
  let newBacnetDevice = new BacnetDevice(true, device);
@@ -209,12 +207,13 @@ class BacnetClient extends EventEmitter {
209
207
  });
210
208
  }
211
209
  if (parsedData.pointList) that.networkTree = parsedData.pointList;
212
- if (parsedData.renderListCount) that.renderListCount = parsedData.renderListCount;
210
+ // renderListCount is no longer cached - will be recalculated by tree builder
211
+ // if (parsedData.renderListCount) that.renderListCount = parsedData.renderListCount;
213
212
  }
214
213
  }
215
214
  }
216
215
 
217
- testFunction(address, port, type, instance, property) {
216
+ testFunction(address, port, type, instance, property, nodeWarnCallback) {
218
217
  let that = this;
219
218
  console.log("test function ");
220
219
 
@@ -229,6 +228,11 @@ class BacnetClient extends EventEmitter {
229
228
  console.log("1 - readPropertyMultiple: ");
230
229
 
231
230
  console.log(value);
231
+
232
+ if (nodeWarnCallback) {
233
+ nodeWarnCallback(value);
234
+ }
235
+
232
236
  if (value) {
233
237
  // If the result has value, resolve the promise
234
238
  console.log(value.values[0]);
@@ -720,35 +724,30 @@ class BacnetClient extends EventEmitter {
720
724
  const that = this;
721
725
  const roundDecimal = readConfig.precision;
722
726
  const devicesToRead = Object.keys(readConfig.pointsToRead);
723
- const bacnetResults = {};
724
- let pendingRequests = 0;
727
+ let completedDevices = 0;
725
728
 
726
729
  try {
727
- // Process all devices in sequence
728
- for (let deviceIndex = 0; deviceIndex < devicesToRead.length; deviceIndex++) {
729
- const key = devicesToRead[deviceIndex];
730
+ // Create array of device processing promises
731
+ const devicePromises = devicesToRead.map(async (key, deviceIndex) => {
730
732
  const device = that.findDeviceByKey(key);
731
- if (!device) continue;
733
+ if (!device) return null;
732
734
 
733
735
  const deviceName = that.computeDeviceName(device);
734
736
  const deviceKey = that.createDeviceKey(device);
735
737
  const deviceObject = that.networkTree[deviceKey];
736
738
  const maxObjectCount = that.estimateMaxObjectSize(device.getMaxApdu());
737
739
 
738
- if (!bacnetResults[deviceName]) {
739
- bacnetResults[deviceName] = {};
740
- }
740
+ const bacnetResults = {};
741
+ bacnetResults[deviceName] = {};
741
742
 
742
743
  // Process points for the current device
743
744
  const pointsToRead = readConfig.pointsToRead[key];
744
745
  const pointNames = Object.keys(pointsToRead);
745
746
  let totalPoints = pointNames.length - 1;
746
747
  let requestArray = [];
747
- let processedPoints = 0; // Counter for processed points
748
748
 
749
749
  // Process each point for the device in batches
750
750
  for (let i = 0; i < pointNames.length; i++) {
751
-
752
751
  const pointName = pointNames[i];
753
752
  if (pointName === "deviceName") {
754
753
  continue;
@@ -771,17 +770,7 @@ class BacnetClient extends EventEmitter {
771
770
  }
772
771
 
773
772
  // Process the batch when the request array is full or the last point is reached
774
- if (requestArray.length === maxObjectCount) {
775
- if (device.getProtocolServiceSupport("ReadPropertyMultiple") == true) {
776
- await that.processBatch(device, requestArray, deviceName, bacnetResults, that, roundDecimal);
777
- } else {
778
- await that.processIndividualPoints(device, requestArray, deviceName, bacnetResults, that, roundDecimal);
779
- }
780
-
781
- requestArray = [];
782
- // Increment the processed points counter
783
- processedPoints += maxObjectCount;
784
- } else if (i === pointNames.length - 1) {
773
+ if (requestArray.length === maxObjectCount || i === pointNames.length - 1) {
785
774
  if (device.getProtocolServiceSupport("ReadPropertyMultiple") == true) {
786
775
  await that.processBatch(device, requestArray, deviceName, bacnetResults, that, roundDecimal);
787
776
  } else {
@@ -789,28 +778,34 @@ class BacnetClient extends EventEmitter {
789
778
  }
790
779
 
791
780
  requestArray = [];
792
- // Increment the processed points counter
793
- processedPoints += i;
794
781
  }
782
+ }
795
783
 
796
- // Check if all points for the device have been processed
797
- if (processedPoints >= totalPoints) {
798
- pendingRequests++;
799
-
800
- // Emit the `values` event for the current device
801
- that.emit(
802
- "values",
803
- bacnetResults,
804
- outputType,
805
- objectPropertyType,
806
- readNodeName,
807
- pendingRequests,
808
- devicesToRead.length
809
- );
810
- delete bacnetResults[deviceName];
811
- }
784
+ // Return results for this device
785
+ return {
786
+ deviceName,
787
+ results: bacnetResults,
788
+ deviceIndex: deviceIndex + 1,
789
+ totalDevices: devicesToRead.length,
790
+ };
791
+ });
792
+
793
+ // Process all devices in parallel and emit results as they complete
794
+ const results = await Promise.allSettled(devicePromises);
795
+
796
+ results.forEach((result, index) => {
797
+ if (result.status === "fulfilled" && result.value) {
798
+ completedDevices++;
799
+ const { deviceName, results: bacnetResults, deviceIndex, totalDevices } = result.value;
800
+
801
+ // Emit the `values` event for this device immediately
802
+ that.emit("values", bacnetResults, outputType, objectPropertyType, readNodeName, completedDevices, totalDevices);
803
+ } else {
804
+ // Handle failed device (offline/error)
805
+ completedDevices++;
806
+ that.logOut(`Device ${devicesToRead[index]} failed:`, result.reason);
812
807
  }
813
- }
808
+ });
814
809
  } catch (error) {
815
810
  that.logOut("doRead error: ", error);
816
811
  }
@@ -847,6 +842,7 @@ class BacnetClient extends EventEmitter {
847
842
  if (isNumber(val)) {
848
843
  pointRef.presentValue = roundDecimalPlaces(val, roundDecimal);
849
844
  pointRef.error = "none";
845
+ pointRef.status = "online";
850
846
  if (pointRef.meta.objectId.type == 19 || pointRef.meta.objectId.type == 13 || pointRef.meta.objectId.type == 14) {
851
847
  if (pointRef.stateTextArray && typeof pointRef.stateTextArray[0].value !== "object") {
852
848
  if (val != 0) {
@@ -860,14 +856,18 @@ class BacnetClient extends EventEmitter {
860
856
  if (typeof val !== "object") {
861
857
  pointRef.presentValue = val;
862
858
  pointRef.error = "none";
859
+ pointRef.status = "online";
863
860
  } else if (val.errorClass && val.errorClass) {
864
861
  pointRef.error = getBacnetErrorString(val.errorClass, val.errorClass);
862
+ pointRef.status = "offline";
863
+ } else {
864
+ pointRef.error = "none";
865
+ pointRef.status = "online";
865
866
  }
866
867
  }
867
868
 
868
869
  pointRef.meta["device"] = deviceMetaInfo;
869
870
  pointRef.timestamp = Date.now();
870
- pointRef.status = "online";
871
871
 
872
872
  // Store the point data in results
873
873
  bacnetResults[deviceName][pointNameRef] = pointRef;
@@ -891,7 +891,6 @@ class BacnetClient extends EventEmitter {
891
891
  for (const request of requestArray) {
892
892
  const { objectId, pointRef, pointName } = request;
893
893
  try {
894
-
895
894
  const result = await that.updatePoint(device, pointRef);
896
895
 
897
896
  if (result.objectId.type == objectId.type && result.objectId.instance == objectId.instance) {
@@ -899,6 +898,8 @@ class BacnetClient extends EventEmitter {
899
898
 
900
899
  if (isNumber(val)) {
901
900
  pointRef.presentValue = roundDecimalPlaces(val, roundDecimal);
901
+ pointRef.error = "none";
902
+ pointRef.status = "online";
902
903
 
903
904
  if (pointRef.meta.objectId.type == 19 || pointRef.meta.objectId.type == 13 || pointRef.meta.objectId.type == 14) {
904
905
  if (pointRef.stateTextArray && typeof pointRef.stateTextArray[0].value !== "object") {
@@ -910,13 +911,21 @@ class BacnetClient extends EventEmitter {
910
911
  }
911
912
  }
912
913
  } else {
913
- pointRef.presentValue = val;
914
+ if (typeof val !== "object") {
915
+ pointRef.presentValue = val;
916
+ pointRef.error = "none";
917
+ pointRef.status = "online";
918
+ } else if (val.errorClass && val.errorClass) {
919
+ pointRef.error = getBacnetErrorString(val.errorClass, val.errorClass);
920
+ pointRef.status = "offline";
921
+ } else {
922
+ pointRef.error = "none";
923
+ pointRef.status = "online";
924
+ }
914
925
  }
915
926
 
916
927
  pointRef.meta["device"] = deviceMetaInfo;
917
928
  pointRef.timestamp = Date.now();
918
- pointRef.status = "online";
919
- pointRef.error = "none";
920
929
 
921
930
  // Store the point data in results
922
931
  bacnetResults[deviceName][pointName] = pointRef;
@@ -981,7 +990,7 @@ class BacnetClient extends EventEmitter {
981
990
 
982
991
  return true;
983
992
  } catch (e) {
984
- throw e
993
+ throw e;
985
994
  }
986
995
  }
987
996
 
@@ -1007,7 +1016,7 @@ class BacnetClient extends EventEmitter {
1007
1016
  let settings = {
1008
1017
  maxSegments: maxSegments,
1009
1018
  maxApdu: maxApdu,
1010
- }
1019
+ };
1011
1020
 
1012
1021
  return new Promise((resolve, reject) => {
1013
1022
  that.client.readProperty(
@@ -1681,7 +1690,7 @@ class BacnetClient extends EventEmitter {
1681
1690
  if (json.body.renderListCount) {
1682
1691
  that.renderListCount = json.body.renderListCount;
1683
1692
  }
1684
- resolve();
1693
+ resolve(true);
1685
1694
  } catch (e) {
1686
1695
  reject(e);
1687
1696
  }