@bitpoolos/edge-bacnet 1.4.1 → 1.4.3

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.
@@ -42,7 +42,124 @@
42
42
  </div>
43
43
  </div>
44
44
 
45
-
45
+ <div class="form-row" style="display: flex; width: fit-content; flex-wrap: nowrap; flex-direction: row; padding-top: 10px; margin-left: auto; margin-right: auto;">
46
+
47
+ <div
48
+ id="node-input-object_properties_group"
49
+ style="display: flex; align-items: flex-start; flex-direction: column;">
50
+ <label
51
+ for="node-input-object_properties_group"
52
+ style="display: flex; align-items: center; white-space: nowrap; text-decoration: underline;"
53
+ >
54
+ Object Properties:
55
+ </label>
56
+
57
+ <div class="objectPropertiesLabel">
58
+ <label
59
+ for="node-input-object_property_simplePayload"
60
+ style="padding-left: 4px; width: auto; align-items: start;"
61
+ class="objectPropertiesLabel">
62
+ <i class="icon-tag"></i> <span data-i18n="bitpool-bacnet.label.object_property_simplePayload"></span>
63
+ <input
64
+ style="margin-left: 5px;"
65
+ class=" objectProp"
66
+ type="checkbox"
67
+ id="node-input-object_property_simplePayload" />
68
+ <a style="white-space: nowrap; padding-left: 20px;">Simple Payload</a>
69
+ </label>
70
+ </div>
71
+
72
+ <div class="objectPropertiesLabel">
73
+ <label
74
+ for="node-input-object_property_simpleWithStatus"
75
+ style="padding-left: 4px; width: auto; align-items: start;"
76
+ class="objectPropertiesLabel">
77
+ <i class="icon-tag"></i> <span data-i18n="bitpool-bacnet.label.object_property_simpleWithStatus"></span>
78
+ <input
79
+ style="margin-left: 5px;"
80
+ class=" objectProp"
81
+ type="checkbox"
82
+ id="node-input-object_property_simpleWithStatus" />
83
+ <a style="white-space: nowrap; padding-left: 20px;">Simple With Status</a>
84
+ </label>
85
+ </div>
86
+
87
+ <div class="objectPropertiesLabel">
88
+ <label
89
+ for="node-input-object_property_fullObject"
90
+ style="padding-left: 4px; width: auto; align-items: start;"
91
+ class="objectPropertiesLabel">
92
+ <i class="icon-tag"></i> <span data-i18n="bitpool-bacnet.label.object_property_fullObject"></span>
93
+ <input
94
+ style="margin-left: 5px;"
95
+ class=" objectProp"
96
+ type="checkbox"
97
+ id="node-input-object_property_fullObject" />
98
+ <a style="white-space: nowrap; padding-left: 20px;">Full Object</a>
99
+ </label>
100
+ </div>
101
+
102
+ </div>
103
+ <div
104
+ id="node-input-msgType"
105
+ style="display: flex; align-items: flex-start; flex-direction: column; padding-left: 50px;">
106
+ <label for="node-input-msgType" style="text-decoration: underline;"> Message type: </label>
107
+ <!-- class= checkbox-round -->
108
+ <div style="display: flex; flex-direction: row; align-items: flex-start;">
109
+ <input
110
+ class="checkbox-round"
111
+ type="checkbox"
112
+ id="node-input-json"
113
+ style="width: 13px; margin-left: 5px;" /><label
114
+ for="node-input-json"
115
+ style="padding-left: 20px; width: fit-content;"
116
+ >Block per device</label
117
+ >
118
+ </div>
119
+ <div style="display: flex; flex-direction: row; align-items: flex-start;">
120
+ <input
121
+ class="checkbox-round"
122
+ type="checkbox"
123
+ id="node-input-mqtt"
124
+ style="width: 13px; margin-left: 5px;" /><label
125
+ for="node-input-mqtt"
126
+ style="padding-left: 20px; width: fit-content;"
127
+ >Individual msgs</label
128
+ >
129
+ </div>
130
+ <div style="display: flex; flex-direction: row; align-items: flex-start;">
131
+ <input
132
+ class="checkbox-round"
133
+ type="checkbox"
134
+ id="node-input-pointJson"
135
+ style="width: 13px; margin-left: 5px;" /><label
136
+ for="node-input-pointJson"
137
+ style="padding-left: 20px; width: fit-content;"
138
+ >Individual JSON</label
139
+ >
140
+ </div>
141
+ </div>
142
+ </div>
143
+ </div>
144
+
145
+
146
+ <div class="injectHeading" style="margin-top: 20px;">
147
+ <a>Topic Properties</a>
148
+ </div>
149
+
150
+ <div class="objectPropertiesLabel" style="margin-top: 20px;">
151
+ <label
152
+ for="node-input-useDeviceName"
153
+ style="padding-left: 4px; width: auto; align-items: start;"
154
+ class="objectPropertiesLabel">
155
+ <i class="icon-tag"></i> <span data-i18n="bitpool-bacnet.label.useDeviceName"></span>
156
+ <input
157
+ style="margin-left: 5px;"
158
+ class=" objectProp"
159
+ type="checkbox"
160
+ id="node-input-useDeviceName" />
161
+ <a style="white-space: nowrap; padding-left: 20px;">Use device name in topic</a>
162
+ </label>
46
163
  </div>
