node-red-contrib-uos-nats 0.1.52 → 0.1.53
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/nodes/datahub-input.html +389 -67
- package/nodes/datahub-input.js +42 -19
- package/package.json +1 -1
package/nodes/datahub-input.html
CHANGED
|
@@ -357,77 +357,399 @@
|
|
|
357
357
|
<div class="form-row">
|
|
358
358
|
<label for="node-input-connection"><i class="fa fa-cube"></i> u-OS Config</label>
|
|
359
359
|
<input type="text" id="node-input-connection">
|
|
360
|
-
|
|
361
|
-
|
|
362
|
-
<label><i class="fa fa-id-badge"></i> Provider</label>
|
|
363
|
-
<div style="display:flex; align-items:center; flex:1; gap:5px;">
|
|
364
|
-
<select id="datahub-provider-select" style="flex:1;"></select>
|
|
365
|
-
<button type="button" id="datahub-provider-refresh" class="red-ui-button" title="Refresh Providers"><i class="fa fa-refresh"></i></button>
|
|
366
|
-
<input type="hidden" id="node-input-providerId">
|
|
367
|
-
<!-- Manual input fallback kept hidden/removed from flow per user request -->
|
|
368
|
-
<input type="text" id="datahub-provider-manual" style="display:none;" disabled>
|
|
360
|
+
<label for="node-input-name"><i class="fa fa-tag"></i> Name</label>
|
|
361
|
+
<input type="text" id="node-input-name" placeholder="Name">
|
|
369
362
|
</div>
|
|
370
|
-
|
|
371
|
-
|
|
372
|
-
|
|
373
|
-
|
|
374
|
-
|
|
375
|
-
|
|
376
|
-
|
|
377
|
-
|
|
378
|
-
|
|
379
|
-
<button type="button" id="datahub-vars-all" class="red-ui-button red-ui-button-small" title="Select All"><i class="fa fa-check-square-o"></i></button>
|
|
380
|
-
<button type="button" id="datahub-vars-none" class="red-ui-button red-ui-button-small" title="Deselect All"><i class="fa fa-square-o"></i></button>
|
|
363
|
+
<div class="form-row">
|
|
364
|
+
<label for="node-input-connection"><i class="fa fa-globe"></i> Config</label>
|
|
365
|
+
<input type="text" id="node-input-connection">
|
|
366
|
+
</div>
|
|
367
|
+
<div class="form-row">
|
|
368
|
+
<label for="node-input-providerId"><i class="fa fa-server"></i> Provider</label>
|
|
369
|
+
<div style="display: inline-flex; width: 70%;">
|
|
370
|
+
<select id="node-input-providerId" style="flex-grow: 1;"></select>
|
|
371
|
+
<button id="node-input-check-custom-provider" class="red-ui-button" style="margin-left: 5px;">Refresh</button>
|
|
381
372
|
</div>
|
|
382
|
-
<button type="button" id="datahub-vars-refresh" class="red-ui-button red-ui-button-small" title="Refresh Variables"><i class="fa fa-refresh"></i></button>
|
|
383
373
|
</div>
|
|
384
|
-
|
|
385
|
-
|
|
386
|
-
|
|
387
|
-
|
|
388
|
-
|
|
374
|
+
<div class="form-row">
|
|
375
|
+
<label for="node-input-mode"><i class="fa fa-sliders"></i> Mode</label>
|
|
376
|
+
<select id="node-input-mode">
|
|
377
|
+
<option value="auto">Auto-Discovery (Select from List)</option>
|
|
378
|
+
<option value="manual_single">Manual: Single Variable</option>
|
|
379
|
+
<option value="manual_multi">Manual: Multi Variable (Table)</option>
|
|
380
|
+
</select>
|
|
381
|
+
</div>
|
|
382
|
+
|
|
383
|
+
<!-- Mode: Auto-Discovery -->
|
|
384
|
+
<div id="mode-auto-row" class="mode-row">
|
|
385
|
+
<div class="form-row" style="margin-bottom: 0px;">
|
|
386
|
+
<label><i class="fa fa-list"></i> Variables</label>
|
|
387
|
+
<div style="display: inline-block; width: 70%;">
|
|
388
|
+
<div style="margin-bottom: 5px;">
|
|
389
|
+
<button id="node-input-refresh-vars" class="red-ui-button red-ui-button-small"><i class="fa fa-refresh"></i> Refresh</button>
|
|
390
|
+
<button id="node-input-select-all" class="red-ui-button red-ui-button-small" style="margin-left: 5px;">Select All</button>
|
|
391
|
+
<button id="node-input-select-none" class="red-ui-button red-ui-button-small" style="margin-left: 5px;">None</button>
|
|
392
|
+
<span id="vars-status" style="margin-left: 10px; font-style: italic; color: #888;"></span>
|
|
393
|
+
</div>
|
|
394
|
+
</div>
|
|
395
|
+
</div>
|
|
396
|
+
<div class="form-row">
|
|
397
|
+
<input type="hidden" id="node-input-variablesText">
|
|
398
|
+
<div id="node-input-variables-container" style="border: 1px solid #ccc; border-radius: 4px; height: 250px; overflow-y: scroll; padding: 5px; background: #fff; width: 100%; box-sizing: border-box;">
|
|
399
|
+
<!-- Checkboxes will be injected here -->
|
|
400
|
+
<div style="padding: 10px; text-align: center; color: #888;">
|
|
401
|
+
Select a Provider to load variables...
|
|
402
|
+
</div>
|
|
403
|
+
</div>
|
|
404
|
+
<div class="form-tips" style="margin-top: 5px;">
|
|
405
|
+
<i class="fa fa-info-circle"></i> Requires <b>Deploy</b> or active connection.
|
|
406
|
+
</div>
|
|
407
|
+
</div>
|
|
408
|
+
</div>
|
|
409
|
+
|
|
410
|
+
<!-- Mode: Manual Single -->
|
|
411
|
+
<div id="mode-manual-single-row" class="mode-row">
|
|
412
|
+
<div class="form-row">
|
|
413
|
+
<label for="node-input-singleName"><i class="fa fa-font"></i> Var Name</label>
|
|
414
|
+
<input type="text" id="node-input-singleName" placeholder="e.g. digital_nameplate.manufacturer_name">
|
|
415
|
+
</div>
|
|
416
|
+
<div class="form-row">
|
|
417
|
+
<label for="node-input-singleId"><i class="fa fa-hashtag"></i> Var ID</label>
|
|
418
|
+
<input type="number" id="node-input-singleId" placeholder="e.g. 0">
|
|
419
|
+
</div>
|
|
420
|
+
<div class="form-tips">
|
|
421
|
+
<i class="fa fa-info-circle"></i> <b>Manual Mode:</b> Use this if Auto-Discovery fails (Permissions/403). Enter the internal ID from your system.
|
|
422
|
+
</div>
|
|
423
|
+
</div>
|
|
424
|
+
|
|
425
|
+
<!-- Mode: Manual Multi -->
|
|
426
|
+
<div id="mode-manual-multi-row" class="mode-row">
|
|
427
|
+
<div class="form-row">
|
|
428
|
+
<label for="node-input-manualVariables-container"><i class="fa fa-table"></i> Mapping</label>
|
|
429
|
+
<div class="form-row node-input-manualVariables-container-row" style="width: 100%; margin-top: 8px;">
|
|
430
|
+
<ol id="node-input-manual-def-list"></ol>
|
|
431
|
+
</div>
|
|
432
|
+
<!-- Hidden field to store JSON string of definitions -->
|
|
433
|
+
<input type="hidden" id="node-input-manualVariables" />
|
|
434
|
+
</div>
|
|
435
|
+
<div class="form-tips">
|
|
436
|
+
<i class="fa fa-info-circle"></i> <b>Manual Table:</b> Map strictly "Name -> ID". Only variables listed here will be output.
|
|
437
|
+
</div>
|
|
438
|
+
</div>
|
|
439
|
+
|
|
440
|
+
<hr>
|
|
441
|
+
|
|
442
|
+
<div class="form-row">
|
|
443
|
+
<label for="node-input-pollingInterval"><i class="fa fa-clock-o"></i> Poll (ms)</label>
|
|
444
|
+
<input type="number" id="node-input-pollingInterval" placeholder="0 (Disabled)">
|
|
389
445
|
</div>
|
|
390
|
-
<input type="hidden" id="node-input-variablesText">
|
|
391
|
-
</div>
|
|
392
|
-
<div class="form-row">
|
|
393
|
-
<label></label>
|
|
394
|
-
<div id="datahub-variables-status" class="form-tips">Leave empty to listen to every variable.</div>
|
|
395
|
-
</div>
|
|
396
|
-
<div class="form-row" style="margin-bottom:0;">
|
|
397
|
-
<label style="width:auto;"><i class="fa fa-wrench"></i> Manual Definition Map</label>
|
|
398
|
-
<div style="float:right; font-size:12px; color:#888;">Map Variable Names to IDs manually (if Discovery fails)</div>
|
|
399
|
-
</div>
|
|
400
|
-
<div class="form-row node-input-manual-container-row">
|
|
401
|
-
<ol id="node-input-manual-defs-container"></ol>
|
|
402
|
-
</div>
|
|
403
|
-
<input type="hidden" id="node-input-manualVariables">
|
|
404
|
-
<div class="form-tips"><strong>Tip:</strong> If auto-discovery fails (permissions), enter the Name and ID exactly as configured in the server. See your Python config for reference.</div>
|
|
405
446
|
</script>
|
|
406
447
|
|
|
407
448
|
<script type="text/html" data-help-name="datahub-input">
|
|
408
|
-
|
|
409
|
-
|
|
410
|
-
|
|
411
|
-
<
|
|
412
|
-
|
|
413
|
-
|
|
414
|
-
|
|
415
|
-
|
|
416
|
-
|
|
417
|
-
|
|
418
|
-
|
|
419
|
-
|
|
420
|
-
|
|
421
|
-
|
|
422
|
-
|
|
423
|
-
|
|
424
|
-
|
|
425
|
-
|
|
426
|
-
|
|
427
|
-
|
|
428
|
-
|
|
429
|
-
|
|
430
|
-
|
|
431
|
-
|
|
432
|
-
|
|
433
|
-
</
|
|
449
|
+
<p>Reads variables from the u-OS Data Hub (Snapshot or Subscription).</p>
|
|
450
|
+
|
|
451
|
+
<h3>Configuration</h3>
|
|
452
|
+
<dl class="message-properties">
|
|
453
|
+
<dt>Mode</dt>
|
|
454
|
+
<dd>
|
|
455
|
+
<ul>
|
|
456
|
+
<li><b>Auto-Discovery:</b> Connects to the hub, downloads the variable list, and lets you select variables via checkboxes. Requires correct permissions (`hub.variables.readonly`).</li>
|
|
457
|
+
<li><b>Manual (Single):</b> For reading a single variable when Discovery is blocked. Enter Name and ID manually.</li>
|
|
458
|
+
<li><b>Manual (Multi):</b> For reading multiple variables when Discovery is blocked. Use the table to map Names to IDs.</li>
|
|
459
|
+
</ul>
|
|
460
|
+
</dd>
|
|
461
|
+
|
|
462
|
+
<dt>Provider</dt>
|
|
463
|
+
<dd>The Data Hub Provider ID (e.g. `u_os_sbm`).</dd>
|
|
464
|
+
|
|
465
|
+
<dt>Polling</dt>
|
|
466
|
+
<dd>Interval in ms to force-read values. 0 = Disabled (Wait for Change Events).</dd>
|
|
467
|
+
</dl>
|
|
468
|
+
|
|
469
|
+
<h3>Important: Permission Issues / 403</h3>
|
|
470
|
+
<p>
|
|
471
|
+
If you see "Empty List" or "Permission Violation", you must use one of the <b>Manual Modes</b>.
|
|
472
|
+
Auto-Discovery relies on endpoints that are often restricted.
|
|
473
|
+
In Manual Mode, you provide the `ID` (Number) which works even without Discovery permissions.
|
|
474
|
+
</p>
|
|
475
|
+
|
|
476
|
+
<div style="margin-top: 10px; font-size: 0.8em; color: #666; border-top: 1px solid #eee; padding-top: 5px;">
|
|
477
|
+
Developed by <a href="https://github.com/iotueli" target="_blank">iotueli</a>. Not an official Weidmüller product.
|
|
478
|
+
</div>
|
|
479
|
+
</script>
|
|
480
|
+
|
|
481
|
+
<script type="text/javascript">
|
|
482
|
+
RED.nodes.registerType('datahub-input', {
|
|
483
|
+
category: 'u-OS',
|
|
484
|
+
color: '#ff9900',
|
|
485
|
+
defaults: {
|
|
486
|
+
name: { value: "" },
|
|
487
|
+
connection: { type: "uos-config", required: true },
|
|
488
|
+
providerId: { value: "", required: true },
|
|
489
|
+
variablesText: { value: "" }, // Legacy / Auto-Mode list
|
|
490
|
+
manualVariables: { value: "" }, // Manual Table JSON/CSV
|
|
491
|
+
singleName: { value: "" }, // Manual Single Name
|
|
492
|
+
singleId: { value: "" }, // Manual Single ID
|
|
493
|
+
mode: { value: "auto" }, // auto | manual_single | manual_multi
|
|
494
|
+
pollingInterval: { value: 0, validate: RED.validators.number() }
|
|
495
|
+
},
|
|
496
|
+
inputs: 1,
|
|
497
|
+
outputs: 1,
|
|
498
|
+
icon: "datahub-input.svg",
|
|
499
|
+
label: function () {
|
|
500
|
+
if (this.name) return this.name;
|
|
501
|
+
if (this.mode === 'manual_single' && this.singleName) return this.singleName;
|
|
502
|
+
return "u-OS " + (this.providerId || "Data Hub");
|
|
503
|
+
},
|
|
504
|
+
paletteLabel: "u-OS Data Hub",
|
|
505
|
+
oneditprepare: function () {
|
|
506
|
+
const node = this;
|
|
507
|
+
|
|
508
|
+
// --- 1. Mode Visibility Logic ---
|
|
509
|
+
const modeSelect = $('#node-input-mode');
|
|
510
|
+
const autoRow = $('#mode-auto-row');
|
|
511
|
+
const manualSingleRow = $('#mode-manual-single-row');
|
|
512
|
+
const manualMultiRow = $('#mode-manual-multi-row');
|
|
513
|
+
|
|
514
|
+
// Set default if missing (backward compat)
|
|
515
|
+
if (!$('#node-input-mode').val()) {
|
|
516
|
+
$('#node-input-mode').val('auto');
|
|
517
|
+
}
|
|
518
|
+
|
|
519
|
+
const updateVisibility = () => {
|
|
520
|
+
const mode = modeSelect.val();
|
|
521
|
+
autoRow.hide();
|
|
522
|
+
manualSingleRow.hide();
|
|
523
|
+
manualMultiRow.hide();
|
|
524
|
+
|
|
525
|
+
if (mode === 'auto') autoRow.show();
|
|
526
|
+
else if (mode === 'manual_single') manualSingleRow.show();
|
|
527
|
+
else if (mode === 'manual_multi') manualMultiRow.show();
|
|
528
|
+
};
|
|
529
|
+
|
|
530
|
+
modeSelect.on('change', updateVisibility);
|
|
531
|
+
updateVisibility(); // Initial trigger
|
|
532
|
+
|
|
533
|
+
// --- 2. Existing "Auto" Logic (Providers & Checkboxes) ---
|
|
534
|
+
const $config = $('#node-input-connection');
|
|
535
|
+
const $providerRefresh = $('#node-input-check-custom-provider');
|
|
536
|
+
const $variables = $('input[type="checkbox"]');
|
|
537
|
+
const $variablesHidden = $('#node-input-variablesText');
|
|
538
|
+
const $varsRefresh = $('#node-input-refresh-vars');
|
|
539
|
+
|
|
540
|
+
let initialProvider = node.providerId;
|
|
541
|
+
let providerRequest = 0;
|
|
542
|
+
|
|
543
|
+
// Define Helpers (providerValue, providerLabel etc) from original code
|
|
544
|
+
const providerValue = (p) => p.id || p.providerId || p.name;
|
|
545
|
+
const providerLabel = (p) => p.name || p.displayName || p.providerId || p.id;
|
|
546
|
+
|
|
547
|
+
// --- 3. Existing Loading Logic ---
|
|
548
|
+
const setProviderStatus = (msg) => { /* No-op or custom div if needed */ };
|
|
549
|
+
const setProviderValue = (val) => { $('#node-input-providerId').val(val); };
|
|
550
|
+
|
|
551
|
+
const setVariableStatus = (msg) => { $('#vars-status').text(msg); };
|
|
552
|
+
|
|
553
|
+
const normalizeVariables = (payload) => {
|
|
554
|
+
if (Array.isArray(payload)) return payload;
|
|
555
|
+
if (payload && Array.isArray(payload.variables)) return payload.variables;
|
|
556
|
+
return [];
|
|
557
|
+
};
|
|
558
|
+
|
|
559
|
+
const normalizeProviders = (payload) => {
|
|
560
|
+
if (Array.isArray(payload)) return payload;
|
|
561
|
+
if (payload && Array.isArray(payload.providers)) return payload.providers;
|
|
562
|
+
return [];
|
|
563
|
+
};
|
|
564
|
+
|
|
565
|
+
const variableValue = (v) => v.key || v.name || v.path || v.id;
|
|
566
|
+
const variableLabel = (v) => v.tagName || v.key || v.name || v.path || v.id;
|
|
567
|
+
|
|
568
|
+
const syncVariablesField = () => {
|
|
569
|
+
const selected = [];
|
|
570
|
+
varsContainer.find('input[type="checkbox"]:checked').each(function () {
|
|
571
|
+
selected.push($(this).val());
|
|
572
|
+
});
|
|
573
|
+
varsHiddenInput.val(selected.join(','));
|
|
574
|
+
setVariableStatus(`${selected.length} selected`);
|
|
575
|
+
};
|
|
576
|
+
|
|
577
|
+
const loadVariables = (providerId, force = false) => {
|
|
578
|
+
const configId = $config.val();
|
|
579
|
+
if (!configId || !providerId) return;
|
|
580
|
+
|
|
581
|
+
setVariableStatus('Loading...');
|
|
582
|
+
varsContainer.empty().append('<div style="padding:10px; text-align:center; color:#888;"><i class="fa fa-spinner fa-spin"></i> Loading variables...</div>');
|
|
583
|
+
|
|
584
|
+
$.getJSON(`uos/providers/${configId}/${providerId}/variables`)
|
|
585
|
+
.done((payload) => {
|
|
586
|
+
const list = normalizeVariables(payload);
|
|
587
|
+
varsContainer.empty();
|
|
588
|
+
if (!list.length) {
|
|
589
|
+
varsContainer.append('<div style="padding:10px; text-align:center; color:#888;">No variables found for this provider.</div>');
|
|
590
|
+
setVariableStatus('No variables found');
|
|
591
|
+
return;
|
|
592
|
+
}
|
|
593
|
+
|
|
594
|
+
const currentVal = varsHiddenInput.val().split(',');
|
|
595
|
+
// Sort by name/key
|
|
596
|
+
list.sort((a, b) => (variableLabel(a) || '').localeCompare(variableLabel(b) || ''));
|
|
597
|
+
|
|
598
|
+
list.forEach((v) => {
|
|
599
|
+
const val = variableValue(v);
|
|
600
|
+
const lbl = variableLabel(v);
|
|
601
|
+
const checked = currentVal.includes(val) ? 'checked' : '';
|
|
602
|
+
const row = $('<div/>').appendTo(varsContainer);
|
|
603
|
+
$('<label/>').css({ display: 'block', cursor: 'pointer' })
|
|
604
|
+
.append($('<input type="checkbox"/>').val(val).prop('checked', !!checked).on('change', syncVariablesField))
|
|
605
|
+
.append($('<span/>').text(' ' + lbl))
|
|
606
|
+
.appendTo(row);
|
|
607
|
+
});
|
|
608
|
+
syncVariablesField();
|
|
609
|
+
})
|
|
610
|
+
.fail((xhr) => {
|
|
611
|
+
const error = xhr?.responseJSON?.error;
|
|
612
|
+
let msg = error || xhr.statusText || 'Failed to load variables.';
|
|
613
|
+
if (xhr.status === 404 && error === 'config not found') {
|
|
614
|
+
msg = 'Config not deployed yet. Please Deploy first.';
|
|
615
|
+
}
|
|
616
|
+
varsContainer.empty().append(`<div style="padding:10px; color:#d66;">${msg}</div>`);
|
|
617
|
+
setVariableStatus('Error loading variables');
|
|
618
|
+
});
|
|
619
|
+
};
|
|
620
|
+
|
|
621
|
+
const populateProviders = (payload) => {
|
|
622
|
+
const list = normalizeProviders(payload);
|
|
623
|
+
providerSelect.empty();
|
|
624
|
+
if (!list.length) {
|
|
625
|
+
providerSelect.append('<option value="">No providers found</option>').prop('disabled', true);
|
|
626
|
+
return;
|
|
627
|
+
}
|
|
628
|
+
let foundInitial = false;
|
|
629
|
+
list.forEach((prov) => {
|
|
630
|
+
const val = providerValue(prov);
|
|
631
|
+
if (!val) return;
|
|
632
|
+
const opt = $('<option/>').val(val).text(providerLabel(prov));
|
|
633
|
+
providerSelect.append(opt);
|
|
634
|
+
if (val === initialProvider) foundInitial = true;
|
|
635
|
+
});
|
|
636
|
+
providerSelect.prop('disabled', false);
|
|
637
|
+
|
|
638
|
+
if (initialProvider && foundInitial) {
|
|
639
|
+
providerSelect.val(initialProvider);
|
|
640
|
+
} else if (initialProvider) {
|
|
641
|
+
// Preserve unknown provider (e.g. from manual entry or offline)
|
|
642
|
+
providerSelect.append($('<option/>').val(initialProvider).text(initialProvider + ' (unknown)'));
|
|
643
|
+
providerSelect.val(initialProvider);
|
|
644
|
+
}
|
|
645
|
+
|
|
646
|
+
if (!providerSelect.val()) {
|
|
647
|
+
providerSelect.val(providerSelect.find('option:first').val());
|
|
648
|
+
}
|
|
649
|
+
|
|
650
|
+
// Trigger variable load for selected
|
|
651
|
+
const selected = providerSelect.val();
|
|
652
|
+
if (selected) loadVariables(selected);
|
|
653
|
+
};
|
|
654
|
+
|
|
655
|
+
const loadProviders = (force = false) => {
|
|
656
|
+
const configId = $config.val();
|
|
657
|
+
if (!configId) {
|
|
658
|
+
providerSelect.empty().append('<option value="">Select Config Node first</option>').prop('disabled', true);
|
|
659
|
+
return;
|
|
660
|
+
}
|
|
661
|
+
|
|
662
|
+
const seq = ++providerRequest;
|
|
663
|
+
// Cache logic omitted for brevity/safety - simplified direct load
|
|
664
|
+
providerSelect.prop('disabled', true);
|
|
665
|
+
|
|
666
|
+
$.getJSON(`uos/providers/${configId}`)
|
|
667
|
+
.done((payload) => {
|
|
668
|
+
if (seq !== providerRequest) return;
|
|
669
|
+
populateProviders(payload);
|
|
670
|
+
})
|
|
671
|
+
.fail((xhr) => {
|
|
672
|
+
const error = xhr?.responseJSON?.error;
|
|
673
|
+
const msg = error || 'Failed to load providers';
|
|
674
|
+
providerSelect.empty().append('<option value="">Error loading providers</option>').prop('disabled', true);
|
|
675
|
+
});
|
|
676
|
+
};
|
|
677
|
+
|
|
678
|
+
// Event Listeners
|
|
679
|
+
providerSelect.on('change', () => {
|
|
680
|
+
const val = providerSelect.val();
|
|
681
|
+
varsHiddenInput.val('');
|
|
682
|
+
syncVariablesField();
|
|
683
|
+
loadVariables(val);
|
|
684
|
+
});
|
|
685
|
+
|
|
686
|
+
$providerRefresh.on('click', () => loadProviders(true));
|
|
687
|
+
$varsRefresh.on('click', () => {
|
|
688
|
+
const v = providerSelect.val();
|
|
689
|
+
if (v) loadVariables(v, true);
|
|
690
|
+
});
|
|
691
|
+
|
|
692
|
+
$('#node-input-select-all').on('click', () => {
|
|
693
|
+
varsContainer.find('input[type="checkbox"]').prop('checked', true).trigger('change');
|
|
694
|
+
});
|
|
695
|
+
$('#node-input-select-none').on('click', () => {
|
|
696
|
+
varsContainer.find('input[type="checkbox"]').prop('checked', false).trigger('change');
|
|
697
|
+
});
|
|
698
|
+
|
|
699
|
+
$config.on('change', () => {
|
|
700
|
+
setTimeout(() => loadProviders(false), 50);
|
|
701
|
+
});
|
|
702
|
+
|
|
703
|
+
// Initial Load
|
|
704
|
+
setTimeout(() => loadProviders(false), 100);
|
|
705
|
+
|
|
706
|
+
|
|
707
|
+
// --- 4. Manual Table Logic ---
|
|
708
|
+
const $manualList = $('#node-input-manual-def-list');
|
|
709
|
+
const $manualHidden = $('#node-input-manualVariables');
|
|
710
|
+
|
|
711
|
+
$manualList.editableList({
|
|
712
|
+
addItem: function (row, index, data) {
|
|
713
|
+
row.css({ display: 'flex', alignItems: 'center', gap: '5px' });
|
|
714
|
+
|
|
715
|
+
$('<input/>', { class: 'node-input-manual-name', type: 'text', placeholder: 'Name', style: 'flex:1;' })
|
|
716
|
+
.val(data.name || '')
|
|
717
|
+
.appendTo(row);
|
|
718
|
+
|
|
719
|
+
$('<input/>', { class: 'node-input-manual-id', type: 'number', placeholder: 'ID', style: 'width:80px;' })
|
|
720
|
+
.val(data.id || '')
|
|
721
|
+
.appendTo(row);
|
|
722
|
+
},
|
|
723
|
+
removable: true,
|
|
724
|
+
header: $('<div></div>').append(
|
|
725
|
+
$.parseHTML('<div style="display:flex; gap:5px; padding-left:5px;"><div style="flex:1;">Name (String)</div><div style="width:80px;">ID (Int)</div></div>')
|
|
726
|
+
),
|
|
727
|
+
addButton: 'Add Mapping'
|
|
728
|
+
});
|
|
729
|
+
|
|
730
|
+
// Populate Table
|
|
731
|
+
const currentManual = $manualHidden.val() || '';
|
|
732
|
+
if (currentManual) {
|
|
733
|
+
currentManual.split(',').forEach(entry => {
|
|
734
|
+
const parts = entry.split(':');
|
|
735
|
+
if (parts.length === 2) {
|
|
736
|
+
const n = parts[0].trim();
|
|
737
|
+
const i = parts[1].trim();
|
|
738
|
+
if (n && i) $manualList.editableList('addItem', { name: n, id: i });
|
|
739
|
+
}
|
|
740
|
+
});
|
|
741
|
+
}
|
|
742
|
+
|
|
743
|
+
},
|
|
744
|
+
oneditsave: function () {
|
|
745
|
+
const $manualList = $('#node-input-manual-def-list');
|
|
746
|
+
const items = [];
|
|
747
|
+
$manualList.editableList('items').each(function () {
|
|
748
|
+
const name = $(this).find('.node-input-manual-name').val().trim();
|
|
749
|
+
const id = $(this).find('.node-input-manual-id').val().trim();
|
|
750
|
+
if (name && id) {
|
|
751
|
+
items.push(`${name}:${id}`);
|
|
752
|
+
}
|
|
753
|
+
});
|
|
754
|
+
$('#node-input-manualVariables').val(items.join(','));
|
|
755
|
+
}
|
package/nodes/datahub-input.js
CHANGED
|
@@ -27,30 +27,53 @@ module.exports = function (RED) {
|
|
|
27
27
|
}
|
|
28
28
|
|
|
29
29
|
this.providerId = config.providerId || 'sampleprovider';
|
|
30
|
-
this.
|
|
30
|
+
this.providerId = config.providerId || 'sampleprovider';
|
|
31
|
+
this.pollingInterval = parseInt(config.pollingInterval, 10) || 0; // ms
|
|
32
|
+
this.mode = config.mode || 'auto';
|
|
33
|
+
|
|
31
34
|
const text = config.variablesText || '';
|
|
32
35
|
const manualText = config.manualVariables || '';
|
|
36
|
+
const singleName = config.singleName || '';
|
|
37
|
+
const singleId = config.singleId || '';
|
|
33
38
|
|
|
34
|
-
//
|
|
35
|
-
this.variables =
|
|
36
|
-
.split(',')
|
|
37
|
-
.map((entry) => (entry ? String(entry).trim() : ''))
|
|
38
|
-
.filter((entry) => entry.length > 0);
|
|
39
|
-
|
|
40
|
-
// Parse Manual Definitions "Name:ID"
|
|
39
|
+
// Initialize containers
|
|
40
|
+
this.variables = [];
|
|
41
41
|
this.manualDefs = [];
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
}
|
|
42
|
+
|
|
43
|
+
// --- Mode-Based Initialization ---
|
|
44
|
+
if (this.mode === 'manual_single') {
|
|
45
|
+
// Mode: Manual Single
|
|
46
|
+
// Strictly use Single Name/ID. Ignore others.
|
|
47
|
+
if (singleName && singleId !== '') {
|
|
48
|
+
const id = parseInt(singleId, 10);
|
|
49
|
+
if (!isNaN(id)) {
|
|
50
|
+
this.manualDefs.push({ id, key: String(singleName).trim() });
|
|
52
51
|
}
|
|
53
|
-
}
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
else if (this.mode === 'manual_multi') {
|
|
55
|
+
// Mode: Manual Multi (Table)
|
|
56
|
+
if (manualText) {
|
|
57
|
+
manualText.split(',').forEach(entry => {
|
|
58
|
+
let trimmed = entry ? String(entry).trim() : '';
|
|
59
|
+
if (trimmed.includes(':')) {
|
|
60
|
+
const parts = trimmed.split(':');
|
|
61
|
+
const name = parts[0].trim();
|
|
62
|
+
const id = parseInt(parts[1].trim(), 10);
|
|
63
|
+
if (name && !isNaN(id)) {
|
|
64
|
+
this.manualDefs.push({ id, key: name });
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
});
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
else {
|
|
71
|
+
// Mode: Auto (Default)
|
|
72
|
+
// Use the standard checkbox list
|
|
73
|
+
this.variables = text
|
|
74
|
+
.split(',')
|
|
75
|
+
.map((entry) => (entry ? String(entry).trim() : ''))
|
|
76
|
+
.filter((entry) => entry.length > 0);
|
|
54
77
|
}
|
|
55
78
|
|
|
56
79
|
let nc;
|