@bitpoolos/edge-bacnet 1.2.8 → 1.3.1

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/bacnet_read.html CHANGED
@@ -3,26 +3,24 @@
3
3
  -->
4
4
 
5
5
  <script type="text/javascript">
6
-
7
- RED.nodes.registerType('Bacnet-Discovery', {
8
- category: 'Bitpool BACnet',
9
- color: '#00aeef',
6
+ RED.nodes.registerType("Bacnet-Discovery", {
7
+ category: "Bitpool BACnet",
8
+ color: "#00aeef",
10
9
  defaults: {
11
- name: {value: ""},
12
- events: {value: true},
13
- json: {value: true},
14
- mqtt: {value: false},
15
- hiddenDeployToggle: {value: false},
16
- prevHiddenToggleState: {value: false},
17
- roundDecimal: {value: 2},
10
+ name: { value: "" },
11
+ events: { value: true },
12
+ json: { value: true },
13
+ mqtt: { value: false },
14
+ hiddenDeployToggle: { value: false },
15
+ prevHiddenToggleState: { value: false },
16
+ roundDecimal: { value: 2 },
18
17
  deviceList: [],
19
18
  pointList: [],
20
- pointsToRead: {value: {}},
21
- readDevices: {value: []},
19
+ pointsToRead: { value: {} },
20
+ readDevices: { value: [] },
22
21
  devicesToRead: [],
23
- object_property_simplePayload: {value: false},
24
- object_property_fullObject: {value: true}
25
-
22
+ object_property_simplePayload: { value: false },
23
+ object_property_fullObject: { value: true },
26
24
  },
27
25
  inputs: 1,
28
26
  outputs: 1,
@@ -38,7 +36,7 @@
38
36
 
39
37
  node.prevHiddenToggleState = node.hiddenDeployToggle;
40
38
 
41
- const {createApp, ref, onMounted} = Vue;
39
+ const { createApp, ref, onMounted } = Vue;
42
40
 
43
41
  //prime vue app
44
42
  const App = {
@@ -53,7 +51,13 @@
53
51
  pollFrequency: ref(),
54
52
  deviceCount: ref(),
55
53
  progressBarValue: ref(),
56
- }
54
+ rightClickedDevice: ref(),
55
+ rightClickedPoint: ref(),
56
+ showDeviceNameDialog: ref(false),
57
+ showPointNameDialog: ref(false),
58
+ deviceDisplayNameValue: ref(),
59
+ pointDisplayNameValue: ref(),
60
+ };
57
61
  },
58
62
  setup() {
59
63
  let pointList;
@@ -66,7 +70,7 @@
66
70
  expandNode(node);
67
71
  }
68
72
 
69
- expandedKeys.value = {...expandedKeys.value};
73
+ expandedKeys.value = { ...expandedKeys.value };
70
74
  };
71
75
  const collapseAll = () => {
72
76
  expandedKeys.value = {};
@@ -87,27 +91,49 @@
87
91
  expandAll,
88
92
  collapseAll,
89
93
  expandNode,
90
- }
94
+ };
91
95
  },