47
164
 
48
165
  <div class="injectHeading" style="margin-top: 20px;">
@@ -373,6 +490,13 @@
373
490
  payloadType: { value: "date" },
374
491
  doPoll: { value: true },
375
492
  doDiscover: { value: false },
493
+ json: { value: false },
494
+ mqtt: { value: false },
495
+ pointJson: { value: true },
496
+ object_property_simplePayload: { value: false },
497
+ object_property_simpleWithStatus: { value: false },
498
+ object_property_fullObject: { value: true },
499
+ useDeviceName: { value: true },
376
500
  },
377
501
  icon: "bitpool.svg",
378
502
  inputs: 0,
@@ -669,10 +793,28 @@
669
793
  $("#inject-time-type-select").trigger("change");
670
794
  $("#inject-time-interval-time-start").trigger("change");
671
795
 
796
+ document.getElementById("node-input-object_property_simplePayload").checked = node.object_property_simplePayload;
797
+ document.getElementById("node-input-object_property_simplePayload").onclick = handlePayloadCheckboxClick;
798
+ document.getElementById("node-input-object_property_simpleWithStatus").checked = node.object_property_simpleWithStatus;
799
+ document.getElementById("node-input-object_property_simpleWithStatus").onclick = handlePayloadCheckboxClick;
800
+ document.getElementById("node-input-object_property_fullObject").checked = node.object_property_fullObject;
801
+ document.getElementById("node-input-object_property_fullObject").onclick = handlePayloadCheckboxClick;
802
+
803
+ document.getElementById("node-input-useDeviceName").checked = node.useDeviceName;
804
+
672
805
 
673
806
  document.getElementById("node-input-doDiscover").onclick = handleCheckboxClick;
674
807
  document.getElementById("node-input-doPoll").onclick = handleCheckboxClick;
675
808
 
809
+ document.getElementById("node-input-json").checked = node.json;
810
+ document.getElementById("node-input-json").onclick = handleMsgTypeClick;
811
+ document.getElementById("node-input-mqtt").checked = node.mqtt;
812
+ document.getElementById("node-input-mqtt").onclick = handleMsgTypeClick;
813
+ document.getElementById("node-input-pointJson").checked = node.pointJson;
814
+ document.getElementById("node-input-pointJson").onclick = handleMsgTypeClick;
815
+
816
+
817
+
676
818
  function handleCheckboxClick() {
677
819
  if (this.id == "node-input-doDiscover") {
678
820
  document.getElementById("node-input-doPoll").checked = false;
@@ -682,6 +824,36 @@
682
824
  }
683
825
  }
684
826
 
