node-red-contrib-uos-nats 1.3.3 → 1.3.39
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/README.md +10 -4
- package/lib/subjects.js +3 -0
- package/nodes/datahub-input.html +127 -179
- package/nodes/datahub-input.js +141 -201
- package/nodes/datahub-output.html +1 -1
- package/nodes/datahub-output.js +70 -29
- package/nodes/datahub-write.html +102 -191
- package/nodes/datahub-write.js +86 -45
- package/nodes/uos-config.html +7 -1
- package/nodes/uos-config.js +455 -148
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -56,7 +56,7 @@ Restart Node-RED. The nodes will appear in the **"Weidmüller DataHub"** categor
|
|
|
56
56
|
Import this flow to test reading and writing immediately:
|
|
57
57
|
|
|
58
58
|
```json
|
|
59
|
-
[{"id":"cdad2fa96dc6eeec","type":"datahub-input","z":"c221537c994b056a","name":"Read Zipcode","connection":"a0ba0e15c8dad779","providerId":"u_os_adm","manualVariables":"digital_nameplate.address_information.zipcode:2","triggerMode":"
|
|
59
|
+
[{"id":"cdad2fa96dc6eeec","type":"datahub-input","z":"c221537c994b056a","name":"Read Zipcode","connection":"a0ba0e15c8dad779","providerId":"u_os_adm","manualVariables":"digital_nameplate.address_information.zipcode:2","triggerMode":"event","pollingInterval":"1000","x":190,"y":100,"wires":[["315d179d66bf9b93"]]},{"id":"315d179d66bf9b93","type":"debug","z":"c221537c994b056a","name":"Debug Output","active":true,"tosidebar":true,"console":false,"tostatus":false,"complete":"payload","targetType":"msg","statusVal":"","statusType":"auto","x":440,"y":100,"wires":[]},{"id":"a0ba0e15c8dad779","type":"uos-config","clientName":"nodered-client"}]
|
|
60
60
|
```
|
|
61
61
|
|
|
62
62
|
---
|
|
@@ -92,10 +92,16 @@ Publishes your own data to the Data Hub.
|
|
|
92
92
|
## Troubleshooting
|
|
93
93
|
|
|
94
94
|
- **Provider not visible?** Ensure **Provider ID** matches your **Client ID**. Easiest way: Leave Provider ID empty in the node.
|
|
95
|
-
- **
|
|
95
|
+
- **Node Status is Yellow?**
|
|
96
|
+
- `cooldown (10s)`: The node is waiting to protect the device. This is normal after an error.
|
|
97
|
+
- `provider offline`: The connection to NATS is OK, but the target (e.g. `u_os_sbm`) is not responding (503).
|
|
98
|
+
- `auth failed`: Check your OAuth Client Secret and Scopes.
|
|
99
|
+
- **Node Status is Red?**
|
|
100
|
+
- `illegal ID`: You used a reserved name like `u_os_sbm`. Rename your Client/Provider.
|
|
101
|
+
- `write error`: A command failed. Check Scopes (`hub.variables.readwrite`) or Fingerprint.
|
|
96
102
|
- **Variable ID "undefined" or "ERR"?**
|
|
97
|
-
-
|
|
98
|
-
-
|
|
103
|
+
- The ID column is hidden by default to avoid confusion. The node handles ID resolution automatically.
|
|
104
|
+
- If a variable fails, check if the Key (name) is correct on the Data Hub.
|
|
99
105
|
- **Write not working?** Ensure your OAuth client has `hub.variables.readwrite` scope.
|
|
100
106
|
- **Debug:** Check the Node-RED "Debug" sidebar for error messages.
|
|
101
107
|
|
package/lib/subjects.js
CHANGED
|
@@ -18,3 +18,6 @@ export function readProviderDefinitionQuery(providerId) {
|
|
|
18
18
|
export function registryStateEvent() {
|
|
19
19
|
return `${VERSION_PREFIX}.${LOCATION_PREFIX}.registry.state.evt.changed`;
|
|
20
20
|
}
|
|
21
|
+
export function writeVariablesCommand(providerId) {
|
|
22
|
+
return `${VERSION_PREFIX}.${LOCATION_PREFIX}.${providerId}.vars.cmd.write`;
|
|
23
|
+
}
|
package/nodes/datahub-input.html
CHANGED
|
@@ -5,7 +5,7 @@
|
|
|
5
5
|
defaults: {
|
|
6
6
|
name: { value: "" },
|
|
7
7
|
connection: { type: "uos-config", required: true },
|
|
8
|
-
providerId: { value: "", required: true },
|
|
8
|
+
providerId: { value: "", required: true }, // Changed default to empty to encourage selection
|
|
9
9
|
manualVariables: { value: "" },
|
|
10
10
|
triggerMode: { value: "event" },
|
|
11
11
|
pollingInterval: { value: 0, validate: RED.validators.number() },
|
|
@@ -16,7 +16,7 @@
|
|
|
16
16
|
icon: "datahub-read.svg",
|
|
17
17
|
label: function () {
|
|
18
18
|
if (this.name) return this.name;
|
|
19
|
-
return "DataHub - Read";
|
|
19
|
+
return this.providerId || "DataHub - Read";
|
|
20
20
|
},
|
|
21
21
|
paletteLabel: "DataHub - Read",
|
|
22
22
|
oneditprepare: function () {
|
|
@@ -30,11 +30,16 @@
|
|
|
30
30
|
|
|
31
31
|
// Convert stored ms to unit for display
|
|
32
32
|
let currentMs = node.pollingInterval || 0;
|
|
33
|
-
let uiVal = currentMs;
|
|
34
33
|
let uiUnit = node.pollingUnit || 'ms';
|
|
34
|
+
let uiVal = currentMs;
|
|
35
35
|
|
|
36
|
-
//
|
|
37
|
-
if (
|
|
36
|
+
// If unit is set, reverse convert for display
|
|
37
|
+
if (uiUnit === 'min') {
|
|
38
|
+
uiVal = currentMs / 60000;
|
|
39
|
+
} else if (uiUnit === 's') {
|
|
40
|
+
uiVal = currentMs / 1000;
|
|
41
|
+
} else if (!node.pollingUnit && currentMs > 0) {
|
|
42
|
+
// Heuristic fallback (Legacy): Guess unit if none was saved
|
|
38
43
|
if (currentMs >= 60000 && currentMs % 60000 === 0) { uiVal = currentMs / 60000; uiUnit = 'min'; }
|
|
39
44
|
else if (currentMs >= 1000 && currentMs % 1000 === 0) { uiVal = currentMs / 1000; uiUnit = 's'; }
|
|
40
45
|
}
|
|
@@ -52,6 +57,66 @@
|
|
|
52
57
|
$triggerMode.on('change', updateTriggerVisibility);
|
|
53
58
|
updateTriggerVisibility();
|
|
54
59
|
|
|
60
|
+
// --- Provider Lookup Logic ---
|
|
61
|
+
$('#btn-lookup-provider').on('click', function () {
|
|
62
|
+
const configNodeId = $('#node-input-connection').val();
|
|
63
|
+
const $btn = $(this);
|
|
64
|
+
|
|
65
|
+
if (!configNodeId || configNodeId === "_ADD_") {
|
|
66
|
+
RED.notify("Please select a valid Configuration Node first.", "error");
|
|
67
|
+
return;
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
$btn.addClass('disabled').html('<i class="fa fa-spinner fa-spin"></i>');
|
|
71
|
+
|
|
72
|
+
$.getJSON('uos/providers/' + configNodeId, function (data) {
|
|
73
|
+
$btn.removeClass('disabled').html('<i class="fa fa-search"></i>');
|
|
74
|
+
|
|
75
|
+
if (!data || data.length === 0) {
|
|
76
|
+
RED.notify("No providers found.", "warning");
|
|
77
|
+
return;
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
// Build List
|
|
81
|
+
const providerList = data.map(p => {
|
|
82
|
+
const pid = p.id || p.Id || p;
|
|
83
|
+
return `<li class="red-ui-list-item provider-item" data-id="${pid}" style="cursor: pointer; padding: 8px; border-bottom: 1px solid #eee;">
|
|
84
|
+
<i class="fa fa-cube"></i> <b>${pid}</b>
|
|
85
|
+
</li>`;
|
|
86
|
+
}).join('');
|
|
87
|
+
|
|
88
|
+
const content = `<div style="padding:10px; font-size:12px; color:#666;">Select a provider to read from:</div>
|
|
89
|
+
<ul style="list-style: none; padding: 0; margin:0; max-height: 300px; overflow-y: auto; border:1px solid #ddd;">${providerList}</ul>`;
|
|
90
|
+
|
|
91
|
+
const $dialog = $('<div id="provider-lookup-dialog"></div>')
|
|
92
|
+
.html(content)
|
|
93
|
+
.dialog({
|
|
94
|
+
title: "Select Provider",
|
|
95
|
+
width: 400,
|
|
96
|
+
modal: true,
|
|
97
|
+
buttons: {
|
|
98
|
+
"Cancel": function () { $(this).dialog("close"); }
|
|
99
|
+
}
|
|
100
|
+
});
|
|
101
|
+
|
|
102
|
+
// Click Handler
|
|
103
|
+
$dialog.find('.provider-item').hover(
|
|
104
|
+
function () { $(this).css("background-color", "#eef"); },
|
|
105
|
+
function () { $(this).css("background-color", "white"); }
|
|
106
|
+
).click(function () {
|
|
107
|
+
const selectedId = $(this).data('id');
|
|
108
|
+
$("#node-input-providerId").val(selectedId);
|
|
109
|
+
$dialog.dialog("close");
|
|
110
|
+
$dialog.remove(); // Cleanup
|
|
111
|
+
});
|
|
112
|
+
|
|
113
|
+
}).fail(function (jqxhr) {
|
|
114
|
+
$btn.removeClass('disabled').html('<i class="fa fa-search"></i>');
|
|
115
|
+
RED.notify("Could not fetch providers. Ensure Config Node is DEPLOYED.", "error");
|
|
116
|
+
});
|
|
117
|
+
});
|
|
118
|
+
|
|
119
|
+
|
|
55
120
|
// --- Variable Selector Logic ---
|
|
56
121
|
const $fetchBtn = $('#btn-fetch-vars');
|
|
57
122
|
const $listContainer = $('#variable-list-container');
|
|
@@ -61,17 +126,14 @@
|
|
|
61
126
|
const $clearAllBtn = $('#btn-clear-all');
|
|
62
127
|
const $hiddenManual = $('#node-input-manualVariables');
|
|
63
128
|
|
|
64
|
-
// Styles
|
|
65
|
-
// Adjusted: ID column removed per user request (Key is main identifier)
|
|
66
129
|
const rowStyle = "display:grid; grid-template-columns: 30px 1fr; align-items:center; padding:4px 0; border-bottom:1px solid #eee; font-family:'Helvetica Neue', Arial, sans-serif; font-size:12px;";
|
|
67
|
-
// const idStyle = ... (removed)
|
|
68
130
|
const keyStyle = "overflow:hidden; text-overflow:ellipsis; white-space:nowrap; padding-left:10px;";
|
|
69
131
|
|
|
70
|
-
let currentVariables = [];
|
|
132
|
+
let currentVariables = [];
|
|
71
133
|
|
|
72
134
|
// Parse existing selection
|
|
73
135
|
const getSelectedMap = () => {
|
|
74
|
-
const selected = new Map();
|
|
136
|
+
const selected = new Map();
|
|
75
137
|
const raw = $hiddenManual.val();
|
|
76
138
|
if (raw) {
|
|
77
139
|
raw.split(',').forEach(entry => {
|
|
@@ -94,7 +156,6 @@
|
|
|
94
156
|
const selectedMap = getSelectedMap();
|
|
95
157
|
|
|
96
158
|
vars.forEach(v => {
|
|
97
|
-
// Robust ID handling
|
|
98
159
|
let rawId = (v.id !== undefined && v.id !== null) ? v.id : v.Id;
|
|
99
160
|
const safeId = (rawId !== undefined && rawId !== null) ? rawId : 'ERR';
|
|
100
161
|
const isSelected = selectedMap.has(v.key);
|
|
@@ -105,15 +166,12 @@
|
|
|
105
166
|
const cb = $('<input type="checkbox" class="var-checkbox">')
|
|
106
167
|
.prop('checked', isSelected)
|
|
107
168
|
.data('key', v.key)
|
|
108
|
-
.data('id', safeId);
|
|
169
|
+
.data('id', safeId);
|
|
109
170
|
cbContainer.append(cb);
|
|
110
171
|
|
|
111
|
-
// ID Column Removed from View
|
|
112
|
-
// const idBadge = ...
|
|
113
|
-
|
|
114
172
|
const label = $('<div>', { style: keyStyle, title: v.key }).text(v.key);
|
|
115
173
|
|
|
116
|
-
row.append(cbContainer).append(label);
|
|
174
|
+
row.append(cbContainer).append(label);
|
|
117
175
|
$listContainer.append(row);
|
|
118
176
|
});
|
|
119
177
|
};
|
|
@@ -122,7 +180,7 @@
|
|
|
122
180
|
const rows = $listContainer.find('.var-row');
|
|
123
181
|
term = term.toLowerCase();
|
|
124
182
|
rows.each(function () {
|
|
125
|
-
const text = $(this).find('div:nth-child(
|
|
183
|
+
const text = $(this).find('div:nth-child(2)').text().toLowerCase(); // 2nd child is name (grid)
|
|
126
184
|
$(this).toggle(text.indexOf(term) > -1);
|
|
127
185
|
});
|
|
128
186
|
};
|
|
@@ -156,21 +214,27 @@
|
|
|
156
214
|
$statusMsg.text('Fetching...').css('color', 'blue');
|
|
157
215
|
$listContainer.html('<div style="padding:20px; text-align:center;"><i class="fa fa-spinner fa-spin"></i> Loading...</div>');
|
|
158
216
|
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
217
|
+
// Headers for stateless support would go here if we had access to credentials in editor?
|
|
218
|
+
// We generally don't. But fetch providers works if deployed.
|
|
219
|
+
|
|
220
|
+
$.ajax({
|
|
221
|
+
url: 'uos/providers/' + configNodeId + '/' + providerId + '/variables',
|
|
222
|
+
success: function (data) {
|
|
223
|
+
$fetchBtn.prop('disabled', false);
|
|
224
|
+
$statusMsg.text('');
|
|
225
|
+
currentVariables = data.sort((a, b) => {
|
|
226
|
+
const idA = (a.id !== undefined) ? parseInt(a.id) : 0;
|
|
227
|
+
const idB = (b.id !== undefined) ? parseInt(b.id) : 0;
|
|
228
|
+
return idA - idB;
|
|
229
|
+
});
|
|
230
|
+
renderList(currentVariables);
|
|
231
|
+
$statusMsg.text(`Loaded ${data.length} variables.`).css('color', 'green');
|
|
232
|
+
},
|
|
233
|
+
error: function (jqxhr) {
|
|
234
|
+
$fetchBtn.prop('disabled', false);
|
|
235
|
+
$statusMsg.text('Error: ' + (jqxhr.responseJSON?.error || 'Unknown')).css('color', 'red');
|
|
236
|
+
$listContainer.html('<div style="padding:15px; color:#c00; text-align:center;">Failed to load variables.<br>Ensure Config is deployed.</div>');
|
|
237
|
+
}
|
|
174
238
|
});
|
|
175
239
|
});
|
|
176
240
|
|
|
@@ -179,11 +243,10 @@
|
|
|
179
243
|
if (initialMap.size > 0 && currentVariables.length === 0) {
|
|
180
244
|
const dummyVars = [];
|
|
181
245
|
initialMap.forEach((id, key) => dummyVars.push({ id, key }));
|
|
182
|
-
// Just in case stored ID is undefined, show it so user can see it's broken
|
|
183
246
|
renderList(dummyVars);
|
|
184
|
-
$statusMsg.text('Cached
|
|
247
|
+
$statusMsg.text('Cached selection shown. Load again to refresh.').css('color', '#888');
|
|
185
248
|
} else {
|
|
186
|
-
$listContainer.html('<div style="padding:20px; text-align:center; color:#999;">
|
|
249
|
+
$listContainer.html('<div style="padding:20px; text-align:center; color:#999;">Click <b>Load Variables</b> to browse.</div>');
|
|
187
250
|
}
|
|
188
251
|
},
|
|
189
252
|
oneditsave: function () {
|
|
@@ -193,7 +256,6 @@
|
|
|
193
256
|
if ($(this).prop('checked')) {
|
|
194
257
|
const k = $(this).data('key');
|
|
195
258
|
const i = $(this).data('id');
|
|
196
|
-
// Important: Don't save if ID is 'ERR' or undefined
|
|
197
259
|
if (k && i !== undefined && i !== 'ERR') {
|
|
198
260
|
items.push(`${k}:${i}`);
|
|
199
261
|
}
|
|
@@ -201,20 +263,15 @@
|
|
|
201
263
|
});
|
|
202
264
|
$('#node-input-manualVariables').val(items.join(','));
|
|
203
265
|
|
|
204
|
-
// 2. Save Polling Interval
|
|
266
|
+
// 2. Save Polling Interval
|
|
205
267
|
const val = parseInt($('#node-input-pollingInterval').val(), 10) || 0;
|
|
206
268
|
const unit = $('#node-input-pollingUnit').val();
|
|
207
269
|
let multiplier = 1;
|
|
208
270
|
if (unit === 's') multiplier = 1000;
|
|
209
271
|
if (unit === 'min') multiplier = 60000;
|
|
210
|
-
|
|
211
|
-
// We overwrite the raw value with calculated MS.
|
|
212
|
-
// WAIT! The 'defaults' define pollingInterval. If we overwrite it here, the UI input needs to read it back correctly in oneditprepare.
|
|
213
|
-
// Actually, standard pattern is to store the MS value in 'pollingInterval' and recover the Unit in UI.
|
|
214
|
-
// But my oneditprepare logic for unit recovery was heuristic. Let's make it robust by trusting the calculation.
|
|
215
272
|
const totalMs = val * multiplier;
|
|
216
273
|
$('#node-input-pollingInterval').val(totalMs);
|
|
217
|
-
$('#node-input-pollingUnit').val(unit);
|
|
274
|
+
$('#node-input-pollingUnit').val(unit);
|
|
218
275
|
}
|
|
219
276
|
});
|
|
220
277
|
</script>
|
|
@@ -232,39 +289,38 @@
|
|
|
232
289
|
|
|
233
290
|
<div class="form-row">
|
|
234
291
|
<label for="node-input-providerId"><i class="fa fa-server"></i> Provider ID</label>
|
|
235
|
-
<div style="display:flex;
|
|
292
|
+
<div style="display:flex;">
|
|
236
293
|
<input type="text" id="node-input-providerId" placeholder="e.g. u_os_adm" style="flex:1;">
|
|
237
|
-
<
|
|
294
|
+
<a id="btn-lookup-provider" class="red-ui-button" style="margin-left: 5px;" title="Search Providers"><i class="fa fa-search"></i></a>
|
|
238
295
|
</div>
|
|
239
|
-
<div id="fetch-status" style="margin-top:5px; font-size:0.9em; min-height:1.2em;"></div>
|
|
240
296
|
</div>
|
|
241
297
|
|
|
242
|
-
<div class="form-row"
|
|
243
|
-
<div style="
|
|
244
|
-
|
|
245
|
-
|
|
298
|
+
<div class="form-row">
|
|
299
|
+
<div style="display:flex; justify-content:space-between; align-items:center;">
|
|
300
|
+
<label><i class="fa fa-list"></i> Variables</label>
|
|
301
|
+
<button id="btn-fetch-vars" class="red-ui-button red-ui-button-small"><i class="fa fa-refresh"></i> Load Variables</button>
|
|
246
302
|
</div>
|
|
303
|
+
<div id="fetch-status" style="font-size:0.9em; min-height:1.2em; text-align:right; margin-bottom:5px;"></div>
|
|
247
304
|
|
|
248
|
-
<div style="
|
|
249
|
-
<div style="
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
305
|
+
<div style="border:1px solid #ccc; padding:0; border-radius:4px; background:#fff;">
|
|
306
|
+
<div style="background:#f7f7f7; padding:4px 10px; border-bottom:1px solid #ddd; display:flex; justify-content:space-between; align-items:center;">
|
|
307
|
+
<span style="font-size:11px; font-weight:bold;">Select Variables</span>
|
|
308
|
+
<input type="text" id="node-input-var-search" placeholder="Filter..." style="width:100px; font-size:11px; padding:2px;">
|
|
309
|
+
</div>
|
|
310
|
+
|
|
311
|
+
<div id="variable-list-container" style="height:250px; overflow-y:auto; background:white;">
|
|
312
|
+
<!-- Variables -->
|
|
313
|
+
</div>
|
|
314
|
+
|
|
315
|
+
<div style="background:#f7f7f7; padding:5px 10px; border-top:1px solid #ddd; display:flex; gap:10px;">
|
|
316
|
+
<button id="btn-select-all" class="red-ui-button red-ui-button-small">All</button>
|
|
317
|
+
<button id="btn-clear-all" class="red-ui-button red-ui-button-small">None</button>
|
|
318
|
+
<div style="flex:1; text-align:right; color:#888; font-size:10px; padding-top:4px;">Empty selection reads ALL variables</div>
|
|
319
|
+
</div>
|
|
262
320
|
</div>
|
|
263
321
|
<input type="hidden" id="node-input-manualVariables">
|
|
264
322
|
</div>
|
|
265
323
|
|
|
266
|
-
<hr style="margin:15px 0;">
|
|
267
|
-
|
|
268
324
|
<div class="form-row">
|
|
269
325
|
<label for="node-input-triggerMode"><i class="fa fa-bolt"></i> Trigger</label>
|
|
270
326
|
<select id="node-input-triggerMode" style="width:70%;">
|
|
@@ -285,119 +341,11 @@
|
|
|
285
341
|
</script>
|
|
286
342
|
|
|
287
343
|
<script type="text/html" data-help-name="datahub-input">
|
|
288
|
-
<p><b>DataHub - Read</b> reads variables from u-OS Data Hub providers
|
|
289
|
-
|
|
290
|
-
<h3>Quick Start</h3>
|
|
344
|
+
<p><b>DataHub - Read</b> reads variables from u-OS Data Hub providers.</p>
|
|
345
|
+
<h3>Configuration</h3>
|
|
291
346
|
<ol>
|
|
292
|
-
<li
|
|
293
|
-
<li
|
|
294
|
-
<li>
|
|
295
|
-
<li>Deploy and send a message to the input port to trigger a read</li>
|
|
347
|
+
<li><b>Config:</b> Select your u-OS connection.</li>
|
|
348
|
+
<li><b>Provider ID:</b> ID of the data source (e.g., <code>u_os_adm</code>). Use the search button to find available providers.</li>
|
|
349
|
+
<li><b>Variables:</b> Click "Load Variables" to select specific data points. Leave empty to read everything.</li>
|
|
296
350
|
</ol>
|
|
297
|
-
|
|
298
|
-
<h3>Configuration</h3>
|
|
299
|
-
|
|
300
|
-
<h4>1. Config Node</h4>
|
|
301
|
-
<p>
|
|
302
|
-
Select your u-OS connection settings. If you haven't created one yet, click the <b>pencil icon</b> next to "Config" to create a new uos-config node.
|
|
303
|
-
</p>
|
|
304
|
-
|
|
305
|
-
<h4>2. Provider ID</h4>
|
|
306
|
-
<p>
|
|
307
|
-
The name of the data source you want to read from.
|
|
308
|
-
</p>
|
|
309
|
-
<p><b>How to find it:</b></p>
|
|
310
|
-
<ul>
|
|
311
|
-
<li><b>Web UI:</b> u-Control → Data Hub → Providers → Note the Provider ID</li>
|
|
312
|
-
<li><b>Common examples:</b> <code>u_os_adm</code>, <code>hub</code>, <code>custom-provider</code></li>
|
|
313
|
-
</ul>
|
|
314
|
-
|
|
315
|
-
<h4>3. Variables Table</h4>
|
|
316
|
-
<p>
|
|
317
|
-
Use the <b>Load Variables</b> button to browse and select variables from the provider.
|
|
318
|
-
Selected variables are automatically added to the list.
|
|
319
|
-
</p>
|
|
320
|
-
<p>
|
|
321
|
-
The table maps Variable Keys (names) to their IDs.
|
|
322
|
-
</p>
|
|
323
|
-
|
|
324
|
-
<h5>Manual Entry (Fallback)</h5>
|
|
325
|
-
<p>
|
|
326
|
-
If the "Load Variables" feature is unavailable, you can manually add variables knowing their IDs:
|
|
327
|
-
</p>
|
|
328
|
-
<ul>
|
|
329
|
-
<li><b>Variable Key:</b> The name (e.g. <code>machine.temp</code>)</li>
|
|
330
|
-
<li><b>Variable ID:</b> The numeric ID (e.g. <code>5</code>)</li>
|
|
331
|
-
</ul>
|
|
332
|
-
|
|
333
|
-
<h4>4. Trigger Mode</h4>
|
|
334
|
-
<dl>
|
|
335
|
-
<dt>Event (on change) – Default</dt>
|
|
336
|
-
<dd>Efficient. Outputs only when values change. Recommended for most use cases.</dd>
|
|
337
|
-
|
|
338
|
-
<dt>Poll (interval)</dt>
|
|
339
|
-
<dd>Forces periodic reads (e.g. every 1000ms). Use if you need guaranteed updates regardless of changes.</dd>
|
|
340
|
-
</dl>
|
|
341
|
-
|
|
342
|
-
<h3>Output Format</h3>
|
|
343
|
-
<p>When triggered (via input port), the node outputs:</p>
|
|
344
|
-
<pre><code>{
|
|
345
|
-
"type": "snapshot",
|
|
346
|
-
"variables": [
|
|
347
|
-
{
|
|
348
|
-
"providerId": "u_os_adm",
|
|
349
|
-
"id": 0,
|
|
350
|
-
"key": "manufacturer_name",
|
|
351
|
-
"value": "Weidmüller",
|
|
352
|
-
"quality": "GOOD",
|
|
353
|
-
"timestampNs": 1234567890000000000
|
|
354
|
-
}
|
|
355
|
-
]
|
|
356
|
-
}</code></pre>
|
|
357
|
-
|
|
358
|
-
<h3>Triggering Reads</h3>
|
|
359
|
-
<p>
|
|
360
|
-
Connect an <b>Inject</b> node to the input port. Sending any message triggers an immediate read.
|
|
361
|
-
</p>
|
|
362
|
-
<p>
|
|
363
|
-
<b>Event Mode:</b> Also outputs automatically when values change.<br>
|
|
364
|
-
<b>Poll Mode:</b> Reads at the specified interval (e.g. every 1000ms).
|
|
365
|
-
</p>
|
|
366
|
-
|
|
367
|
-
<h3>⚠️ Important: Don't Read Your Own Output Provider</h3>
|
|
368
|
-
<p>
|
|
369
|
-
<b>Do NOT</b> use the provider created by your <b>DataHub - OUT</b> node (e.g. <code>nodered</code>) as input.
|
|
370
|
-
</p>
|
|
371
|
-
<p><b>Why?</b></p>
|
|
372
|
-
<ul>
|
|
373
|
-
<li>The Output provider only exists <b>while Node-RED is running</b></li>
|
|
374
|
-
<li>On restart, the provider disappears → Input node fails</li>
|
|
375
|
-
<li>This creates a circular dependency</li>
|
|
376
|
-
</ul>
|
|
377
|
-
<p><b>What to use instead:</b></p>
|
|
378
|
-
<ul>
|
|
379
|
-
<li>Read from <b>system providers</b> (e.g. <code>u_os_adm</code>, <code>hub</code>)</li>
|
|
380
|
-
<li>Read from <b>other apps/devices</b> (not your own Node-RED)</li>
|
|
381
|
-
</ul>
|
|
382
|
-
|
|
383
|
-
<h3>Troubleshooting</h3>
|
|
384
|
-
|
|
385
|
-
<h4>No Output</h4>
|
|
386
|
-
<ul>
|
|
387
|
-
<li>✓ Config node deployed?</li>
|
|
388
|
-
<li>✓ Provider ID correct?</li>
|
|
389
|
-
<li>✓ Variable IDs correct?</li>
|
|
390
|
-
<li>✓ Inject signal sent to input port?</li>
|
|
391
|
-
</ul>
|
|
392
|
-
|
|
393
|
-
<h4>"Variable not found"</h4>
|
|
394
|
-
<ul>
|
|
395
|
-
<li>Double-check the IDs in your table</li>
|
|
396
|
-
<li>Verify IDs match those in the Python config or Web UI</li>
|
|
397
|
-
</ul>
|
|
398
|
-
|
|
399
|
-
<div style="margin-top:15px; padding-top:10px; border-top:1px solid #ddd; font-size:0.85em; color:#666;">
|
|
400
|
-
<p><b>Developed by <a href="https://www.linkedin.com/in/iotueli/" target="_blank">IoTUeli</a></b></p>
|
|
401
|
-
<p>Not an official Weidmüller product. Community contribution.</p>
|
|
402
|
-
</div>
|
|
403
351
|
</script>
|