92
96
  mounted() {
93
97
  let app = this;
94
- this.getData();
95
- if(!node.reloadTimer) {
96
- node.reloadTimer = setInterval(() => app.getData(), 5000);
98
+ this.getData(true);
99
+ if (!node.reloadTimer) {
100
+ node.reloadTimer = setInterval(() => app.getData(false), 7000);
97
101
  }
102
+
103
+ $("#readlist-file-upload").on("change", function (event) {
104
+ const input = event.target.files[0];
105
+ const reader = new FileReader();
106
+
107
+ reader.readAsText(input);
108
+
109
+ reader.onload = function (e) {
110
+ const text = e.target.result;
111
+ let jsonPayload = JSON.parse(text);
112
+
113
+ app.nodeService.importReadList(jsonPayload).then(function (result) {
114
+ if (result) {
115
+ app.pointsToRead = jsonPayload;
116
+ app.addToReadDevices(jsonPayload);
117
+ }
118
+ });
119
+ };
120
+ });
98
121
  },
99
122
  methods: {
100
- getData() {
123
+ getData(initial) {
101
124
  let app = this;
102
125
  this.nodeService.getNetworkData().then(function (result) {
126
+ //remove after below fixed
103
127
  app.devices = result.renderList;
128
+ //end remove
129
+
104
130
  app.deviceList = result.deviceList;
105
131
  app.pointList = result.pointList;
106
132
  app.pollFrequency = parseInt(result.pollFrequency);
107
133
  app.deviceCount = result.deviceList.length;
108
134
  //progress bar percentage
109
135
  let progressVal = parseInt((result.renderListCount / result.deviceList.length) * 100);
110
- if(typeof progressVal == "number" && !isNaN(progressVal)) {
136
+ if (typeof progressVal == "number" && !isNaN(progressVal)) {
111
137
  app.progressBarValue = progressVal;
112
138
  } else {
113
139
  app.progressBarValue = 0;
@@ -121,7 +147,7 @@
121
147
  rebuildDataModel() {
122
148
  let app = this;
123
149
  app.nodeService.rebuildDataModel().then(function (result) {
124
- if(result == true){
150
+ if (result == true) {
125
151
  app.progressBarValue = 0;
126
152
  }
127
153
  });
@@ -130,37 +156,47 @@
130
156
  addAllDevices() {
131
157
  let app = this;
132
158
  app.devices.forEach(function (device) {
133
- app.addAllClicked({"node": device});
159
+ app.addAllClicked({ node: device });
134
160
  });
135
161
  app.$forceUpdate();
136
162
  },
137
163
  addAllClicked(slotProps) {
138
- //update UI
164
+ //update UI
139
165
  let app = this;
140
-
141
166
  let clone = JSON.parse(JSON.stringify(slotProps.node));
142
167
 
168
+ while (clone.children.length > 1) {
169
+ let child = clone.children[1];
170
+ if (child.label.includes('MSTP')) {
171
+ child.children.forEach(function (mstpDevice) {
172
+ app.addAllClicked({ node: mstpDevice });
173
+ });
174
+ clone.children.splice(1, 1);
175
+ }
176
+ }
177
+
143
178
  if (this.readDevices) {
144
- let foundIndex = this.readDevices.findIndex(ele => ele.key == slotProps.node.key && ele.label == slotProps.node.label);
145
- if (foundIndex == -1) this.readDevices.push(clone);
179
+ let foundIndex = this.readDevices.findIndex((ele) => ele.initialName == slotProps.node.initialName);
180
+ if (foundIndex == -1) {
181
+ this.readDevices.push(clone);
182
+ } else if (foundIndex !== -1) {
183
+ this.readDevices[foundIndex] = clone;
184
+ }
146
185
  } else {
147
186
  this.readDevices = [clone];
148
187
  }
149
-
150
- let deviceSlot = this.devices.find(ele => ele.label == slotProps.node.label);
151
- if(deviceSlot) {
188
+ let deviceSlot = this.devices.find((ele) => ele.initialName == slotProps.node.initialName);
189
+ if (deviceSlot) {
152
190
  deviceSlot.showAdded = true;
153
- deviceSlot.children.forEach(function(child) {
191
+ deviceSlot.children[0].children.forEach(function (child) {
154
192
  child.showAdded = true;
155
193
  });
156
194
  }
157
195
  slotProps.node.showAdded = true;
158
-
159
- this.$forceUpdate()
160
-
196
+ this.$forceUpdate();
161
197
  //update node-red data structure to forward to gateway
162
- let device = this.deviceList.find(ele => {
163
- if(ele.address.address) {
198
+ let device = this.deviceList.find((ele) => {
199
+ if (ele.address.address) {
164
200
  return ele.address.address == slotProps.node.ipAddr && ele.deviceId == slotProps.node.deviceId;
165
201
  } else {
166
202
  return ele.address == slotProps.node.ipAddr && ele.deviceId == slotProps.node.deviceId;
@@ -169,101 +205,68 @@
169
205
  let deviceAddress = app.getDeviceAddress(device.address);
170
206
  let key = `${deviceAddress}-${device.deviceId}`;
171
207
  let points = this.pointList[key];
172
-
173
208
  if (!this.pointsToRead[key]) {
174
209
  this.pointsToRead[key] = {};
175
210
  }
176
-
177
211
  for (let pointName in points) {
178
212
  let point = this.pointList[key][pointName];
179
213
  this.pointsToRead[key][point.objectName] = point;
180
214
  }
181
-
182
215
  //force a deploy state
183
216
  node.hiddenDeployToggle = !node.prevHiddenToggleState;
184
217
  },
185
- removeAllDevices() {
186
- let app = this;
187
-
188
- let clone = JSON.parse(JSON.stringify(app.readDevices));
189
-
190
- clone.forEach(function (device) {
191
- app.removeAllClicked({"node": device});
192
- });
193
-
194
- app.$forceUpdate();
195
-
196
- },
197
- removeAllClicked(slotProps) {
198
- let app = this;
199
- try {
200
- //update UI
201
- if (this.readDevices.length > 0) {
202
- let foundIndex = this.readDevices.findIndex(ele => ele.key == slotProps.node.key && ele.label == slotProps.node.label);
203
- if (foundIndex !== -1) this.readDevices.splice(foundIndex, 1);
204
- }
205
-
206
- let deviceSlot = this.devices.find(ele => ele.label == slotProps.node.label);
207
- if(deviceSlot) {
208
- deviceSlot.showAdded = false;
209
- deviceSlot.children.forEach(function(child) {
210
- child.showAdded = false;
211
- });
212
- }
213
- slotProps.node.showAdded = false;
214
- this.$forceUpdate();
215
-
216
- //update node-red data structure
217
- let device = this.deviceList.find(ele => {
218
- if(ele.address.address) {
219
- return ele.address.address == slotProps.node.ipAddr && ele.deviceId == slotProps.node.deviceId;
220
- } else{
221
- return ele.address == slotProps.node.ipAddr && ele.deviceId == slotProps.node.deviceId;
222
- }
223
- });
224
- let deviceAddress = app.getDeviceAddress(device.address);
225
- let key = `${deviceAddress}-${device.deviceId}`;
226
- if (this.pointsToRead[key]) {
227
- delete this.pointsToRead[key];
228
- }
229
-
230
- //force a deploy state
231
- node.hiddenDeployToggle = !node.prevHiddenToggleState;
232
-
233
- } catch(e) {
234
- //do nothing
235
- }
236
-
237
- app.$forceUpdate();
238
- },
239
218
  addPointClicked(slotProps) {
240
219
  let app = this;
241
220
  //update UI
242
- let parentDeviceName = slotProps.node.parentDevice;
243
- let foundDeviceIndex = this.readDevices ? this.readDevices.findIndex(ele => ele.label == parentDeviceName) : -1;
244
- let device = this.deviceList.find(ele => ele.deviceName == parentDeviceName);
221
+ const parentDeviceName = slotProps.node.parentDevice;
222
+ const slotDeviceId = slotProps.node.parentDeviceId;
223
+ let device = this.deviceList.find((ele) => ele.deviceId == slotDeviceId);
224
+ let foundDeviceIndex = this.readDevices
225
+ ? this.readDevices.findIndex((ele) => ele.deviceId == device.deviceId)
226
+ : -1;
245
227
  let deviceAddress = app.getDeviceAddress(device.address);
246
228
  let key = `${deviceAddress}-${device.deviceId}`;
247
- let parentDevice = this.devices.find(ele => ele.ipAddr == deviceAddress);
229
+ let parentDevice = this.devices.find((ele) => ele.ipAddr == deviceAddress);
248
230
  let childDevice;
249
- if(device.isMstp){
250
- let foundChildIndex = parentDevice.children[1].children.findIndex(ele => ele.label == parentDeviceName);
251
- if(foundChildIndex !== -1) {
252
- childDevice = parentDevice.children[1].children[foundChildIndex];
231
+ if (device.isMstp) {
232
+ let indexMap = {
233
+ deviceIndex: -1,
234
+ mstpNetorkIndex: -1
235
+ };
236
+ parentDevice.children.forEach(function (child, index) {
237
+ if (child.label.includes("MSTP")) {
238
+ const tempIndex = child.children.findIndex(
239
+ (ele) => ele.deviceId == slotDeviceId
240
+ );
241
+ if (tempIndex !== -1) {
242
+ indexMap.deviceIndex = tempIndex;
243
+ indexMap.mstpNetorkIndex = index;
244
+ }
245
+ }
246
+ });
247
+
248
+ if (indexMap.deviceIndex !== -1 && indexMap.mstpNetorkIndex !== -1) {
249
+ childDevice = parentDevice.children[indexMap.mstpNetorkIndex].children[indexMap.deviceIndex];
253
250
  }
254
- }
251
+ }
255
252
 
256
253
  if (foundDeviceIndex == -1) {
257
254
  //no read devices present, add new
258
255
  let newReadParent;
259
- if(childDevice) {
260
- newReadParent = {...childDevice};
261
- } else{
262
- newReadParent = {...parentDevice};
263
- }
264
- newReadParent.children = [];
265
- newReadParent.children.push(slotProps.node);
266
-
256
+ if (childDevice) {
257
+ newReadParent = JSON.parse(JSON.stringify(childDevice));
258
+ } else {
259
+ newReadParent = JSON.parse(JSON.stringify(parentDevice));
260
+ }
261
+ newReadParent.children[0].children = [];
262
+ newReadParent.children[0].children.push(slotProps.node);
263
+ while (newReadParent.children.length > 1) {
264
+ newReadParent.children.forEach(function (child, index) {
265
+ if (child.label.includes("MSTP")) {
266
+ newReadParent.children.splice(index, 1);
267
+ }
268
+ });
269
+ }
267
270
  if (this.readDevices) {
268
271
  this.readDevices.push(newReadParent);
269
272
  } else {
@@ -271,10 +274,15 @@
271
274
  }
272
275
  } else {
273
276
  // read device found, add point to existing
274
- this.readDevices[foundDeviceIndex].children.push(slotProps.node);
277
+ let pointIndex = this.readDevices[foundDeviceIndex].children[0].children.findIndex(
278
+ (ele) => ele.pointName == slotProps.node.pointName
279
+ );
280
+ if (pointIndex == -1) {
281
+ this.readDevices[foundDeviceIndex].children[0].children.push(slotProps.node);
282
+ }
275
283
  }
276
284
 
277
- //set show added to true
285
+ //set show added to true
278
286
  slotProps.node.showAdded = true;
279
287
  this.$forceUpdate();
280
288
 
@@ -291,25 +299,85 @@
291
299
 
292
300
  app.$forceUpdate();
293
301
  },
302
+ removeAllDevices() {
303
+ let app = this;
304
+ let clone = JSON.parse(JSON.stringify(app.readDevices));
305
+ clone.forEach(function (device) {
306
+ app.removeAllClicked({ node: device });
307
+ });
308
+
309
+ app.pointsToRead = {};
310
+
311
+ app.$forceUpdate();
312
+ },
313
+ removeAllClicked(slotProps) {
314
+ let app = this;
315
+
316
+ try {
317
+ //update UI
318
+ if (app.readDevices.length > 0) {
319
+ let foundIndex = app.readDevices.findIndex(
320
+ (ele) => ele.key == slotProps.node.key && ele.label == slotProps.node.label
321
+ );
322
+ if (foundIndex !== -1) app.readDevices.splice(foundIndex, 1);
323
+ }
324
+
325
+ let deviceSlot = app.devices.find((ele) => ele.label == slotProps.node.label);
326
+ if (deviceSlot) {
327
+ deviceSlot.showAdded = false;
328
+ deviceSlot.children.forEach(function (child) {
329
+ child.showAdded = false;
330
+ });
331
+ }
332
+ slotProps.node.showAdded = false;
333
+ app.$forceUpdate();
334
+
335
+ //update node-red data structure
336
+ let device = app.deviceList.find((ele) => {
337
+ if (ele.address.address) {
338
+ return ele.address.address == slotProps.node.ipAddr && ele.deviceId == slotProps.node.deviceId;
339
+ } else {
340
+ return ele.address == slotProps.node.ipAddr && ele.deviceId == slotProps.node.deviceId;
341
+ }
342
+ });
343
+ let deviceAddress = app.getDeviceAddress(device.address);
344
+ let key = `${deviceAddress}-${device.deviceId}`;
345
+ if (app.pointsToRead[key]) {
346
+ delete app.pointsToRead[key];
347
+ }
348
+
349
+ //force a deploy state
350
+ node.hiddenDeployToggle = !node.prevHiddenToggleState;
351
+ } catch (e) {
352
+ //do nothing
353
+ }
354
+
355
+ app.$forceUpdate();
356
+ },
294
357
  removePointClicked(slotProps) {
295
358
  let app = this;
359
+
296
360
  //update UI
297
361
  let parentDeviceName = slotProps.node.parentDevice;
298
- let foundDeviceIndex = this.readDevices ? this.readDevices.findIndex(ele => ele.label == parentDeviceName) : -1;
299
- let parentDevice = this.devices.find(ele => ele.label == parentDeviceName);
300
- let device = this.deviceList.find(ele => ele.deviceName == parentDeviceName);
362
+ let parentDevice = this.devices.find((ele) => ele.label == parentDeviceName);
363
+ let device = this.deviceList.find((ele) => ele.deviceName == parentDeviceName);
364
+ let foundDeviceIndex = this.readDevices
365
+ ? this.readDevices.findIndex((ele) => ele.deviceId == device.deviceId)
366
+ : -1;
301
367
  let deviceAddress = app.getDeviceAddress(device.address);
302
368
  let key = `${deviceAddress}-${device.deviceId}`;
303
369
 
304
370
  if (foundDeviceIndex !== -1) {
305
- let foundIndex = this.readDevices[foundDeviceIndex].children.findIndex(ele => ele.key == slotProps.node.key && ele.pointName == slotProps.node.pointName);
306
- if (foundIndex !== -1) this.readDevices[foundDeviceIndex].children.splice(foundIndex, 1);
307
- if (this.readDevices[foundDeviceIndex].children.length == 0) {
371
+ let foundIndex = this.readDevices[foundDeviceIndex].children[0].children.findIndex(
372
+ (ele) => ele.pointName == slotProps.node.pointName
373
+ );
374
+ if (foundIndex !== -1) this.readDevices[foundDeviceIndex].children[0].children.splice(foundIndex, 1);
375
+ if (this.readDevices[foundDeviceIndex].children[0].children.length == 0) {
308
376
  this.readDevices.splice(foundDeviceIndex, 1);
309
377
  }
310
378
  }
311
379
 
312
- //set show added to true
380
+ //set show added to true
313
381
  slotProps.node.showAdded = false;
314
382
  this.$forceUpdate();
315
383
 
@@ -317,26 +385,69 @@
317
385
  let point = this.pointList[key][slotProps.node.pointName];
318
386
  if (this.pointsToRead[key][point.objectName]) delete this.pointsToRead[key][point.objectName];
319
387
  //if last point is removed, deleted whole entry
320
- if(Object.keys(this.pointsToRead[key]).length == 0) delete this.pointsToRead[key];
388
+ if (Object.keys(this.pointsToRead[key]).length == 0) delete this.pointsToRead[key];
321
389
 
322
390
  //force a deploy state
323
391
  node.hiddenDeployToggle = !node.prevHiddenToggleState;
324
392
 
325
393
  app.$forceUpdate();
326
394
  },
395
+ exportPointListCsv() {
396
+ let app = this;
397
+ let csvContent = "data:text/csv;charset=utf-8,ipAddress,deviceId,deviceName,pointName,objectType" + "\r\n";
398
+ const keys = Object.keys(app.pointList);
399
+ for (key in keys) {
400
+ const guid = keys[key];
401
+ const ipAddress = guid.split("-")[0];
402
+ const deviceId = guid.split("-")[1];
403
+ const deviceObject = app.pointList[keys[key]];
404
+ const device = app.deviceList.find((ele) => {
405
+ if (ele.address.address) {
406
+ return ele.address.address == ipAddress && ele.deviceId == deviceId;
407
+ } else {
408
+ return ele.address == ipAddress && ele.deviceId == deviceId;
409
+ }
410
+ });
411
+ const deviceName = device.deviceName;
412
+ const points = Object.keys(deviceObject);
413
+ for (point in points) {
414
+ csvContent +=
415
+ ipAddress +
416
+ "," +
417
+ deviceId +
418
+ "," +
419
+ deviceName +
420
+ "," +
421
+ points[point] +
422
+ "," +
423
+ deviceObject[points[point]].meta.objectId.type +
424
+ "\r\n";
425
+
426
+ if (parseInt(point) == points.length - 1 && parseInt(key) == keys.length - 1) {
427
+ // last iteration
428
+ var encodedUri = encodeURI(csvContent);
429
+ var link = document.createElement("a");
430
+ link.setAttribute("href", encodedUri);
431
+ link.setAttribute("download", "pointslist.csv");
432
+ document.body.appendChild(link);
433
+ link.click();
434
+ }
435
+ }
436
+ }
437
+ },
327
438
  getDeviceAddress(addr) {
328
- switch(typeof addr) {
439
+ switch (typeof addr) {
329
440
  case "object":
330
441
  return addr.address;
331
442
  case "string":
332
443
  return addr;
333
- default:
444
+ default:
334
445
  return addr;
335
446
  }
336
447
  },
337
448
  isDeviceActive(slotProps) {
338
449
  let app = this;
339
- if (((Date.now() - slotProps.node.lastSeen) / 1000) < (app.pollFrequency + 5)) {
450
+ if ((Date.now() - slotProps.node.lastSeen) / 1000 < app.pollFrequency + 5) {
340
451
  return true;
341
452
  }
342
453
  return false;
@@ -348,23 +459,400 @@
348
459
  return false;
349
460
  }
350
461
  },
462
+ hasMstpDevices(slotProps) {
463
+ if (slotProps.node.children[1] && slotProps.node.children[1].children.length > 0) {
464
+ return true;
465
+ }
466
+ return false;
467
+ },
351
468
  isSlotAdded(slotProps) {
352
469
  return slotProps.node.showAdded;
353
470
  },
354
471
  hasData() {
355
472
  if (this.devices && this.devices.length > 0) {
356
- return true
473
+ return true;
357
474
  } else {
358
475
  return false;
359
476
  }
360
- }
477
+ },
478
+ emptyTree() {
479
+ if (this.devices.length > 0) {
480
+ return true;
481
+ }
482
+ return false;
483
+ },
484
+ onDeviceRightClick(slotProps, event) {
485
+ let app = this;
486
+ app.rightClickedDevice = slotProps;
487
+ event.preventDefault();
488
+ menu.style.setProperty("--mouse-x", event.clientX + "px");
489
+ menu.style.setProperty("--mouse-y", event.clientY + "px");
490
+ menu.style.display = "block";
491
+ },
492
+ onPointRightClick(slotProps, event) {
493
+ let app = this;
494
+ app.rightClickedPoint = slotProps;
495
+ event.preventDefault();
496
+ pointMenu.style.setProperty("--mouse-x", event.clientX + "px");
497
+ pointMenu.style.setProperty("--mouse-y", event.clientY + "px");
498
+ pointMenu.style.display = "block";
499
+ },
500
+ handleContextMenuClick(type) {
501
+ let app = this;
502
+ switch (type) {
503
+ case "purgeDevice":
504
+ app.purgeDevice(app.rightClickedDevice);
505
+ break;
506
+ case "updatePoints":
507
+ app.updatePointsForDevice(app.rightClickedDevice);
508
+ break;
509
+ case "addAllPoints":
510
+ app.addAllClicked(app.rightClickedDevice);
511
+ break;
512
+ case "removeAllPoints":
513
+ app.removeAllClicked(app.rightClickedDevice);
514
+ break;
515
+ case "setDeviceName":
516
+ app.showDeviceNameDialog = true;
517
+ app.deviceDisplayNameValue = app.rightClickedDevice.node.label;
518
+ break;
519
+ default:
520
+ break;
521
+ }
522
+ },
523
+ handlePointContextMenuClick(type) {
524
+ let app = this;
525
+ switch (type) {
526
+ case "setPointName":
527
+ app.showPointNameDialog = true;
528
+ app.pointDisplayNameValue = app.rightClickedPoint.node.label;
529
+ break;
530
+ case "updatePoint":
531
+ app.updatePoint(app.rightClickedDevice);
532
+ break;
533
+ default:
534
+ break;
535
+ }
536
+ },
537
+ purgeDevice(slotProps) {
538
+ let app = this;
539
+ let device = app.deviceList.find((ele) => {
540
+ if (ele.address.address) {
541
+ return ele.address.address == slotProps.node.ipAddr && ele.deviceId == slotProps.node.deviceId;
542
+ } else {
543
+ return ele.address == slotProps.node.ipAddr && ele.deviceId == slotProps.node.deviceId;
544
+ }
545
+ });
546
+ if (device) {
547
+ app.nodeService.purgeDevice(device).then(function (result) { });
548
+ }
549
+ },
550
+ updatePointsForDevice(slotProps) {
551
+ let app = this;
552
+ let device = app.deviceList.find((ele) => {
553
+ if (ele.address.address) {
554
+ return ele.address.address == slotProps.node.ipAddr && ele.deviceId == slotProps.node.deviceId;
555
+ } else {
556
+ return ele.address == slotProps.node.ipAddr && ele.deviceId == slotProps.node.deviceId;
557
+ }
558
+ });
559
+ if (device) {
560
+ app.nodeService.updatePointsForDevice(device).then(function (result) { });
561
+ }
562
+ },
563
+ updatePoint(slotProps) {
564
+ let app = this;
565
+ let device = app.deviceList.find((ele) => {
566
+ if (ele.address.address) {
567
+ return ele.address.address == slotProps.node.ipAddr && ele.deviceId == slotProps.node.deviceId;
568
+ } else {
569
+ return ele.address == slotProps.node.ipAddr && ele.deviceId == slotProps.node.deviceId;
570
+ }
571
+ });
572
+ if (device) {
573
+ app.nodeService.updatePoint(device).then(function (result) { });
574
+ }
575
+ },
576
+ setDeviceName() {
577
+ let app = this;
578
+ const slotProps = app.rightClickedDevice;
579
+ const displayName = app.deviceDisplayNameValue;
580
+ let device = app.deviceList.find((ele) => {
581
+ if (ele.address.address) {
582
+ return ele.address.address == slotProps.node.ipAddr && ele.deviceId == slotProps.node.deviceId;
583
+ } else {
584
+ return ele.address == slotProps.node.ipAddr && ele.deviceId == slotProps.node.deviceId;
585
+ }
586
+ });
587
+
588
+ if (device) {
589
+ app.nodeService.setDeviceDisplayName(device, displayName).then(function (result) {
590
+ if (result) {
591
+ slotProps.node.label = displayName;
592
+ }
593
+ });
594
+ }
595
+
596
+ app.showDeviceNameDialog = false;
597
+ },
598
+ setPointName() {
599
+ let app = this;
600
+
601
+ const slotProps = app.rightClickedPoint;
602
+ const pointDisplayName = app.pointDisplayNameValue;
603
+ const pointName = slotProps.node.pointName;
604
+
605
+ let device = app.deviceList.find((ele) => {
606
+ return ele.deviceName == slotProps.node.parentDevice;
607
+ });
608
+
609
+ if (device) {
610
+ let deviceAddress = app.getDeviceAddress(device.address);
611
+ let deviceKey = `${deviceAddress}-${device.deviceId}`;
612
+
613
+ app.nodeService.setPointDisplayName(deviceKey, pointName, pointDisplayName).then(function (result) {
614
+ if (result) {
615
+ slotProps.node.label = pointDisplayName;
616
+ }
617
+ });
618
+ }
619
+
620
+ app.showPointNameDialog = false;
621
+ },
622
+ settingDeviceName(slotProps) {
623
+ let app = this;
624
+ if (slotProps.node.settingDisplayName) {
625
+ return true;
626
+ }
627
+ return false;
628
+ },
629
+ cancelDisplayNameDialog() {
630
+ let app = this;
631
+ app.deviceDisplayNameValue = "";
632
+ app.showDeviceNameDialog = false;
633
+ },
634
+ cancelPointNameDialog() {
635
+ let app = this;
636
+ app.pointDisplayNameValue = "";
637
+ app.showPointNameDialog = false;
638
+ },
639
+ exportReadList() {
640
+ let app = this;
641
+ let exportJson = {};
642
+ let deviceIndex = 0;
643
+ const devicesToExport = Object.keys(app.pointsToRead);
644
+
645
+ doDevice(deviceIndex);
646
+
647
+ function doDevice(deviceIndex) {
648
+ const key = devicesToExport[deviceIndex];
649
+ const device = app.pointsToRead[key];
650
+
651
+ let readDevice = app.readDevices.find(ele => ele.ipAddr == key.split("-")[0] && ele.deviceId == key.split("-")[1]);
652
+ let deviceName;
653
+
654
+ if (readDevice) {
655
+ deviceName = readDevice.label
656
+ }
657
+
658
+ exportJson[key] = {};
659
+ if (deviceName) {
660
+ exportJson[key]["deviceName"] = deviceName;
661
+ }
662
+
663
+ const devicePoints = Object.keys(device);
664
+ let pointIndex = 0;
665
+ doPoints(pointIndex);
666
+
667
+ function doPoints(pointIndex) {
668
+ let pointName = devicePoints[pointIndex];
669
+ let pointObject = device[pointName];
670
+
671
+ //formatting json payload, still in progress
672
+
673
+ exportJson[key][pointName] = {
674
+ meta: pointObject.meta,
675
+ objectName: pointObject.objectName,
676
+ displayName: pointObject.displayName,
677
+ };
678
+
679
+ // exportJson[key][pointName] = pointObject;
680
+
681
+ if (pointIndex < devicePoints.length - 1) {
682
+ //more points to read for device
683
+ pointIndex++;
684
+ doPoints(pointIndex);
685
+ } else if (pointIndex == devicePoints.length - 1) {
686
+ //all points have been iterated
687
+
688
+ if (deviceIndex < devicesToExport.length - 1) {
689
+ //more work to do
690
+ deviceIndex++;
691
+ doDevice(deviceIndex);
692
+ } else if (deviceIndex == devicesToExport.length - 1) {
693
+ //all read devices complete
694
+ let data = "text/json;charset=utf-8," + encodeURIComponent(JSON.stringify(exportJson));
695
+ let aEle = document.getElementById("exportJSON");
696
+ aEle.setAttribute("href", "data:" + data);
697
+ aEle.setAttribute("download", "bacnet-read-list.json");
698
+ aEle.click();
699
+ }
700
+ }
701
+ }
702
+ }
703
+ },
704
+ importReadList() {
705
+ let app = this;
706
+ $("#readlist-file-upload").click();
707
+ },
708
+ addToReadDevices(pointsToRead) {
709
+ let app = this;
710
+
711
+ for (let key in pointsToRead) {
712
+ let ip = key.split("-")[0];
713
+ let id = key.split("-")[1];
714
+ const importedDevice = pointsToRead[key];
715
+ let foundIndex = app.devices.findIndex((ele) => ele.ipAddr == ip);
716
+
717
+ if (foundIndex !== -1) {
718
+ // match ip, check id
719
+ if (app.devices[foundIndex].deviceId == id) {
720
+ //found device
721
+ let treeDevice = app.devices[foundIndex];
722
+ let device = app.deviceList.find((ele) => {
723
+ if (ele.address.address) {
724
+ return ele.address.address == ip && ele.deviceId == id;
725
+ } else {
726
+ return ele.address == ip && ele.deviceId == id;
727
+ }
728
+ });
729
+
730
+ for (let pointName in importedDevice) {
731
+ let point = importedDevice[pointName];
732
+ if (pointName == "deviceName") {
733
+ app.nodeService.setDeviceDisplayName(device, point);
734
+ treeDevice.label = point;
735
+ }
736
+ let pointInTree = treeDevice.children[0].children.find(
737
+ (ele) => ele.data == pointName && ele.bacnetType == point.meta.objectId.type
738
+ );
739
+
740
+ if (pointInTree) {
741
+ pointInTree.label = point.displayName;
742
+
743
+ const isDeviceInReadList = app.readDevices
744
+ ? app.readDevices.findIndex((ele) => ele.deviceId == treeDevice.deviceId)
745
+ : -1;
746
+
747
+ if (isDeviceInReadList == -1) {
748
+ //no read devices present, add new
749
+ let newReadParent = JSON.parse(JSON.stringify(treeDevice));
750
+ newReadParent.children[0].children = [];
751
+ newReadParent.children[0].children.push(pointInTree);
752
+ if (app.readDevices) {
753
+ app.readDevices.push(newReadParent);
754
+ } else {
755
+ app.readDevices = [newReadParent];
756
+ }
757
+ } else {
758
+ // read device found, add point to existing
759
+ let pointIndex = app.readDevices[isDeviceInReadList].children[0].children.findIndex(
760
+ (ele) => ele.data == point.objectName
761
+ );
762
+ if (pointIndex == -1) {
763
+ app.readDevices[isDeviceInReadList].children[0].children.push(pointInTree);
764
+ } else {
765
+ app.readDevices[isDeviceInReadList].children[0].children[pointIndex] = pointInTree;
766
+ }
767
+ }
768
+ }
769
+ }
770
+ } else {
771
+ //search mstp devices
772
+ let mstpIndex = app.devices[foundIndex].children[1].children.findIndex((ele) => ele.deviceId == id);
773
+
774
+ if (mstpIndex !== -1) {
775
+ let mstpDevice = app.devices[foundIndex].children[1].children[mstpIndex];
776
+ let device = app.deviceList.find((ele) => {
777
+ if (ele.address.address) {
778
+ return ele.address.address == ip && ele.deviceId == id;
779
+ } else {
780
+ return ele.address == ip && ele.deviceId == id;
781
+ }
782
+ });
783
+
784
+ for (let pointName in importedDevice) {
785
+ let point = importedDevice[pointName];
786
+ if (pointName == "deviceName") {
787
+ app.nodeService.setDeviceDisplayName(device, point);
788
+ mstpDevice.label = point;
789
+ }
790
+ let pointInTree = mstpDevice.children[0].children.find(
791
+ (ele) => ele.data == pointName && ele.bacnetType == point.meta.objectId.type
792
+ );
793
+
794
+ if (pointInTree) {
795
+ pointInTree.label = point.displayName;
796
+
797
+ const isDeviceInReadList = app.readDevices
798
+ ? app.readDevices.findIndex((ele) => ele.deviceId == mstpDevice.deviceId)
799
+ : -1;
800
+
801
+ if (isDeviceInReadList == -1) {
802
+ //no read devices present, add new
803
+ let newReadParent = JSON.parse(JSON.stringify(mstpDevice));
804
+ newReadParent.children[0].children = [];
805
+ newReadParent.children[0].children.push(pointInTree);
806
+ if (app.readDevices) {
807
+ app.readDevices.push(newReadParent);
808
+ } else {
809
+ app.readDevices = [newReadParent];
810
+ }
811
+ } else {
812
+ // read device found, add point to existing
813
+ let pointIndex = app.readDevices[isDeviceInReadList].children[0].children.findIndex(
814
+ (ele) => ele.data == point.objectName
815
+ );
816
+ if (pointIndex == -1) {
817
+ app.readDevices[isDeviceInReadList].children[0].children.push(pointInTree);
818
+ } else {
819
+ app.readDevices[isDeviceInReadList].children[0].children[pointIndex] = pointInTree;
820
+ }
821
+ }
822
+ }
823
+ }
824
+ } else {
825
+ // not part of device list at all, notify user
826
+ }
827
+ }
828
+ }
829
+ }
830
+
831
+ app.$forceUpdate();
832
+ },
833
+ calculateMstpCount(slotProps) {
834
+ let count = 0;
835
+ slotProps.node.children.forEach(function (child) {
836
+ if (child.label.includes("MSTP")) {
837
+ count += child.children.length;
838
+ }
839
+ });
840
+
841
+ return count;
842
+ },
843
+ calculatePointCount(slotProps) {
844
+ let count = 0;
845
+ return count;
846
+ },
361
847
  },
362
848
  components: {
363
849
  "p-tree": primevue.tree,
364
850
  "p-button": primevue.button,
365
851
  "p-timeline": primevue.timeline,
366
- "p-progressbar": primevue.progressbar
367
- }
852
+ "p-progressbar": primevue.progressbar,
853
+ "p-dialog": primevue.dialog,
854
+ "p-input-text": primevue.inputtext,
855
+ },
368
856
  };
369
857
 
370
858
  let vueapp = createApp(App);
@@ -394,13 +882,26 @@
394
882
  document.getElementById("node-input-mqtt").checked = node.mqtt;
395
883
  document.getElementById("node-input-mqtt").onclick = handleMsgTypeClick;
396
884
 
397
- function handleCheckboxClick() {
885
+ var menu = document.querySelector(".context-menu");
886
+ window.addEventListener("click", (event) => {
887
+ menu.style.display = "none";
888
+ });
398
889
 
399
- if(this.id == "node-input-object_property_simplePayload") {
400
- document.getElementById("node-input-object_property_fullObject").checked = !document.getElementById("node-input-object_property_fullObject").checked;
890
+ var pointMenu = document.querySelector(".point-context-menu");
891
+ window.addEventListener("click", (event) => {
892
+ pointMenu.style.display = "none";
893
+ });
894
+
895
+ function handleCheckboxClick() {
896
+ if (this.id == "node-input-object_property_simplePayload") {
897
+ document.getElementById("node-input-object_property_fullObject").checked = !document.getElementById(
898
+ "node-input-object_property_fullObject"
899
+ ).checked;
401
900
  }
402
- if(this.id == "node-input-object_property_fullObject") {
403
- document.getElementById("node-input-object_property_simplePayload").checked = !document.getElementById("node-input-object_property_simplePayload").checked;
901
+ if (this.id == "node-input-object_property_fullObject") {
902
+ document.getElementById("node-input-object_property_simplePayload").checked = !document.getElementById(
903
+ "node-input-object_property_simplePayload"
904
+ ).checked;
404
905
  }
405
906
  }
406
907
 
@@ -413,33 +914,29 @@
413
914
  }
414
915
  }
415
916
 
416
- //create tabs
417
- let tabs = RED.tabs.create(
418
- {
419
- id: "node-input-read-tabs",
420
- onchange: function (tab) {
421
- $("#node-input-tabs-content").children().hide()
422
- $("#" + tab.id).show()
423
- }
424
- });
425
-
426
- tabs.addTab(
427
- {
428
- id: "read-networkTree-tab",
429
- label: "Device List"
430
- });
431
-
432
- tabs.addTab(
433
- {
434
- id: "read-readList-tab",
435
- label: "Read List"
436
- });
437
-
438
- tabs.addTab(
439
- {
440
- id: "read-properties-tab",
441
- label: "Properties"
442
- });
917
+ //create tabs
918
+ let tabs = RED.tabs.create({
919
+ id: "node-input-read-tabs",
920
+ onchange: function (tab) {
921
+ $("#node-input-tabs-content").children().hide();
922
+ $("#" + tab.id).show();
923
+ },
924
+ });
925
+
926
+ tabs.addTab({
927
+ id: "read-networkTree-tab",
928
+ label: "Device List",
929
+ });
930
+
931
+ tabs.addTab({
932
+ id: "read-readList-tab",
933
+ label: "Read List",
934
+ });
935
+
936
+ tabs.addTab({
937
+ id: "read-properties-tab",
938
+ label: "Properties",
939
+ });
443
940
 
444
941
  //remove loading animation
445
942
  let loadingGif = document.getElementById("loadingGif");
@@ -448,7 +945,6 @@
448
945
  loadingGif.style.display = "none";
449
946
  loadingText.style.display = "none";
450
947
  table.style.display = "inherit";
451
-
452
948
  },
453
949
  oneditsave: function () {
454
950
  let node = this;
@@ -456,7 +952,7 @@
456
952
  if (node.vm.$data.readDevices) node.readDevices = node.vm.$data.readDevices;
457
953
  if (node.vm.$data.pointsToRead) node.pointsToRead = node.vm.$data.pointsToRead;
458
954
  //clear reload data function
459
- if(node.reloadTimer) {
955
+ if (node.reloadTimer) {
460
956
  clearInterval(node.reloadTimer);
461
957
  node.reloadTimer = null;
462
958
  }
@@ -467,470 +963,424 @@
467
963
  if (node.vm.$data.readDevices) node.readDevices = node.vm.$data.readDevices;
468
964
  if (node.vm.$data.pointsToRead) node.pointsToRead = node.vm.$data.pointsToRead;
469
965
  //clear reload data function
470
- if(node.reloadTimer) {
966
+ if (node.reloadTimer) {
471
967
  clearInterval(node.reloadTimer);
472
968
  node.reloadTimer = null;
473
969
  }
474
- }
970
+ },
475
971
  });
476
-
477
972
  </script>
478
973
 
479
974
  <script type="text/html" data-template-name="Bacnet-Discovery">
480
- <style>
481
-
482
- .p-treenode-label {
483
- color: black;
484
- width: 100%;
485
- }
486
- .p-tree {
487
- background: inherit !important;
488
- border: inherit !important;
489
- padding-right: 0px !important;
490
- }
491
- .p-button {
492
- margin-right: .5rem;
493
- }
494
- .addPointButton {
495
- float: right;
496
- }
497
- .minusPointButton {
498
- float: right;
499
- }
500
- .addPointButton:hover, .minusPointButton:hover {
501
- background: #d5d5d5 !important;
502
- }
503
- .pointLabel {
504
- font-weight: 400;
505
- }
506
- .deviceLabel {
507
- font-weight: 100;
508
- }
509
- .removeAllButton {
510
- float: right;
511
- }
512
- .addAllButton {
513
- float: right;
514
- }
515
- .bacnetbutton {
516
- background: none;
517
- border: none;
518
- color: black;
519
- font-weight: 400;
520
- }
521
- .bacnetbutton:hover {
522
- background: #f0f0f0;
523
- border-radius: 10px;
524
- }
525
- .allFunctionsText {
526
- font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Helvetica, Arial, sans-serif, "Apple Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol" !important;
527
- padding-left: 5px;
528
- }
529
- .p-treenode-children > li > .p-treenode-children > li {
530
- font-size: 14px;
531
- /* height: 35px; */
532
- color: #b5b5b5 !important;
533
- }
534
- .p-treenode-children > li > .p-treenode-children > .p-treenode-label {
535
- color: #b5b5b5 !important;
536
- }
537
- /*
538
- .p-treenode-children > li > .p-treenode-children > li:last-child {
539
- padding-bottom: 30px;
540
- }
541
- */
542
- .p-tree-toggler:enabled:hover {
543
- background: #d5d5d5 !important;
544
- }
545
- .p-tree-toggler:focus {
546
- border: none !important;
547
- box-shadow: none !important;
548
- }
549
- .deviceStatus {
550
- font-size: 10px;
551
- }
552
- .statusOnline {
553
- color: #11c511;
554
- }
555
- .statusOffline {
556
- color: red;
557
- }
558
-
559
- .deviceLabel {
560
- position: absolute;
561
- }
562
- .objectPropertiesLabel {
563
- display: flex !important;
564
- flex-direction: row;
565
- align-items: center;
566
- }
567
- .p-tree .p-tree-container .p-treenode .p-treenode-content:focus {
568
- outline: 0 none !important;
569
- outline-offset: 0 !important;
570
- box-shadow: inset 0 0 0 1px #bdbdbd !important;
571
- }
572
- .checkbox-round {
573
- width: 13px !important;
574
- height: 13px !important;
575
- background-color: white;
576
- border-radius: 50%;
577
- vertical-align: middle;
578
- border: 1px solid #ddd;
579
- appearance: none;
580
- -webkit-appearance: none;
581
- outline: none;
582
- cursor: pointer;
583
- }
584
- .checkbox-round:checked {
585
- background-color: #00AEEF;
586
- }
587
- .checkbox-round:focus {
588
- outline: none !important;
589
- }
590
- .p-tree-filter:focus, .p-tree-filter:focus-visible, .p-inputtext:enabled:hover {
591
- box-shadow: inset 0 0 0 0.15rem #dfdcdc !important;
592
- border-color: transparent !important;
593
- }
594
- .bacnetbutton > .pi {
595
- display: flex;
596
- }
597
- .p-tree-container {
598
- margin: 0 !important;
599
- }
600
- .reloadButtonIcon {
601
- font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Helvetica, Arial, sans-serif, "Apple Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol" !important;
602
- }
603
- .removeAllDevicesButton {
604
- border: none;
605
- background: none;
606
- font-size: 14px !important;
607
- float: right;
608
- display: flex;
609
- align-items: center;
610
- }
611
- .removeAllDevicesButton:hover {
612
- background-color: #d5d5d5;
613
- border-radius: 10px;
614
- }
615
- .reloadButton {
616
- border: none;
617
- background: none;
618
- font-size: 14px !important;
619
- float: right;
620
- }
621
- .rebuildDataButton {
622
- border: none;
623
- background: none;
624
- font-size: 14px !important;
625
- float: right;
626
- }
627
- .reloadButton:hover {
628
- background-color: #d5d5d5;
629
- border-radius: 10px;
630
- }
631
- .rebuildDataButton:hover {
632
- background-color: #d5d5d5;
633
- border-radius: 10px;
634
- }
635
- .headerDiv {
636
- display: flex;
637
- flex-direction: row;
638
- flex-wrap: nowrap;
639
- justify-content: space-around;
640
- height: 20px;
641
- }
642
- .msgTypeDiv {
643
- display: flex;
644
- align-items: flex-start;
645
- flex-direction: column;
646
- }
647
- .p-progressbar .p-progressbar-value {
648
- background: #00AEEF !important;
649
- }
650
- .p-treenode-label {
651
- overflow: hidden;
652
- white-space: nowrap;
653
- text-overflow: ellipsis;
654
- }
655
- .buttonGroup {
656
- padding-left: 7px;
657
- }
658
- #read-readList-tab {
659
- display: flex;
660
- flex-direction: column;
661
- }
662
- .removeAllDevicesDiv {
663
- font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Helvetica, Arial, sans-serif, "Apple Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol";
664
- padding-right: 20px;
665
- }
666
-
667
- .p-tree .p-treenode-children {
668
- padding: 0 !important;
669
- }
670
-
671
- .p-tree-toggler .p-link {
672
- width: 25px !important;
673
- }
674
-
675
-
676
- /*
677
- .p-treenode-content {
678
- height: 15px;
679
- }
680
- */
681
-
682
-
683
- </style>
684
-
685
- <div class="form-row">
686
- <label for="node-input-name"><i class="icon-tag"></i><span data-i18n="bitpool-bacnet.label.name"></span> Name</label>
687
- <input type="text" id="node-input-name" placeholder="Name">
688
- </div>
689
-
690
- <div class='form-row node-input-read-tabs-row'>
691
- <ul style='min-width:600px;margin-bottom:20px' id='node-input-read-tabs'></ul>
692
- </div>
693
-
694
- <div id='node-input-tabs-content'>
695
-
696
- <!--
975
+ <div class="form-row">
976
+ <label for="node-input-name"><i class="icon-tag"></i><span data-i18n="bitpool-bacnet.label.name"></span> Name</label>
977
+ <input type="text" id="node-input-name" placeholder="Name" />
978
+ </div>
979
+
980
+ <div class="form-row node-input-read-tabs-row">
981
+ <ul style="min-width:600px;margin-bottom:20px" id="node-input-read-tabs"></ul>
982
+ </div>
983
+
984
+ <div id="node-input-tabs-content">
985
+ <!-- Start Device Context Menu -->
986
+ <ul
987
+ class="red-ui-menu red-ui-menu-dropdown red-ui-menu-dropdown-direction-right red-ui-menu-dropdown-noicons red-ui-menu-dropdown-submenus context-menu">
988
+ <li class="context-menu-item" @click="handleContextMenuClick('purgeDevice')">
989
+ <a class="red-ui-menu-label">
990
+ <i class="pi pi-wrench context-menu-icon"></i>
991
+ <span class="context-menu-item-text">Purge Device</span>
992
+ </a>
993
+ </li>
994
+ <li class="context-menu-item" @click="handleContextMenuClick('updatePoints')">
995
+ <a class="red-ui-menu-label">
996
+ <i class="pi pi-refresh context-menu-icon"></i>
997
+ <span class="context-menu-item-text">Update Points</span>
998
+ </a>
999
+ </li>
1000
+ <li class="red-ui-menu-divider"></li>
1001
+ <li class="context-menu-item" @click="handleContextMenuClick('addAllPoints')">
1002
+ <a class="red-ui-menu-label">
1003
+ <i class="pi pi-plus-circle context-menu-icon"></i>
1004
+ <span class="context-menu-item-text">Add All Points </span>
1005
+ </a>
1006
+ </li>
1007
+ <li class="context-menu-item" @click="handleContextMenuClick('removeAllPoints')">
1008
+ <a class="red-ui-menu-label">
1009
+ <i class="pi pi-minus-circle context-menu-icon"></i>
1010
+ <span class="context-menu-item-text">Remove All Points</span>
1011
+ </a>
1012
+ </li>
1013
+ <li class="red-ui-menu-divider"></li>
1014
+ <li class="context-menu-item" @click="handleContextMenuClick('setDeviceName')">
1015
+ <a class="red-ui-menu-label">
1016
+ <i class="pi pi-pencil context-menu-icon"></i>
1017
+ <span class="context-menu-item-text">Set Device Name</span>
1018
+ </a>
1019
+ </li>
1020
+ </ul>
1021
+ <!-- End Device Context Menu -->
1022
+
1023
+ <!-- Start Point Context Menu -->
1024
+ <ul
1025
+ class="red-ui-menu red-ui-menu-dropdown red-ui-menu-dropdown-direction-right red-ui-menu-dropdown-noicons red-ui-menu-dropdown-submenus point-context-menu">
1026
+ <li class="context-menu-item" @click="handlePointContextMenuClick('setPointName')">
1027
+ <a class="red-ui-menu-label">
1028
+ <i class="pi pi-pencil context-menu-icon"></i>
1029
+ <span class="context-menu-item-text">Set Point Name</span>
1030
+ </a>
1031
+ </li>
1032
+ <li class="red-ui-menu-divider"></li>
1033
+ <li class="context-menu-item" @click="handlePointContextMenuClick('updatePoint')">
1034
+ <a class="red-ui-menu-label">
1035
+ <i class="pi pi-refresh context-menu-icon"></i>
1036
+ <span class="context-menu-item-text">Update Point</span>
1037
+ </a>
1038
+ </li>
1039
+ </ul>
1040
+ <!-- End Point Context Menu -->
1041
+
1042
+ <!--
697
1043
  *
698
1044
  *
699
1045
  * Network Tree tab
700
1046
  *
701
1047
  *
702
1048
  -->
703
- <div id='read-networkTree-tab' style='display:none'>
704
-
705
- <div id='read-networkTree-tab-content' class="networkTreeContent" style='display:none'>
706
-
707
- <div class="headerDiv">
708
- <a class="countStatus" style="margin-left: 15px;">Count: {{deviceCount}} device(s)</a>
709
- <p-progressbar :value="progressBarValue" :show-value="true" style="width: 300px; height: 20px;"></p-progressbar>
710
- <div class="buttonGroup" style="padding-left: 15px;">
711
- <button @click="rebuildDataModel()" class="rebuildDataButton" title="Rebuild Data Model" >
712
- <i class="pi pi-wrench" style="color: #ff0000;"></i>
713
- </button>
714
- <button @click="getData()" class="reloadButton" title="Reload Data">
715
- <i class="pi pi-refresh" style="color: #00AEEF;"></i>
716
- </button>
717
- <button @click="addAllDevices()" class="reloadButton" title="Add all devices">
718
- <i class="pi pi-plus" style="color: #00AEEF;"></i>
719
- </button>
720
- </div>
721
- </div>
722
-
723
-
724
- <div id="deviceListApp">
725
- <p-tree :value="devices" selectable="false" :filter="true" filterMode="lenient" filterPlaceholder="No results found." v-if="hasData()">
726
- <template #device="slotProps">
727
- <div v-if="isDeviceActive(slotProps)" class="deviceLabelParent">
728
- <b class="deviceLabel">{{slotProps.node.label}} <b class="statusOnline deviceStatus">Online</b></b>
729
- </div>
730
- <div v-else>
731
- <b class="deviceLabel">{{slotProps.node.label}} <b class="statusOffline deviceStatus">Offline</b></b>
732
- </div>
733
-
734
- <template v-if="isDeviceAdded(slotProps)">
735
- <button class="addAllButton bacnetbutton">
736
- <i class="pi pi-check-circle " style="color: #00AEEF;"></i>
737
- </button>
738
- </template>
739
- <template v-else>
740
- <button @click="addAllClicked(slotProps)" class="addAllButton bacnetbutton">
741
- <i class="pi pi-plus-circle "><a class="allFunctionsText">Add all</a></i>
742
- </button>
743
- </template>
744
-
745
- <button @click="removeAllClicked(slotProps, this)" class="removeAllButton bacnetbutton">
746
- <i class="pi pi-minus-circle "><a class="allFunctionsText">Remove all</a></i>
747
- </button>
748
- </template>
749
-
750
- <!-- <template #pointFolder="slotProps">
751
-
752
- </template>
753
-
754
-
755
- <template #deviceFolder="slotProps">
756
-
757
- </template> -->
758
-
759
- <template #point="slotProps" v-model:class="pointContent">
760
- <b class="pointLabel">{{slotProps.node.label}}</b>
761
-
762
- <template v-if="isSlotAdded(slotProps)">
763
- <button class="addPointButton bacnetbutton">
764
- <i class="pi pi-check-circle " style="color: #00AEEF;"></i>
765
- </button>
766
- </template>
767
- <template v-else>
768
- <button @click="addPointClicked(slotProps, this)" class="addPointButton bacnetbutton">
769
- <i class="pi pi-plus-circle "></i>
770
- </button>
771
- </template>
772
-
773
- <button @click="removePointClicked(slotProps, this)" class="minusPointButton bacnetbutton">
774
- <i class="pi pi-minus-circle "></i>
775
- </button>
776
- </template>
777
- </p-tree>
778
- <div v-else style="text-align: center; padding-top: 20px;">
779
- <a>No devices found.</a>
780
- </div>
781
- </div>
1049
+ <div id="read-networkTree-tab" style="display:none">
1050
+ <div id="read-networkTree-tab-content" class="networkTreeContent" style="display:none">
1051
+ <p-dialog
1052
+ v-model:visible="showDeviceNameDialog"
1053
+ modal
1054
+ header="Set Display Name"
1055
+ :style="{ width: '25rem' }"
1056
+ class="deviceNameDialog">
1057
+ <div class="flex align-items-center gap-3 mb-3">
1058
+ <label for="displayName" class="font-semibold">Name</label>
1059
+ <p-input-text id="displayName" class="flex-auto" autocomplete="off" v-model="deviceDisplayNameValue" />
1060
+ </div>
1061
+ <div class="flex justify-content-end gap-2">
1062
+ <p-button
1063
+ class="diplayNameDialogCancel"
1064
+ type="button"
1065
+ label="Cancel"
1066
+ severity="secondary"
1067
+ @click="cancelDisplayNameDialog"></p-button>
1068
+ <p-button class="diplayNameDialogSave" type="button" label="Save" @click="setDeviceName"></p-button>
1069
+ </div>
1070
+ </p-dialog>
1071
+
1072
+ <p-dialog
1073
+ v-model:visible="showPointNameDialog"
1074
+ modal
1075
+ header="Set Point Name"
1076
+ :style="{ width: '25rem' }"
1077
+ class="deviceNameDialog">
1078
+ <div class="flex align-items-center gap-3 mb-3">
1079
+ <label for="displayName" class="font-semibold">Name</label>
1080
+ <p-input-text id="displayName" class="flex-auto" autocomplete="off" v-model="pointDisplayNameValue" />
1081
+ </div>
1082
+ <div class="flex justify-content-end gap-2">
1083
+ <p-button
1084
+ class="diplayNameDialogCancel"
1085
+ type="button"
1086
+ label="Cancel"
1087
+ severity="secondary"
1088
+ @click="cancelPointNameDialog"></p-button>
1089
+ <p-button class="diplayNameDialogSave" type="button" label="Save" @click="setPointName"></p-button>
1090
+ </div>
1091
+ </p-dialog>
1092
+
1093
+ <div class="headerDiv">
1094
+ <a class="countStatus" style="margin-left: 15px;">Count: {{deviceCount}} device(s)</a>
1095
+ <p-progressbar :value="progressBarValue" :show-value="true" style="width: 300px; height: 20px;"></p-progressbar>
1096
+ <div class="buttonGroup" style="padding-left: 15px;">
1097
+ <button @click="rebuildDataModel()" class="rebuildDataButton" title="Rebuild Data Model">
1098
+ <i class="pi pi-wrench" style="color: #ff0000;"></i>
1099
+ </button>
1100
+ <button @click="getData()" class="reloadButton" title="Reload Data">
1101
+ <i class="pi pi-refresh" style="color: #00AEEF;"></i>
1102
+ </button>
1103
+ <button @click="addAllDevices()" class="reloadButton" title="Add all devices">
1104
+ <i class="pi pi-plus" style="color: #00AEEF;"></i>
1105
+ </button>
1106
+ </div>
1107
+ </div>
1108
+
1109
+ <div id="deviceListApp" class="readDeviceList">
1110
+ <p-tree
1111
+ :value="devices"
1112
+ selectable="false"
1113
+ :filter="true"
1114
+ filterMode="lenient"
1115
+ filterPlaceholder="No results found."
1116
+ v-if="hasData()">
1117
+ <template #device="slotProps">
1118
+ <div @contextmenu="onDeviceRightClick(slotProps, $event)" class="p-treenode-label">
1119
+ <div v-if="isDeviceActive(slotProps)" class="deviceLabelParent">
1120
+ <b class="deviceLabel">
1121
+ <span class="statusOnline deviceStatus dotOnline dot"></span>
1122
+ {{slotProps.node.label}}
1123
+ <span v-if="hasMstpDevices(slotProps)" class="mstpDeviceCount"
1124
+ >&nbsp; {{calculateMstpCount(slotProps)}} &nbsp;</span
1125
+ >
1126
+ </b>
1127
+ </div>
1128
+
1129
+ <div v-else>
1130
+ <b class="deviceLabel">
1131
+ <span class="statusOffline deviceStatus dotOffline dot"></span>
1132
+ {{slotProps.node.label}}
1133
+ <span v-if="hasMstpDevices(slotProps)" class="mstpDeviceCount"
1134
+ >&nbsp; {{calculateMstpCount(slotProps)}} &nbsp;</span
1135
+ >
1136
+ </b>
782
1137
  </div>
783
1138
 
784
- <img id="loadingGif" src="resources/@bitpoolos/edge-bacnet/BitpoolCube_Blue_350.gif" style="width: 70px; display: block; margin-left: auto; margin-right: auto;" />
785
- <p id="loadingText" style="display: block; margin-left: auto; margin-right: auto; text-align: center; color: #505050;">Loading current network info</p>
786
- </div>
1139
+ <template v-if="isDeviceAdded(slotProps)">
1140
+ <button class="addAllButton bacnetbutton">
1141
+ <i class="pi pi-check-circle " style="color: #00AEEF;"></i>
1142
+ </button>
1143
+ </template>
1144
+ <template v-else>
1145
+ <button @click="addAllClicked(slotProps)" class="addAllButton bacnetbutton">
1146
+ <i class="pi pi-plus-circle "><a class="allFunctionsText">Add all</a></i>
1147
+ </button>
1148
+ </template>
1149
+
1150
+ <button @click="removeAllClicked(slotProps, this)" class="removeAllButton bacnetbutton">
1151
+ <i class="pi pi-minus-circle "><a class="allFunctionsText">Remove all</a></i>
1152
+ </button>
1153
+ </div>
1154
+ </template>
1155
+
1156
+ <template #point="slotProps" v-model:class="pointContent">
1157
+ <div @contextmenu="onPointRightClick(slotProps, $event)">
1158
+ <b class="pointLabel">{{slotProps.node.label}}</b>
1159
+
1160
+ <template v-if="isSlotAdded(slotProps)">
1161
+ <button class="addPointButton bacnetbutton">
1162
+ <i class="pi pi-check-circle " style="color: #00AEEF;"></i>
1163
+ </button>
1164
+ </template>
1165
+ <template v-else>
1166
+ <button @click="addPointClicked(slotProps, this)" class="addPointButton bacnetbutton">
1167
+ <i class="pi pi-plus-circle "></i>
1168
+ </button>
1169
+ </template>
1170
+
1171
+ <button @click="removePointClicked(slotProps, this)" class="minusPointButton bacnetbutton">
1172
+ <i class="pi pi-minus-circle "></i>
1173
+ </button>
1174
+ </div>
1175
+ </template>
1176
+ </p-tree>
1177
+ <div v-else style="text-align: center; padding-top: 20px;">
1178
+ <a>Building Tree...</a>
1179
+ </div>
1180
+ </div>
1181
+ </div>
1182
+
1183
+ <img
1184
+ id="loadingGif"
1185
+ src="resources/@bitpoolos/edge-bacnet/BitpoolCube_Blue_350.gif"
1186
+ style="width: 70px; display: block; margin-left: auto; margin-right: auto;" />
1187
+ <p id="loadingText" style="display: block; margin-left: auto; margin-right: auto; text-align: center; color: #505050;">
1188
+ Loading current network info
1189
+ </p>
1190
+ </div>
787
1191
 
788
- <!--
1192
+ <!--
789
1193
  *
790
1194
  *
791
1195
  * Read list tab
792
1196
  *
793
1197
  *
794
1198
  -->
795
- <div id='read-readList-tab' style='display:none'>
796
- <div class="removeAllDevicesDiv" >
797
- <button @click="removeAllDevices()" class="removeAllDevicesButton" title="Remove all devices">
798
- <i class="pi pi-minus-circle" style="color: #ff0000; padding-right: 5px;"> </i><a style="color: #ff0000;">Remove All Devices</a>
799
- </button>
800
- </div>
801
-
802
- <p-tree :value="readDevices">
803
- <template #device="slotProps">
804
- <b class="deviceLabel">{{slotProps.node.label}} </b>
805
- <button @click="removeAllClicked(slotProps, this)" class="removeAllButton bacnetbutton">
806
- <i class="pi pi-minus-circle "><a class="allFunctionsText">Remove all</a></i>
807
- </button>
808
- </template>
809
-
810
- <template #point="slotProps" v-model:class="pointContent">
811
- <b class="pointLabel">{{slotProps.node.label}}</b>
812
- <button @click="removePointClicked(slotProps, this)" class="minusPointButton bacnetbutton">
813
- <i class="pi pi-minus-circle "></i>
814
- </button>
815
- </template>
816
- </p-tree>
817
-
818
- </div>
819
- <!--
1199
+ <div id="read-readList-tab" style="display:none">
1200
+ <div class="removeAllDevicesDiv">
1201
+ <button @click="exportReadList()" class="export-points-button" title="Export Read List">
1202
+ <i class="fa fa-cloud-download"> </i><a id="fileLabelText" style="padding-left: 10px;">Export</a>
1203
+ </button>
1204
+ <a id="exportJSON" style="display: none"></a>
1205
+
1206
+ <button @click="importReadList()" class="export-points-button" title="Import Read List">
1207
+ <i class="fa fa-cloud-upload" id="importReadListButton"></i
1208
+ ><a id="fileLabelText" style="padding-left: 10px;">Import</a>
1209
+ </button>
1210
+ <input type="file" id="readlist-file-upload" accept="application/JSON" class="inputStyle" style="display: none;" />
1211
+
1212
+ <button @click="removeAllDevices()" class="removeAllDevicesButton" title="Remove all devices">
1213
+ <i class="pi pi-minus-circle" style="color: #ff0000; padding-right: 5px;"> </i
1214
+ ><a style="color: #ff0000;">Remove All Devices</a>
1215
+ </button>
1216
+ </div>
1217
+
1218
+ <p-tree :value="readDevices">
1219
+ <template #device="slotProps">
1220
+ <b class="deviceLabel">{{slotProps.node.label}} </b>
1221
+ <button @click="removeAllClicked(slotProps, this)" class="removeAllButton bacnetbutton">
1222
+ <i class="pi pi-minus-circle "><a class="allFunctionsText">Remove all</a></i>
1223
+ </button>
1224
+ </template>
1225
+
1226
+ <template #point="slotProps" v-model:class="pointContent">
1227
+ <b class="pointLabel">{{slotProps.node.label}}</b>
1228
+ <button @click="removePointClicked(slotProps, this)" class="minusPointButton bacnetbutton">
1229
+ <i class="pi pi-minus-circle "></i>
1230
+ </button>
1231
+ </template>
1232
+ </p-tree>
1233
+ </div>
1234
+ <!--
820
1235
  *
821
1236
  *
822
1237
  * Read Properties tab
823
1238
  *
824
1239
  *
825
1240
  -->
826
- <div id='read-properties-tab' style='display:none'>
827
- <div class="form-row" style="display: flex; width: fit-content; flex-wrap: nowrap; flex-direction: column;">
828
-
829
- <label for="node-input-object_properties_group" style="display: flex; align-items: center; white-space: nowrap; text-decoration: underline;">Object Properties:</label>
830
-
831
-
832
- <div id="node-input-object_properties_group" style="display: flex; align-items: flex-start; flex-direction: column; padding-left: 50px;">
833
- <div class="objectPropertiesLabel">
834
- <label for="node-input-object_property_simplePayload" style="padding-left: 4px; width: auto; align-items: start;" class="objectPropertiesLabel">
835
- <i class="icon-tag"></i> <span data-i18n="bitpool-bacnet.label.object_property_simplePayload"></span>
836
- <input style="margin-left: 5px;" class=" objectProp" type="checkbox" id="node-input-object_property_simplePayload">
837
- <a style="white-space: nowrap; padding-left: 20px;">Simple Payload</a>
838
- </label>
839
- </div>
840
-
841
- <div class="objectPropertiesLabel">
842
- <label for="node-input-object_property_fullObject" style="padding-left: 4px; width: auto; align-items: start;" class="objectPropertiesLabel">
843
- <i class="icon-tag"></i> <span data-i18n="bitpool-bacnet.label.object_property_fullObject"></span>
844
- <input style="margin-left: 5px;" class=" objectProp" type="checkbox" id="node-input-object_property_fullObject">
845
- <a style="white-space: nowrap; padding-left: 20px;">Full Object</a>
846
- </label>
847
- </div>
848
-
849
- </div>
850
- </div>
851
-
852
- <hr>
853
-
854
- <div class="form-row msgTypeDiv" style="display: flex;">
855
- <label for="node-input-msgType" style="text-decoration: underline;">
856
- Message type:
857
- </label>
858
- <div id="node-input-msgType" style="display: flex; align-items: flex-start; flex-direction: column; padding-left: 50px;">
859
-
860
- <!-- class= checkbox-round -->
861
- <div style="display: flex; flex-direction: row; align-items: flex-start;"><input class="checkbox-round" type="checkbox" id="node-input-json" style="width: 13px; margin-left: 5px;"/><label for="node-input-json" style="padding-left: 20px; width: fit-content;">Block per device</label> </div>
862
- <div style="display: flex; flex-direction: row; align-items: flex-start;"><input class="checkbox-round" type="checkbox" id="node-input-mqtt" style="width: 13px; margin-left: 5px;"/><label for="node-input-mqtt" style="padding-left: 20px; width: fit-content;">Individual msgs</label> </div>
863
-
864
- </div>
865
- </div>
866
-
867
- <div class="form-row">
868
- <label for="node-input-roundDecimal">
869
- Decimal place precision
870
- </label>
871
- <input type="number" id="node-input-roundDecimal" placeholder="None">
872
- </div>
873
-
874
- <label for="node-input-hiddenDeployToggle" style="display: none !important;" >
875
- <i class="icon-tag" style="display: none !important;"></i> <span data-i18n="bitpool-bacnet.label.hiddenDeployToggle" style="display: none !important;"></span>
876
- <input style="display: none !important;" type="checkbox" id="node-input-hiddenDeployToggle">
1241
+ <div id="read-properties-tab" style="display:none">
1242
+ <div class="form-row" style="display: flex; width: fit-content; flex-wrap: nowrap; flex-direction: column;">
1243
+ <label
1244
+ for="node-input-object_properties_group"
1245
+ style="display: flex; align-items: center; white-space: nowrap; text-decoration: underline;"
1246
+ >Object Properties:</label
1247
+ >
1248
+
1249
+ <div
1250
+ id="node-input-object_properties_group"
1251
+ style="display: flex; align-items: flex-start; flex-direction: column; padding-left: 50px;">
1252
+ <div class="objectPropertiesLabel">
1253
+ <label
1254
+ for="node-input-object_property_simplePayload"
1255
+ style="padding-left: 4px; width: auto; align-items: start;"
1256
+ class="objectPropertiesLabel">
1257
+ <i class="icon-tag"></i> <span data-i18n="bitpool-bacnet.label.object_property_simplePayload"></span>
1258
+ <input
1259
+ style="margin-left: 5px;"
1260
+ class=" objectProp"
1261
+ type="checkbox"
1262
+ id="node-input-object_property_simplePayload" />
1263
+ <a style="white-space: nowrap; padding-left: 20px;">Simple Payload</a>
1264
+ </label>
1265
+ </div>
1266
+
1267
+ <div class="objectPropertiesLabel">
1268
+ <label
1269
+ for="node-input-object_property_fullObject"
1270
+ style="padding-left: 4px; width: auto; align-items: start;"
1271
+ class="objectPropertiesLabel">
1272
+ <i class="icon-tag"></i> <span data-i18n="bitpool-bacnet.label.object_property_fullObject"></span>
1273
+ <input
1274
+ style="margin-left: 5px;"
1275
+ class=" objectProp"
1276
+ type="checkbox"
1277
+ id="node-input-object_property_fullObject" />
1278
+ <a style="white-space: nowrap; padding-left: 20px;">Full Object</a>
877
1279
  </label>
1280
+ </div>
878
1281
  </div>
879
-
1282
+ </div>
1283
+
1284
+ <hr />
1285
+
1286
+ <div class="form-row msgTypeDiv" style="display: flex;">
1287
+ <label for="node-input-msgType" style="text-decoration: underline;"> Message type: </label>
1288
+ <div
1289
+ id="node-input-msgType"
1290
+ style="display: flex; align-items: flex-start; flex-direction: column; padding-left: 50px;">
1291
+ <!-- class= checkbox-round -->
1292
+ <div style="display: flex; flex-direction: row; align-items: flex-start;">
1293
+ <input
1294
+ class="checkbox-round"
1295
+ type="checkbox"
1296
+ id="node-input-json"
1297
+ style="width: 13px; margin-left: 5px;" /><label
1298
+ for="node-input-json"
1299
+ style="padding-left: 20px; width: fit-content;"
1300
+ >Block per device</label
1301
+ >
1302
+ </div>
1303
+ <div style="display: flex; flex-direction: row; align-items: flex-start;">
1304
+ <input
1305
+ class="checkbox-round"
1306
+ type="checkbox"
1307
+ id="node-input-mqtt"
1308
+ style="width: 13px; margin-left: 5px;" /><label
1309
+ for="node-input-mqtt"
1310
+ style="padding-left: 20px; width: fit-content;"
1311
+ >Individual msgs</label
1312
+ >
1313
+ </div>
1314
+ </div>
1315
+ </div>
1316
+
1317
+ <div class="form-row">
1318
+ <label for="node-input-roundDecimal"> Decimal place precision </label>
1319
+ <input type="number" id="node-input-roundDecimal" placeholder="None" />
1320
+ </div>
1321
+
1322
+ <label for="node-input-hiddenDeployToggle" style="display: none !important;">
1323
+ <i class="icon-tag" style="display: none !important;"></i>
1324
+ <span data-i18n="bitpool-bacnet.label.hiddenDeployToggle" style="display: none !important;"></span>
1325
+ <input style="display: none !important;" type="checkbox" id="node-input-hiddenDeployToggle" />
1326
+ </label>
1327
+
1328
+ <div class="form-row">
1329
+ <label for="node-input-exportCsv"> Points List </label>
1330
+ <label for="points-export" class="export-points-button" @click="exportPointListCsv()">
1331
+ <i class="fa fa-cloud-download" id="fileLabel"></i> <a id="points-export" style="padding-left: 10px;">Export</a>
1332
+ </label>
1333
+ </div>
880
1334
  </div>
881
-
1335
+ </div>
882
1336
  </script>
883
1337
  <script type="text/html" data-help-name="Bacnet-Discovery">
884
- <p> A node used to view, select devices, device points, and point properties to add to the Read polling list. </p>
885
-
886
- <h3><strong>Device List</strong></h3>
887
- <ol class="node-ports">
888
- <p>
889
- This tab displays the devices and device points that are a result of a network Discover. The data is broken down and listed as Devices, Points for the Device,
890
- and Point properties for the Points.
891
-
892
- On this tab the user may choose specific points to read, or all of the points present in a device.
893
-
894
- The reload button may be used to show any new data that may have been recieved by the bitpool BACnet node.
895
-
896
- Please note: Data can only be shown here once a Discover sucessfully recieves a response from online devices on the network
897
- </p>
898
-
899
- </ol>
900
-
901
- <h3><strong>Read List</strong></h3>
902
- <ol class="node-ports">
903
- <p>
904
- This tab shows all of the devices and points that have been chosen to read.
905
- </p>
906
-
907
- </ol>
908
-
909
- <h3><strong>Properties</strong></h3>
910
- <ol class="node-ports">
911
- <p>
912
- This tab shows all of the point properties the user may choose to read from the points detailed in the Read List tab.
913
- Here the user may also choose the output format of the Read data, as MQTT compatible Individual msgs, or a JSON block per device.
914
- </p>
915
-
916
- </ol>
917
-
918
- <h3><strong>Examples</strong></h3>
919
- <p>For example flows, please use the examples section for this node. These examples can be found at: Node-red hamburger menu on top right -> Import -> Examples -> @bitpoolos/edge-bacnet</p>
920
- <p>To find captured examples of settings and flows, please go to our wiki <a href="https://wiki.bitpool.com/en/edge/apps/bitpool-edge/nr-bacnet">here</a></p>
921
-
922
-
923
-
924
-
925
- <h3>Resources:</h3>
926
-
927
- <p><a href="https://youtu.be/4K7mVxfvfbc">Video Walk-through </a></p>
928
-
929
- <h4><strong>Online Docs:</strong></h4>
930
- <ul type="1">
931
- <li><a href="https://www.bitpool.com/">bitpool.com</a> - check us out here.</li>
932
- <li><a href="https://app.bitpool.com/">app.bitpool.com</a> - set up your account.</li>
933
- <li><a href="https://wiki.bitpool.com/">wiki.bitpool.com</a> - find more documentation.</li>
934
- <li><a href="https://bacnet.org/">BACnet</a> - find more about the protocol.</li>
935
- </ul>
1338
+ <p>A node used to view, select devices, device points, and point properties to add to the Read polling list.</p>
1339
+
1340
+ <h3><strong>Device List</strong></h3>
1341
+ <ol class="node-ports">
1342
+ <p>
1343
+ This tab displays the devices and device points that are a result of a network Discover. The data is broken down and
1344
+ listed as Devices, Points for the Device, and Point properties for the Points. On this tab the user may choose specific
1345
+ points to read, or all of the points present in a device. The reload button may be used to show any new data that may
1346
+ have been recieved by the bitpool BACnet node. Please note: Data can only be shown here once a Discover sucessfully
1347
+ recieves a response from online devices on the network
1348
+ </p>
1349
+ </ol>
1350
+
1351
+ <h3><strong>Read List</strong></h3>
1352
+ <ol class="node-ports">
1353
+ <p>This tab shows all of the devices and points that have been chosen to read.</p>
1354
+ </ol>
1355
+
1356
+ <h3><strong>Properties</strong></h3>
1357
+ <ol class="node-ports">
1358
+ <p>
1359
+ This tab shows all of the point properties the user may choose to read from the points detailed in the Read List tab.
1360
+ Here the user may also choose the output format of the Read data, as MQTT compatible Individual msgs, or a JSON block
1361
+ per device.
1362
+ </p>
1363
+ </ol>
1364
+
1365
+ <h3><strong>Examples</strong></h3>
1366
+ <p>
1367
+ For example flows, please use the examples section for this node. These examples can be found at: Node-red hamburger menu
1368
+ on top right -> Import -> Examples -> @bitpoolos/edge-bacnet
1369
+ </p>
1370
+ <p>
1371
+ To find captured examples of settings and flows, please go to our wiki
1372
+ <a href="https://wiki.bitpool.com/en/edge/apps/bitpool-edge/nr-bacnet">here</a>
1373
+ </p>
1374
+
1375
+ <h3>Resources:</h3>
1376
+
1377
+ <p><a href="https://youtu.be/4K7mVxfvfbc">Video Walk-through </a></p>
1378
+
1379
+ <h4><strong>Online Docs:</strong></h4>
1380
+ <ul type="1">
1381
+ <li><a href="https://www.bitpool.com/">bitpool.com</a> - check us out here.</li>
1382
+ <li><a href="https://app.bitpool.com/">app.bitpool.com</a> - set up your account.</li>
1383
+ <li><a href="https://wiki.bitpool.com/">wiki.bitpool.com</a> - find more documentation.</li>
1384
+ <li><a href="https://bacnet.org/">BACnet</a> - find more about the protocol.</li>
1385
+ </ul>
936
1386
  </script>