@bitpoolos/edge-bacnet 1.6.8 → 1.6.9

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.
Files changed (44) hide show
  1. package/CHANGELOG.md +10 -0
  2. package/bacnet_client.js +32 -4
  3. package/bacnet_device.js +16 -15
  4. package/package.json +1 -1
  5. package/.github/ISSUE_TEMPLATE/bug_report.md +0 -41
  6. package/resources/node-bacstack-ts/dist/index.js +0 -3
  7. package/resources/node-bacstack-ts/dist/lib/apdu.js +0 -193
  8. package/resources/node-bacstack-ts/dist/lib/asn1.js +0 -1671
  9. package/resources/node-bacstack-ts/dist/lib/bvlc.js +0 -47
  10. package/resources/node-bacstack-ts/dist/lib/client.js +0 -1679
  11. package/resources/node-bacstack-ts/dist/lib/enum.js +0 -2114
  12. package/resources/node-bacstack-ts/dist/lib/npdu.js +0 -112
  13. package/resources/node-bacstack-ts/dist/lib/services/add-list-element.js +0 -58
  14. package/resources/node-bacstack-ts/dist/lib/services/alarm-acknowledge.js +0 -93
  15. package/resources/node-bacstack-ts/dist/lib/services/alarm-summary.js +0 -42
  16. package/resources/node-bacstack-ts/dist/lib/services/atomic-read-file.js +0 -157
  17. package/resources/node-bacstack-ts/dist/lib/services/atomic-write-file.js +0 -136
  18. package/resources/node-bacstack-ts/dist/lib/services/cov-notify.js +0 -119
  19. package/resources/node-bacstack-ts/dist/lib/services/create-object.js +0 -104
  20. package/resources/node-bacstack-ts/dist/lib/services/delete-object.js +0 -21
  21. package/resources/node-bacstack-ts/dist/lib/services/device-communication-control.js +0 -46
  22. package/resources/node-bacstack-ts/dist/lib/services/error.js +0 -27
  23. package/resources/node-bacstack-ts/dist/lib/services/event-information.js +0 -100
  24. package/resources/node-bacstack-ts/dist/lib/services/event-notify-data.js +0 -219
  25. package/resources/node-bacstack-ts/dist/lib/services/get-enrollment-summary.js +0 -172
  26. package/resources/node-bacstack-ts/dist/lib/services/get-event-information.js +0 -135
  27. package/resources/node-bacstack-ts/dist/lib/services/i-am-broadcast.js +0 -59
  28. package/resources/node-bacstack-ts/dist/lib/services/i-have-broadcast.js +0 -34
  29. package/resources/node-bacstack-ts/dist/lib/services/index.js +0 -32
  30. package/resources/node-bacstack-ts/dist/lib/services/life-safety-operation.js +0 -40
  31. package/resources/node-bacstack-ts/dist/lib/services/private-transfer.js +0 -43
  32. package/resources/node-bacstack-ts/dist/lib/services/read-property-multiple.js +0 -44
  33. package/resources/node-bacstack-ts/dist/lib/services/read-property.js +0 -122
  34. package/resources/node-bacstack-ts/dist/lib/services/read-range.js +0 -201
  35. package/resources/node-bacstack-ts/dist/lib/services/reinitialize-device.js +0 -35
  36. package/resources/node-bacstack-ts/dist/lib/services/subscribe-cov.js +0 -55
  37. package/resources/node-bacstack-ts/dist/lib/services/subscribe-property.js +0 -93
  38. package/resources/node-bacstack-ts/dist/lib/services/time-sync.js +0 -31
  39. package/resources/node-bacstack-ts/dist/lib/services/who-has.js +0 -56
  40. package/resources/node-bacstack-ts/dist/lib/services/who-is.js +0 -45
  41. package/resources/node-bacstack-ts/dist/lib/services/write-property-multiple.js +0 -105
  42. package/resources/node-bacstack-ts/dist/lib/services/write-property.js +0 -90
  43. package/resources/node-bacstack-ts/dist/lib/transport.js +0 -86
  44. package/resources/node-bacstack-ts/dist/lib/types.js +0 -2
package/CHANGELOG.md CHANGED
@@ -1,5 +1,15 @@
1
1
  # Changelog
2
2
 
