node-red-contrib-lorawan-bacnet-server 1.2.0

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.

Potentially problematic release.


This version of node-red-contrib-lorawan-bacnet-server might be problematic. Click here for more details.

Binary file
Binary file
Binary file
Binary file
Binary file
Binary file
@@ -0,0 +1,403 @@
1
+ <script type="text/javascript">
2
+ RED.nodes.registerType('bacnet-point', {
3
+ category: 'BACnet',
4
+ color: '#3FADB5',
5
+ icon: 'font-awesome/fa-dot-circle-o',
6
+ defaults: {
7
+ name: { value: '' },
8
+ server: { value: '', type: 'bacnet-server', required: true },
9
+ objectType: { value: 'analogValue', required: true },
10
+ instanceNumber: { value: 0, required: true, validate: RED.validators.number() },
11
+ objectName: { value: '', required: true },
12
+ initialValue: { value: 0 },
13
+ units: { value: '' },
14
+ description: { value: '' },
15
+ writable: { value: true },
16
+ usePriorityArray: { value: false },
17
+ relinquishDefault: { value: 0 },
18
+ covIncrement: { value: 0 },
19
+ numberOfStates: { value: 3 },
20
+ stateText: { value: '' },
21
+ outputOnRead: { value: false },
22
+ outputOnWrite: { value: true },
23
+ outputOnCov: { value: false }
24
+ },
25
+ inputs: 1,
26
+ outputs: 1,
27
+ label: function () {
28
+ if (this.name) return this.name;
29
+ var typeLabels = {
30
+ 'analogInput': 'AI',
31
+ 'analogOutput': 'AO',
32
+ 'analogValue': 'AV',
33
+ 'binaryInput': 'BI',
34
+ 'binaryOutput': 'BO',
35
+ 'binaryValue': 'BV',
36
+ 'multiStateValue': 'MSV'
37
+ };
38
+ var typeLabel = typeLabels[this.objectType] || this.objectType;
39
+ return typeLabel + ':' + this.instanceNumber;
40
+ },
41
+ paletteLabel: 'bacnet point',
42
+ oneditprepare: function () {
43
+ var node = this;
44
+
45
+ // Toggle visibility based on object type
46
+ function updateVisibility() {
47
+ var objectType = $('#node-input-objectType').val();
48
+ var isAnalog = objectType.startsWith('analog');
49
+ var isBinary = objectType.startsWith('binary');
50
+ var isMsv = objectType === 'multiStateValue';
51
+ var isCommandable = ['analogOutput', 'analogValue', 'binaryOutput', 'binaryValue'].includes(objectType);
52
+
53
+ // Units field - only for analog types
54
+ if (isAnalog) {
55
+ $('#units-row').show();
56
+ } else {
57
+ $('#units-row').hide();
58
+ }
59
+
60
+ // Priority array - only for commandable types
61
+ if (isCommandable) {
62
+ $('#priority-array-row').show();
63
+ updatePriorityArrayOptions();
64
+ } else {
65
+ $('#priority-array-row').hide();
66
+ $('#relinquish-default-row').hide();
67
+ }
68
+
69
+ // MSV-specific options
70
+ if (isMsv) {
71
+ $('#msv-options').show();
72
+ } else {
73
+ $('#msv-options').hide();
74
+ }
75
+
76
+ // Initial value type hint
77
+ if (isBinary) {
78
+ $('#initial-value-hint').text('0 (inactive) or 1 (active)');
79
+ } else if (isMsv) {
80
+ $('#initial-value-hint').text('State number (1 to Number of States)');
81
+ } else {
82
+ $('#initial-value-hint').text('Numeric value');
83
+ }
84
+ }
85
+
86
+ function updatePriorityArrayOptions() {
87
+ var usePriorityArray = $('#node-input-usePriorityArray').prop('checked');
88
+ if (usePriorityArray) {
89
+ $('#relinquish-default-row').show();
90
+ } else {
91
+ $('#relinquish-default-row').hide();
92
+ }
93
+ }
94
+
95
+ // Auto-generate object name if empty
96
+ function generateObjectName() {
97
+ var objectType = $('#node-input-objectType').val();
98
+ var instanceNumber = $('#node-input-instanceNumber').val();
99
+ var currentName = $('#node-input-objectName').val();
100
+
101
+ if (!currentName || currentName === '' || currentName.match(/^[A-Za-z]+_\d+$/)) {
102
+ var typeName = objectType.replace(/([A-Z])/g, '_$1').replace(/^_/, '');
103
+ $('#node-input-objectName').val(typeName + '_' + instanceNumber);
104
+ }
105
+ }
106
+
107
+ // Event handlers
108
+ $('#node-input-objectType').on('change', function () {
109
+ updateVisibility();
110
+ generateObjectName();
111
+ });
112
+
113
+ $('#node-input-instanceNumber').on('change', function () {
114
+ generateObjectName();
115
+ });
116
+
117
+ $('#node-input-usePriorityArray').on('change', function () {
118
+ updatePriorityArrayOptions();
119
+ });
120
+
121
+ // Validate instance number
122
+ $('#node-input-instanceNumber').on('change', function () {
123
+ var val = parseInt($(this).val());
124
+ if (isNaN(val) || val < 0 || val > 4194302) {
125
+ RED.notify('Instance number must be between 0 and 4194302', 'error');
126
+ $(this).val(0);
127
+ }
128
+ });
129
+
130
+ // Initialize visibility
131
+ updateVisibility();
132
+
133
+ // Generate name if empty on load
134
+ if (!node.objectName) {
135
+ generateObjectName();
136
+ }
137
+ },
138
+ oneditsave: function () {
139
+ // Validate configuration
140
+ var objectType = $('#node-input-objectType').val();
141
+ var instanceNumber = parseInt($('#node-input-instanceNumber').val());
142
+
143
+ if (instanceNumber < 0 || instanceNumber > 4194302) {
144
+ RED.notify('Instance number must be between 0 and 4194302', 'error');
145
+ return false;
146
+ }
147
+ }
148
+ });
149
+ </script>
150
+
151
+ <script type="text/html" data-template-name="bacnet-point">
152
+ <div class="form-row">
153
+ <label for="node-input-name"><i class="fa fa-tag"></i> Name</label>
154
+ <input type="text" id="node-input-name" placeholder="Optional display name">
155
+ </div>
156
+
157
+ <div class="form-row">
158
+ <label for="node-input-server"><i class="fa fa-server"></i> Server</label>
159
+ <input type="text" id="node-input-server">
160
+ </div>
161
+
162
+ <div class="form-row">
163
+ <h4><i class="fa fa-cube"></i> Object Configuration</h4>
164
+ </div>
165
+
166
+ <div class="form-row">
167
+ <label for="node-input-objectType"><i class="fa fa-puzzle-piece"></i> Object Type</label>
168
+ <select id="node-input-objectType" style="width: 200px;">
169
+ <option value="analogInput">Analog Input (AI)</option>
170
+ <option value="analogOutput">Analog Output (AO)</option>
171
+ <option value="analogValue">Analog Value (AV)</option>
172
+ <option value="binaryInput">Binary Input (BI)</option>
173
+ <option value="binaryOutput">Binary Output (BO)</option>
174
+ <option value="binaryValue">Binary Value (BV)</option>
175
+ <option value="multiStateValue">Multi-State Value (MSV)</option>
176
+ </select>
177
+ </div>
178
+
179
+ <div class="form-row">
180
+ <label for="node-input-instanceNumber"><i class="fa fa-hashtag"></i> Instance</label>
181
+ <input type="number" id="node-input-instanceNumber" min="0" max="4194302" style="width: 100px;">
182
+ <span class="form-tips" style="margin-left: 10px;">Unique identifier (0-4194302)</span>
183
+ </div>
184
+
185
+ <div class="form-row">
186
+ <label for="node-input-objectName"><i class="fa fa-bookmark"></i> Object Name</label>
187
+ <input type="text" id="node-input-objectName" placeholder="Auto-generated if empty">
188
+ </div>
189
+
190
+ <div class="form-row">
191
+ <label for="node-input-description"><i class="fa fa-align-left"></i> Description</label>
192
+ <input type="text" id="node-input-description" placeholder="Optional description">
193
+ </div>
194
+
195
+ <div class="form-row">
196
+ <label for="node-input-initialValue"><i class="fa fa-pencil"></i> Initial Value</label>
197
+ <input type="number" id="node-input-initialValue" step="any" style="width: 100px;">
198
+ <span id="initial-value-hint" class="form-tips" style="margin-left: 10px;">Numeric value</span>
199
+ </div>
200
+
201
+ <div class="form-row" id="units-row">
202
+ <label for="node-input-units"><i class="fa fa-balance-scale"></i> Units</label>
203
+ <select id="node-input-units" style="width: 200px;">
204
+ <option value="">No units</option>
205
+ <optgroup label="Temperature">
206
+ <option value="62">degrees-Celsius</option>
207
+ <option value="64">degrees-Fahrenheit</option>
208
+ <option value="63">degrees-Kelvin</option>
209
+ </optgroup>
210
+ <optgroup label="Pressure">
211
+ <option value="133">pascals</option>
212
+ <option value="56">bars</option>
213
+ <option value="57">pounds-per-square-inch</option>
214
+ </optgroup>
215
+ <optgroup label="Flow">
216
+ <option value="84">liters-per-second</option>
217
+ <option value="85">liters-per-minute</option>
218
+ <option value="142">cubic-meters-per-second</option>
219
+ <option value="86">cubic-meters-per-hour</option>
220
+ </optgroup>
221
+ <optgroup label="Energy/Power">
222
+ <option value="48">watts</option>
223
+ <option value="49">kilowatts</option>
224
+ <option value="45">kilojoules</option>
225
+ <option value="116">watt-hours</option>
226
+ <option value="53">kilowatt-hours</option>
227
+ </optgroup>
228
+ <optgroup label="Electrical">
229
+ <option value="5">amperes</option>
230
+ <option value="2">milliamperes</option>
231
+ <option value="122">volts</option>
232
+ <option value="123">kilovolts</option>
233
+ <option value="39">ohms</option>
234
+ <option value="121">power-factor</option>
235
+ </optgroup>
236
+ <optgroup label="Time">
237
+ <option value="73">seconds</option>
238
+ <option value="72">minutes</option>
239
+ <option value="71">hours</option>
240
+ <option value="70">days</option>
241
+ </optgroup>
242
+ <optgroup label="Humidity">
243
+ <option value="29">percent-relative-humidity</option>
244
+ <option value="28">percent</option>
245
+ </optgroup>
246
+ <optgroup label="Other">
247
+ <option value="95">no-units</option>
248
+ <option value="28">percent</option>
249
+ <option value="118">parts-per-million</option>
250
+ <option value="143">grams</option>
251
+ <option value="38">kilograms</option>
252
+ <option value="65">meters</option>
253
+ <option value="66">centimeters</option>
254
+ <option value="67">millimeters</option>
255
+ <option value="88">feet</option>
256
+ </optgroup>
257
+ </select>
258
+ </div>
259
+
260
+ <div id="msv-options">
261
+ <div class="form-row">
262
+ <label for="node-input-numberOfStates"><i class="fa fa-list-ol"></i> Number of States</label>
263
+ <input type="number" id="node-input-numberOfStates" min="2" max="255" style="width: 80px;">
264
+ </div>
265
+
266
+ <div class="form-row">
267
+ <label for="node-input-stateText"><i class="fa fa-list"></i> State Text</label>
268
+ <input type="text" id="node-input-stateText" placeholder="State1, State2, State3">
269
+ <span class="form-tips" style="margin-left: 10px;">Comma-separated labels</span>
270
+ </div>
271
+ </div>
272
+
273
+ <div class="form-row">
274
+ <h4><i class="fa fa-cogs"></i> Behavior</h4>
275
+ </div>
276
+
277
+ <div class="form-row">
278
+ <label for="node-input-writable" style="width: auto; margin-right: 10px;">
279
+ <input type="checkbox" id="node-input-writable" style="width: auto; margin: 0;">
280
+ Writable
281
+ </label>
282
+ <span class="form-tips">Allow external BACnet clients to write to this point</span>
283
+ </div>
284
+
285
+ <div class="form-row" id="priority-array-row">
286
+ <label for="node-input-usePriorityArray" style="width: auto; margin-right: 10px;">
287
+ <input type="checkbox" id="node-input-usePriorityArray" style="width: auto; margin: 0;">
288
+ Use Priority Array
289
+ </label>
290
+ <span class="form-tips">Enable 16-level priority array (for commandable objects)</span>
291
+ </div>
292
+
293
+ <div class="form-row" id="relinquish-default-row" style="display: none;">
294
+ <label for="node-input-relinquishDefault"><i class="fa fa-undo"></i> Relinquish Default</label>
295
+ <input type="number" id="node-input-relinquishDefault" step="any" style="width: 100px;">
296
+ <span class="form-tips" style="margin-left: 10px;">Value when all priorities are null</span>
297
+ </div>
298
+
299
+ <div class="form-row">
300
+ <label for="node-input-covIncrement"><i class="fa fa-exchange"></i> COV Increment</label>
301
+ <input type="number" id="node-input-covIncrement" step="any" min="0" style="width: 100px;">
302
+ <span class="form-tips" style="margin-left: 10px;">Threshold for COV notifications (0 = any change)</span>
303
+ </div>
304
+
305
+ <div class="form-row">
306
+ <h4><i class="fa fa-arrow-right"></i> Output Options</h4>
307
+ </div>
308
+
309
+ <div class="form-row">
310
+ <label for="node-input-outputOnRead" style="width: auto; margin-right: 10px;">
311
+ <input type="checkbox" id="node-input-outputOnRead" style="width: auto; margin: 0;">
312
+ Output on Read
313
+ </label>
314
+ <span class="form-tips">Emit message when external client reads this point</span>
315
+ </div>
316
+
317
+ <div class="form-row">
318
+ <label for="node-input-outputOnWrite" style="width: auto; margin-right: 10px;">
319
+ <input type="checkbox" id="node-input-outputOnWrite" style="width: auto; margin: 0;">
320
+ Output on Write
321
+ </label>
322
+ <span class="form-tips">Emit message when external client writes to this point</span>
323
+ </div>
324
+
325
+ <div class="form-row">
326
+ <label for="node-input-outputOnCov" style="width: auto; margin-right: 10px;">
327
+ <input type="checkbox" id="node-input-outputOnCov" style="width: auto; margin: 0;">
328
+ Output on COV
329
+ </label>
330
+ <span class="form-tips">Emit message when value changes (internal use)</span>
331
+ </div>
332
+ </script>
333
+
334
+ <script type="text/html" data-help-name="bacnet-point">
335
+ <p>Represents a BACnet object (point) that is exposed through the BACnet Server.</p>
336
+
337
+ <h3>Inputs</h3>
338
+ <dl class="message-properties">
339
+ <dt>payload <span class="property-type">number | object</span></dt>
340
+ <dd>The new value for the point. Can be:
341
+ <ul>
342
+ <li>A simple number/boolean for direct value update</li>
343
+ <li>An object with <code>value</code> and optional <code>priority</code> (1-16)</li>
344
+ </ul>
345
+ </dd>
346
+ <dt class="optional">bacnet.priority <span class="property-type">number</span></dt>
347
+ <dd>Priority level (1-16) for priority array updates. 1 = highest priority.</dd>
348
+ </dl>
349
+
350
+ <h3>Outputs</h3>
351
+ <dl class="message-properties">
352
+ <dt>payload <span class="property-type">number</span></dt>
353
+ <dd>The current value of the point.</dd>
354
+ <dt>topic <span class="property-type">string</span></dt>
355
+ <dd>The object name.</dd>
356
+ <dt>bacnet <span class="property-type">object</span></dt>
357
+ <dd>BACnet metadata:
358
+ <ul>
359
+ <li><code>event</code>: 'read', 'write', or 'cov'</li>
360
+ <li><code>objectType</code>: Object type name</li>
361
+ <li><code>objectTypeId</code>: BACnet object type number</li>
362
+ <li><code>instanceNumber</code>: Object instance</li>
363
+ <li><code>priority</code>: Write priority (for write events)</li>
364
+ <li><code>source</code>: Source address (for write events)</li>
365
+ </ul>
366
+ </dd>
367
+ </dl>
368
+
369
+ <h3>Object Types</h3>
370
+ <table>
371
+ <tr><th>Type</th><th>Code</th><th>Use Case</th></tr>
372
+ <tr><td>Analog Input (AI)</td><td>0</td><td>Sensor readings (read-only)</td></tr>
373
+ <tr><td>Analog Output (AO)</td><td>1</td><td>Setpoints (commandable)</td></tr>
374
+ <tr><td>Analog Value (AV)</td><td>2</td><td>Calculated/stored values</td></tr>
375
+ <tr><td>Binary Input (BI)</td><td>3</td><td>Status/alarms (read-only)</td></tr>
376
+ <tr><td>Binary Output (BO)</td><td>4</td><td>On/Off control (commandable)</td></tr>
377
+ <tr><td>Binary Value (BV)</td><td>5</td><td>Boolean states</td></tr>
378
+ <tr><td>Multi-State Value (MSV)</td><td>19</td><td>Enumerated states</td></tr>
379
+ </table>
380
+
381
+ <h3>Priority Array</h3>
382
+ <p>Commandable objects (AO, AV, BO, BV) can use a 16-level priority array.
383
+ Higher priority commands (lower numbers) override lower priority commands.
384
+ When all slots are null, the <b>Relinquish Default</b> value is used.</p>
385
+
386
+ <p>To relinquish a priority, send a message with <code>payload: null</code>
387
+ and the desired <code>priority</code>.</p>
388
+
389
+ <h3>COV (Change of Value)</h3>
390
+ <p>External BACnet clients can subscribe to value changes. The <b>COV Increment</b>
391
+ defines the minimum change required to trigger a notification. For binary objects,
392
+ any change triggers a notification.</p>
393
+
394
+ <h3>Example - Update Value</h3>
395
+ <pre>msg.payload = 23.5;</pre>
396
+
397
+ <h3>Example - Update with Priority</h3>
398
+ <pre>msg.payload = { value: 23.5, priority: 8 };</pre>
399
+
400
+ <h3>Example - Relinquish Priority</h3>
401
+ <pre>msg.payload = null;
402
+ msg.bacnet = { priority: 8 };</pre>
403
+ </script>