827
+ function handlePayloadCheckboxClick() {
828
+ if (this.id == "node-input-object_property_simplePayload") {
829
+ document.getElementById("node-input-object_property_fullObject").checked = false;
830
+ document.getElementById("node-input-object_property_simpleWithStatus").checked = false;
831
+ }
832
+ if (this.id == "node-input-object_property_simpleWithStatus") {
833
+ document.getElementById("node-input-object_property_fullObject").checked = false;
834
+ document.getElementById("node-input-object_property_simplePayload").checked = false;
835
+ }
836
+ if (this.id == "node-input-object_property_fullObject") {
837
+ document.getElementById("node-input-object_property_simplePayload").checked = false;
838
+ document.getElementById("node-input-object_property_simpleWithStatus").checked = false;
839
+ }
840
+ }
841
+
842
+ function handleMsgTypeClick() {
843
+ if (this.id == "node-input-json") {
844
+ document.getElementById("node-input-mqtt").checked = false;
845
+ document.getElementById("node-input-pointJson").checked = false;
846
+ }
847
+ if (this.id == "node-input-mqtt") {
848
+ document.getElementById("node-input-json").checked = false;
849
+ document.getElementById("node-input-pointJson").checked = false;
850
+ }
851
+ if (this.id == "node-input-pointJson") {
852
+ document.getElementById("node-input-json").checked = false;
853
+ document.getElementById("node-input-mqtt").checked = false;
854
+ }
855
+ }
856
+
685
857
  },