3
+ ## [1.6.9] - 01-07-2026
4
+
5
+ Bug fix:
6
+
7
+ - Recover missing points during discovery on small MSTP devices (e.g. RC FlexOne) that reject ReadProperty(ALL). When the "read all" attempt fails, discovery now tries a single targeted ReadPropertyMultiple for the required properties before falling back to per-property reads. This avoids the request storm that could cause the device to reject reads and silently drop Analog/Binary Output points from the model.
8
+
9
+ - Fixed object-type whitelist filter leaking non-whitelisted objects (e.g. trend-logs) into the model. A malformed object-list entry could throw and abort the filter, leaving the point list unfiltered. The filter is now null-safe, so it always applies and only whitelisted object types are kept.
10
+
11
+ - Added a discovery log when an object is dropped because no OBJECT_NAME was returned (read likely rejected), to aid diagnosis.
12
+
3
13
  ## [1.6.8] - 10-03-2026
4
14
 
5
15
  Bug fix:
package/bacnet_client.js CHANGED
@@ -1521,6 +1521,26 @@ class BacnetClient extends EventEmitter {
1521
1521
  .catch(reject);
1522
1522
  };
1523
1523
 
1524
+ // Targeted middle tier: request the needed properties in ONE ReadPropertyMultiple.
1525
+ // Used when ALL is rejected (e.g. RC FlexOne / small MSTP that don't support reading
1526
+ // the ALL pseudo-property). Same property set as the per-property fallback, so stored
1527
+ // data is identical - just one request instead of ~12. Falls through to the
1528
+ // per-property reads only if this targeted read also fails.
1529
+ const readTargetedMultiple = (resolve, reject) => {
1530
+ that
1531
+ ._readObject(addressObject, type, instance, propertiesToRead, readOptions)
1532
+ .then((result) => {
1533
+ if (result.value) {
1534
+ resolve(result);
1535
+ } else {
1536
+ readPropertiesIndividually(resolve, reject);
1537
+ }
1538
+ })
1539
+ .catch(() => {
1540
+ readPropertiesIndividually(resolve, reject);
1541
+ });
1542
+ };
1543
+
1524
1544
  return new Promise((resolve, reject) => {
1525
1545
  // For Device objects (type 8), skip ALL attempt - many MSTP devices don't support it
1526
1546
  // Go straight to reading individual properties for better reliability
@@ -1537,13 +1557,15 @@ class BacnetClient extends EventEmitter {
1537
1557
  // If the result has value, resolve the promise
1538
1558
  resolve(result);
1539
1559
  } else {
1540
- // If not, proceed to read individual properties
1541
- readPropertiesIndividually(resolve, reject);
1560
+ // ALL returned no value - try the targeted multi-property RPM before
1561
+ // falling back to the per-property storm.
1562
+ readTargetedMultiple(resolve, reject);
1542
1563
  }
1543
1564
  })
1544
1565
  .catch(() => {
1545
- // On error, proceed to read individual properties
1546
- readPropertiesIndividually(resolve, reject);
1566
+ // ALL errored - try the targeted multi-property RPM before falling back
1567
+ // to the per-property storm.
1568
+ readTargetedMultiple(resolve, reject);
1547
1569
  });
1548
1570
  });
1549
1571
  }
@@ -2237,6 +2259,12 @@ class BacnetClient extends EventEmitter {
2237
2259
  that.logOut("issue resolving bacnet payload, see error: ", e);
2238
2260
  reject(e);
2239
2261
  }
2262
+ } else {
2263
+ that.logOut(
2264
+ "[discovery] dropped " + bac_obj + ":" + pointProperty.objectId.instance +
2265
+ " on device " + device.getDeviceId() +
2266
+ " - no OBJECT_NAME returned (read likely rejected)"
2267
+ );
2240
2268
  }
2241
2269
  });
2242
2270
  } else {
package/bacnet_device.js CHANGED
@@ -268,10 +268,20 @@ class BacnetDevice {
268
268
  }
269
269
 
270
270
  setPointsList(newPoints) {
271
+ // Whitelisted object types kept in the model:
272
+ // DEVICE(8) AI(0) AO(1) AV(2) BI(3) BO(4) BV(5) MSI(13) MSO(14) MSV(19) CS(40)
273
+ const allowedTypes = new Set([8, 0, 1, 2, 3, 4, 5, 13, 14, 19, 40]);
274
+
271
275
  for (let index = 0; index < newPoints.length; index++) {
272
276
  let newPoint = newPoints[index];
273
- if (newPoint) {
274
- let foundIndex = this.pointsList.findIndex(ele => ele.value.type == newPoint.value.type && ele.value.instance == newPoint.value.instance);
277
+ // Guard against malformed entries (e.g. a bad object-list read with no .value):
278
+ // skip them here so they never enter pointsList and can't throw the dedup below.
279
+ if (newPoint && newPoint.value && newPoint.value.type !== undefined) {
280
+ let foundIndex = this.pointsList.findIndex(
281
+ ele => ele && ele.value &&
282
+ ele.value.type == newPoint.value.type &&
283
+ ele.value.instance == newPoint.value.instance
284
+ );
275
285
  if (foundIndex == -1) {
276
286
  //not found
277
287
  this.pointsList.push(newPoint);
@@ -279,19 +289,10 @@ class BacnetDevice {
279
289
  }
280
290
  }
281
291
 
282
- this.pointsList = this.pointsList.filter((point) =>
283
- point.value.type == 8 || //DEVICE
284
- point.value.type == 0 || //AI
285
- point.value.type == 1 || //AV
286
- point.value.type == 2 || //AO
287
- point.value.type == 3 || //BI
288
- point.value.type == 4 || //BV
289
- point.value.type == 5 || //BO
290
- point.value.type == 13 || //MSI
291
- point.value.type == 14 || //MSO
292
- point.value.type == 19 || //MSV
293
- point.value.type == 40 //CS
294
- );
292
+ // Optional chaining so a malformed element is EXCLUDED (allowedTypes.has(undefined) === false)
293
+ // instead of throwing and aborting the filter, which previously left pointsList unfiltered
294
+ // and leaked non-whitelisted types (trend-logs etc.) into the model.
295
+ this.pointsList = this.pointsList.filter(point => allowedTypes.has(point?.value?.type));
295
296
  }
296
297
 
297
298
  getDevicePoints() {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@bitpoolos/edge-bacnet",
3
- "version": "1.6.8",
3
+ "version": "1.6.9",
4
4
  "description": "A bacnet gateway for node-red",
5
5
  "dependencies": {
6
6
  "@plus4nodered/ts-node-bacnet": "^1.0.0-beta.2",
@@ -1,41 +0,0 @@
1
- ---
2
- name: Bug report
3
- about: Create a report to help us improve
4
- title: ''
5
- labels: ''
6
-
7
- ---
8
-
9
- **Describe the bug**
10
- A clear and concise description of what the bug is.
11
-
12
- **To Reproduce**
13
- Steps to reproduce the behavior:
14
- 1. Go to '...'
15
- 2. Click on '....'
16
- 3. Scroll down to '....'
17
- 4. See error
18
-
19
- **Expected behavior**
20
- A clear and concise description of what you expected to happen.
21
-
22
- **Screenshots**
23
- If applicable, add screenshots to help explain your problem.
24
-
25
- **System Information (please complete the following information):**
26
- - OS: [e.g. iOS]
27
- - Browser [e.g. chrome, safari]
28
- - Version [e.g. 1.2]
29
- - Node-RED version:
30
- - NodeJS version:
31
-
32
- **Are you running Node-RED in a docker container or directly on the operating system or virtual machine?**
33
-
34
-
35
-
36
- **Please provide a dump of the Node-RED error log with the Error logging and Device Found check box options enabled (found in _gateway_ node - _Discovery_ tab) if applicable:**
37
-
38
-
39
-
40
- **Additional context**
41
- Add any other context about the problem here.
@@ -1,3 +0,0 @@
1
- 'use strict';
2
- module.exports = require('./lib/client');
3
- module.exports.enum = require('./lib/enum');
@@ -1,193 +0,0 @@
1
- 'use strict';
2
- Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.decodeAbort = exports.encodeAbort = exports.decodeError = exports.encodeError = exports.decodeSegmentAck = exports.encodeSegmentAck = exports.decodeComplexAck = exports.encodeComplexAck = exports.decodeSimpleAck = exports.encodeSimpleAck = exports.decodeUnconfirmedServiceRequest = exports.encodeUnconfirmedServiceRequest = exports.decodeConfirmedServiceRequest = exports.encodeConfirmedServiceRequest = exports.getDecodedInvokeId = exports.setDecodedType = exports.getDecodedType = void 0;
4
- const baEnum = require("./enum");
5
- const getDecodedType = (buffer, offset) => {
6
- return buffer[offset];
7
- };
8
- exports.getDecodedType = getDecodedType;
9
- const setDecodedType = (buffer, offset, type) => {
10
- buffer[offset] = type;
11
- };
12
- exports.setDecodedType = setDecodedType;
13
- const getDecodedInvokeId = (buffer, offset) => {
14
- const type = (0, exports.getDecodedType)(buffer, offset);
15
- switch (type & baEnum.PDU_TYPE_MASK) {
16
- case baEnum.PduTypes.SIMPLE_ACK:
17
- case baEnum.PduTypes.COMPLEX_ACK:
18
- case baEnum.PduTypes.ERROR:
19
- case baEnum.PduTypes.REJECT:
20
- case baEnum.PduTypes.ABORT:
21
- return buffer[offset + 1];
22
- case baEnum.PduTypes.CONFIRMED_REQUEST:
23
- return buffer[offset + 2];
24
- default:
25
- return;
26
- }
27
- };
28
- exports.getDecodedInvokeId = getDecodedInvokeId;
29
- const encodeConfirmedServiceRequest = (buffer, type, service, maxSegments, maxApdu, invokeId, sequencenumber, proposedWindowSize) => {
30
- buffer.buffer[buffer.offset++] = type;
31
- buffer.buffer[buffer.offset++] = maxSegments | maxApdu;
32
- buffer.buffer[buffer.offset++] = invokeId;
33
- if ((type & baEnum.PduConReqBits.SEGMENTED_MESSAGE) > 0) {
34
- buffer.buffer[buffer.offset++] = sequencenumber;
35
- buffer.buffer[buffer.offset++] = proposedWindowSize;
36
- }
37
- buffer.buffer[buffer.offset++] = service;
38
- };
39
- exports.encodeConfirmedServiceRequest = encodeConfirmedServiceRequest;
40
- const decodeConfirmedServiceRequest = (buffer, offset) => {
41
- const orgOffset = offset;
42
- const type = buffer[offset++];
43
- const maxSegments = buffer[offset] & 0xF0;
44
- const maxApdu = buffer[offset++] & 0x0F;
45
- const invokeId = buffer[offset++];
46
- let sequencenumber = 0;
47
- let proposedWindowNumber = 0;
48
- if ((type & baEnum.PduConReqBits.SEGMENTED_MESSAGE) > 0) {
49
- sequencenumber = buffer[offset++];
50
- proposedWindowNumber = buffer[offset++];
51
- }
52
- const service = buffer[offset++];
53
- return {
54
- len: offset - orgOffset,
55
- type: type,
56
- service: service,
57
- maxSegments: maxSegments,
58
- maxApdu: maxApdu,
59
- invokeId: invokeId,
60
- sequencenumber: sequencenumber,
61
- proposedWindowNumber: proposedWindowNumber
62
- };
63
- };
64
- exports.decodeConfirmedServiceRequest = decodeConfirmedServiceRequest;
65
- const encodeUnconfirmedServiceRequest = (buffer, type, service) => {
66
- buffer.buffer[buffer.offset++] = type;
67
- buffer.buffer[buffer.offset++] = service;
68
- };
69
- exports.encodeUnconfirmedServiceRequest = encodeUnconfirmedServiceRequest;
70
- const decodeUnconfirmedServiceRequest = (buffer, offset) => {
71
- const orgOffset = offset;
72
- const type = buffer[offset++];
73
- const service = buffer[offset++];
74
- return {
75
- len: offset - orgOffset,
76
- type: type,
77
- service: service
78
- };
79
- };
80
- exports.decodeUnconfirmedServiceRequest = decodeUnconfirmedServiceRequest;
81
- const encodeSimpleAck = (buffer, type, service, invokeId) => {
82
- buffer.buffer[buffer.offset++] = type;
83
- buffer.buffer[buffer.offset++] = invokeId;
84
- buffer.buffer[buffer.offset++] = service;
85
- };
86
- exports.encodeSimpleAck = encodeSimpleAck;
87
- const decodeSimpleAck = (buffer, offset) => {
88
- const orgOffset = offset;
89
- const type = buffer[offset++];
90
- const invokeId = buffer[offset++];
91
- const service = buffer[offset++];
92
- return {
93
- len: offset - orgOffset,
94
- type: type,
95
- service: service,
96
- invokeId: invokeId
97
- };
98
- };
99
- exports.decodeSimpleAck = decodeSimpleAck;
100
- const encodeComplexAck = (buffer, type, service, invokeId, sequencenumber, proposedWindowNumber) => {
101
- let len = 3;
102
- buffer.buffer[buffer.offset++] = type;
103
- buffer.buffer[buffer.offset++] = invokeId;
104
- if ((type & baEnum.PduConReqBits.SEGMENTED_MESSAGE) > 0) {
105
- buffer.buffer[buffer.offset++] = sequencenumber;
106
- buffer.buffer[buffer.offset++] = proposedWindowNumber;
107
- len += 2;
108
- }
109
- buffer.buffer[buffer.offset++] = service;
110
- return len;
111
- };
112
- exports.encodeComplexAck = encodeComplexAck;
113
- const decodeComplexAck = (buffer, offset) => {
114
- const orgOffset = offset;
115
- const type = buffer[offset++];
116
- const invokeId = buffer[offset++];
117
- let sequencenumber = 0;
118
- let proposedWindowNumber = 0;
119
- if ((type & baEnum.PduConReqBits.SEGMENTED_MESSAGE) > 0) {
120
- sequencenumber = buffer[offset++];
121
- proposedWindowNumber = buffer[offset++];
122
- }
123
- const service = buffer[offset++];
124
- return {
125
- len: offset - orgOffset,
126
- type: type,
127
- service: service,
128
- invokeId: invokeId,
129
- sequencenumber: sequencenumber,
130
- proposedWindowNumber: proposedWindowNumber
131
- };
132
- };
133
- exports.decodeComplexAck = decodeComplexAck;
134
- const encodeSegmentAck = (buffer, type, originalInvokeId, sequencenumber, actualWindowSize) => {
135
- buffer.buffer[buffer.offset++] = type;
136
- buffer.buffer[buffer.offset++] = originalInvokeId;
137
- buffer.buffer[buffer.offset++] = sequencenumber;
138
- buffer.buffer[buffer.offset++] = actualWindowSize;
139
- };
140
- exports.encodeSegmentAck = encodeSegmentAck;
141
- const decodeSegmentAck = (buffer, offset) => {
142
- const orgOffset = offset;
143
- const type = buffer[offset++];
144
- const originalInvokeId = buffer[offset++];
145
- const sequencenumber = buffer[offset++];
146
- const actualWindowSize = buffer[offset++];
147
- return {
148
- len: offset - orgOffset,
149
- type: type,
150
- originalInvokeId: originalInvokeId,
151
- sequencenumber: sequencenumber,
152
- actualWindowSize: actualWindowSize
153
- };
154
- };
155
- exports.decodeSegmentAck = decodeSegmentAck;
156
- const encodeError = (buffer, type, service, invokeId) => {
157
- buffer.buffer[buffer.offset++] = type;
158
- buffer.buffer[buffer.offset++] = invokeId;
159
- buffer.buffer[buffer.offset++] = service;
160
- };
161
- exports.encodeError = encodeError;
162
- const decodeError = (buffer, offset) => {
163
- const orgOffset = offset;
164
- const type = buffer[offset++];
165
- const invokeId = buffer[offset++];
166
- const service = buffer[offset++];
167
- return {
168
- len: offset - orgOffset,
169
- type: type,
170
- service: service,
171
- invokeId: invokeId
172
- };
173
- };
174
- exports.decodeError = decodeError;
175
- const encodeAbort = (buffer, type, invokeId, reason) => {
176
- buffer.buffer[buffer.offset++] = type;
177
- buffer.buffer[buffer.offset++] = invokeId;
178
- buffer.buffer[buffer.offset++] = reason;
179
- };
180
- exports.encodeAbort = encodeAbort;
181
- const decodeAbort = (buffer, offset) => {
182
- const orgOffset = offset;
183
- const type = buffer[offset++];
184
- const invokeId = buffer[offset++];
185
- const reason = buffer[offset++];
186
- return {
187
- len: offset - orgOffset,
188
- type: type,
189
- invokeId: invokeId,
190
- reason: reason
191
- };
192
- };
193
- exports.decodeAbort = decodeAbort;