686
858
  oneditsave: function () {
687
859
  var repeat = "";
package/bitpool_inject.js CHANGED
@@ -19,28 +19,28 @@
19
19
  */
20
20
 
21
21
 
22
- module.exports = function(RED) {
22
+ module.exports = function (RED) {
23
23
  "use strict";
24
- const {scheduleTask} = require("cronosjs");
24
+ const { scheduleTask } = require("cronosjs");
25
25
 
26
26
  function BitpoolInjectNode(n) {
27
- RED.nodes.createNode(this,n);
27
+ RED.nodes.createNode(this, n);
28
28
 
29
29
  /* Handle legacy */
30
- if(!Array.isArray(n.props)){
30
+ if (!Array.isArray(n.props)) {
31
31
  n.props = [];
32
32
  n.props.push({
33
- p:'payload',
34
- v:n.payload,
35
- vt:n.payloadType
33
+ p: 'payload',
34
+ v: n.payload,
35
+ vt: n.payloadType
36
36
  });
37
37
  n.props.push({
38
- p:'topic',
39
- v:n.topic,
40
- vt:'str'
38
+ p: 'topic',
39
+ v: n.topic,
40
+ vt: 'str'
41
41
  });
42
42
  } else {
43
- for (var i=0,l=n.props.length; i<l; i++) {
43
+ for (var i = 0, l = n.props.length; i < l; i++) {
44
44
  if (n.props[i].p === 'payload' && !n.props[i].hasOwnProperty('v')) {
45
45
  n.props[i].v = n.payload;
46
46
  n.props[i].vt = n.payloadType;
@@ -56,6 +56,17 @@
56
56
  this.once = n.once;
57
57
  this.doPoll = n.doPoll;
58
58
  this.doDiscover = n.doDiscover;
59
+
60
+ this.object_property_simplePayload = n.object_property_simplePayload;
61
+ this.object_property_simpleWithStatus = n.object_property_simpleWithStatus;
62
+ this.object_property_fullObject = n.object_property_fullObject;
63
+
64
+ this.useDeviceName = n.useDeviceName;
65
+
66
+ this.json = n.json;
67
+ this.mqtt = n.mqtt;
68
+ this.pointJson = n.pointJson;
69
+
59
70
  this.onceDelay = (n.onceDelay || 0.1) * 1000;
60
71
  this.interval_id = null;
61
72
  this.cronjob = null;
@@ -68,7 +79,7 @@
68
79
  prop.exp = RED.util.prepareJSONataExpression(val, node);
69
80
  }
70
81
  catch (err) {
71
- node.error(RED._("inject.errors.invalid-expr", {error:err.message}));
82
+ node.error(RED._("inject.errors.invalid-expr", { error: err.message }));
72
83
  prop.exp = null;
73
84
  }
74
85
  }
@@ -83,27 +94,39 @@
83
94
  if (this.repeat && !isNaN(this.repeat) && this.repeat > 0) {
84
95
  this.repeat = this.repeat * 1000;
85
96
  this.debug(RED._("inject.repeat", this));
86
- this.interval_id = setInterval(function() {
97
+ this.interval_id = setInterval(function () {
87
98
  node.emit("input", {});
88
99
  }, this.repeat);
89
100
  } else if (this.crontab) {
90
101
  this.debug(RED._("inject.crontab", this));
91
- this.cronjob = scheduleTask(this.crontab,() => { node.emit("input", {})});
102
+ this.cronjob = scheduleTask(this.crontab, () => { node.emit("input", {}) });
92
103
  }
93
104
  };
94
105
 
95
106
  if (this.once) {
96
- this.onceTimeout = setTimeout( function() {
97
- node.emit("input",{});
107
+ this.onceTimeout = setTimeout(function () {
108
+ node.emit("input", {});
98
109
  node.repeaterSetup();
99
110
  }, this.onceDelay);
100
111
  } else {
101
112
  node.repeaterSetup();
102
113
  }
103
114
 
104
- this.on("input", function(msg, send, done) {
115
+ this.on("input", function (msg, send, done) {
105
116
  msg.doDiscover = node.doDiscover;
106
117
  msg.doPoll = node.doPoll;
118
+
119
+ msg.simplePayload = node.object_property_simplePayload;
120
+ msg.simpleWithStatus = node.object_property_simpleWithStatus;
121
+ msg.fullObject = node.object_property_fullObject;
122
+
123
+ msg.json = node.json;
124
+ msg.mqtt = node.mqtt;
125
+ msg.pointJson = node.pointJson;
126
+
127
+ msg.useDeviceName = node.useDeviceName;
128
+
129
+
107
130
  var errors = [];
108
131
  var props = this.props;
109
132
  if (msg.__user_inject_props__ && Array.isArray(msg.__user_inject_props__)) {
@@ -124,14 +147,14 @@
124
147
  var val = RED.util.evaluateJSONataExpression(exp, msg);
125
148
  RED.util.setMessageProperty(msg, property, val, true);
126
149
  }
127
- catch (err) {
150
+ catch (err) {
128
151
  errors.push(err.message);
129
152
  }
130
153
  }
131
154
  return;
132
155
  }
133
156
  try {
134
- RED.util.setMessageProperty(msg,property,RED.util.evaluateNodeProperty(value, valueType, this, msg),true);
157
+ RED.util.setMessageProperty(msg, property, RED.util.evaluateNodeProperty(value, valueType, this, msg), true);
135
158
  } catch (err) {
136
159
  errors.push(err.toString());
137
160
  }
@@ -146,9 +169,9 @@
146
169
  });
147
170
  }
148
171
 
149
- RED.nodes.registerType("Bitpool-Inject",BitpoolInjectNode);
172
+ RED.nodes.registerType("Bitpool-Inject", BitpoolInjectNode);
150
173
 
151
- BitpoolInjectNode.prototype.close = function() {
174
+ BitpoolInjectNode.prototype.close = function () {
152
175
  if (this.onceTimeout) {
153
176
  clearTimeout(this.onceTimeout);
154
177
  }
@@ -160,7 +183,7 @@
160
183
  }
161
184
  };
162
185
 
163
- RED.httpAdmin.post("/inject/:id", RED.auth.needsPermission("inject.write"), function(req,res) {
186
+ RED.httpAdmin.post("/inject/:id", RED.auth.needsPermission("inject.write"), function (req, res) {
164
187
  var node = RED.nodes.getNode(req.params.id);
165
188
  if (node != null) {
166
189
  try {
@@ -170,9 +193,9 @@
170
193
  node.receive();
171
194
  }
172
195
  res.sendStatus(200);
173
- } catch(err) {
196
+ } catch (err) {
174
197
  res.sendStatus(500);
175
- node.error(RED._("inject.failed",{error:err.toString()}));
198
+ node.error(RED._("inject.failed", { error: err.toString() }));
176
199
  }
177
200
  } else {
178
201
  res.sendStatus(404);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@bitpoolos/edge-bacnet",
3
- "version": "1.4.1",
3
+ "version": "1.4.3",
4
4
  "description": "A bacnet gateway for node-red",
5
5
  "dependencies": {
6
6
  "@plus4nodered/ts-node-bacnet": "^1.0.0-beta.2",
@@ -15,6 +15,16 @@
15
15
 
16
16
  /* Read node styles */
17
17
 
18
+ .refreshReadListButton {
19
+ border: none;
20
+ border-radius: 40px;
21
+ height: 35px;
22
+ width: 35px;
23
+ & i {
24
+ color: #6c757d !important;
25
+ }
26
+ }
27
+
18
28
  .p-treenode-label {
19
29
  color: black;
20
30
  width: 100%;
@@ -150,6 +160,8 @@
150
160
  float: right;
151
161
  display: flex;
152
162
  align-items: center;
163
+ padding: 5px;
164
+ font-weight: bold;
153
165
  }
154
166
  .removeAllDevicesButton:hover {
155
167
  background-color: #d5d5d5;
@@ -247,7 +259,6 @@
247
259
  z-index: 10;
248
260
  }
249
261
 
250
-
251
262
  .context-menu {
252
263
  --mouse-x: 0;
253
264
  --mouse-y: 0;
package/treeBuilder.js CHANGED
@@ -1,4 +1,5 @@
1
1
  const { Store_Config } = require("./common");
2
+ const { BacnetDevice } = require("./bacnet_device");
2
3
 
3
4
  /**
4
5
  * The `treeBuilder` class is responsible for building and processing the network tree structure.
@@ -111,6 +112,42 @@ class treeBuilder {
111
112
  }
112
113
  }
113
114
 
115
+ addEmptyIpRootDevice(childDevice) {
116
+
117
+ const ipAddress = this.getDeviceIpAddress(childDevice);
118
+
119
+ let deviceIndex = this.deviceList.findIndex(ele => ele.address === ipAddress && ele.deviceId === null && ele.deviceName === ipAddress && ele.displayName === ipAddress);
120
+
121
+ if (deviceIndex === -1) {
122
+ let newDevice = {
123
+ address: ipAddress,
124
+ isMstp: false,
125
+ deviceId: null,
126
+ maxApdu: childDevice.getMaxApdu(),
127
+ segmentation: childDevice.getSegmentation(),
128
+ vendorId: childDevice.getVendorId(),
129
+ lastSeen: null,
130
+ deviceName: ipAddress,
131
+ pointsList: [],
132
+ pointListUpdateTs: null,
133
+ manualDiscoveryMode: false,
134
+ pointListRetryCount: 0,
135
+ priorityQueueIsActive: false,
136
+ priorityQueue: [],
137
+ lastPriorityQueueTS: null,
138
+ childDevices: [childDevice.getDeviceId()],
139
+ parentDeviceId: null,
140
+ displayName: ipAddress,
141
+ protocolServicesSupported: [],
142
+ isProtocolServicesSet: false,
143
+ isInitialQuery: true,
144
+ };
145
+
146
+ let newBacnetDevice = new BacnetDevice(true, newDevice);
147
+ this.deviceList.push(newBacnetDevice);
148
+ }
149
+ }
150
+
114
151
  /**
115
152
  * Process the points of a device and add them to the render list.
116
153
  *
@@ -124,7 +161,7 @@ class treeBuilder {
124
161
  */
125
162
  async processDevicePoints(device, deviceObject, deviceName, ipAddress, deviceId, index) {
126
163
  // Initialize the list of children points
127
- const children = [];
164
+ let children = [];
128
165
 
129
166
  // Process each point in the device object
130
167
  for (const pointName in deviceObject) {
@@ -171,6 +208,7 @@ class treeBuilder {
171
208
  parentDeviceId: device.getDeviceId(),
172
209
  showAdded: false,
173
210
  bacnetType: point.meta.objectId.type,
211
+ bacnetInstance: point.meta.objectId.instance,
174
212
  };
175
213
  }
176
214
 
@@ -305,7 +343,21 @@ class treeBuilder {
305
343
  } else {
306
344
  mstpNetworkFolder.children[mstpDeviceIndex] = newDeviceEntry;
307
345
  }
346
+ } else {
347
+ //no parent found in render list
348
+
349
+ if (parentDeviceId !== null) {
350
+ let parentDeviceListIndex = this.deviceList.findIndex(ele => ele.getDeviceId == parentDeviceId);
351
+
352
+ if (parentDeviceListIndex !== -1) {
353
+ let parentDevice = this.deviceList[parentDeviceListIndex];
354
+ this.addRootDeviceFolder(parentDevice, parentDevice.getDeviceName(), parentDeviceListIndex, parentDevice.getAddress(), parentDeviceId);
355
+ }
356
+ } else {
357
+ this.addEmptyIpRootDevice(device);
358
+ }
308
359
  }
360
+
309
361
  } else {
310
362
  // Add the new device entry to the root of the render list
311
363
  if (foundIndex === -1) {