node-red-contrib-modbus-modpackqt 3.3.24 → 3.3.27
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/CHANGELOG.md +46 -0
- package/nodes/icons/modpackqt.png +0 -0
- package/nodes/lib/probe-runtime.js +1 -1
- package/nodes/modpackqt-config.html +88 -0
- package/nodes/modpackqt-config.js +7 -6
- package/nodes/modpackqt-master-probe.html +1 -1
- package/nodes/modpackqt-master-read.html +1 -1
- package/nodes/modpackqt-master-write.html +1 -1
- package/nodes/modpackqt-slave-probe.html +6 -6
- package/nodes/modpackqt-slave-read.html +1 -1
- package/nodes/modpackqt-slave-server.html +11 -5
- package/nodes/modpackqt-slave-write.html +1 -1
- package/package.json +1 -1
- package/nodes/icons/modpackqt.svg +0 -8
package/CHANGELOG.md
CHANGED
|
@@ -4,6 +4,52 @@ All notable changes to **node-red-contrib-modbus-modpackqt** are documented
|
|
|
4
4
|
here. This project follows [Semantic Versioning](https://semver.org/) — pin a
|
|
5
5
|
major version (`^2.0.0`) in production.
|
|
6
6
|
|
|
7
|
+
## [3.3.26] — 2026-05-23
|
|
8
|
+
|
|
9
|
+
### Added
|
|
10
|
+
|
|
11
|
+
- **Cloud profile pickers — no more typing IPs manually.**
|
|
12
|
+
Paste your **Cloud Gateway Key** (generate one at
|
|
13
|
+
modpackqt.com → Settings → Cloud Gateways) into the runtime config node and
|
|
14
|
+
a **My Connections** dropdown instantly loads all your saved Modbus devices,
|
|
15
|
+
auto-filling Host, Port, Unit ID, and transport mode. The slave-server and
|
|
16
|
+
slave-probe nodes gain a matching **My Slaves** picker that auto-fills Port
|
|
17
|
+
and Unit ID from your saved slave configs.
|
|
18
|
+
|
|
19
|
+
- **Server-side proxy — key never reaches the browser.**
|
|
20
|
+
`modpackqt-config.js` registers two Node-RED admin endpoints
|
|
21
|
+
(`GET /modpackqt/connections` and `GET /modpackqt/slaves`) that look up the
|
|
22
|
+
stored credential on the config/probe node and proxy the request to
|
|
23
|
+
modpackqt.com with the `X-Tunnel-Key` header. The browser only sees the
|
|
24
|
+
JSON result — credentials stay in the Node-RED process.
|
|
25
|
+
|
|
26
|
+
- **Reuses existing auth — no new mechanism.**
|
|
27
|
+
Uses the same `X-Tunnel-Key` header the Electron gateway already sends to
|
|
28
|
+
modpackqt.com. The cloud server now accepts this header on
|
|
29
|
+
`GET /api/connections` and `GET /api/slaves`, so no new endpoints or tokens
|
|
30
|
+
were introduced.
|
|
31
|
+
|
|
32
|
+
### Fixed
|
|
33
|
+
|
|
34
|
+
- **slave-probe node relabelled "Account Key" → "Cloud Gateway Key"** in
|
|
35
|
+
both the editor dialog and the help panel, consistent with the master-probe
|
|
36
|
+
and slave-server nodes (fixed in v3.3.24 for those nodes; now consistent
|
|
37
|
+
everywhere).
|
|
38
|
+
|
|
39
|
+
### Unchanged
|
|
40
|
+
|
|
41
|
+
- Manual host/port entry is always available as a fallback — no Cloud Gateway
|
|
42
|
+
Key is required for any Modbus master/slave operation. All Modbus traffic
|
|
43
|
+
remains 100% local regardless of whether a key is set.
|
|
44
|
+
|
|
45
|
+
## [3.3.25] — 2026-05-10
|
|
46
|
+
|
|
47
|
+
### Changed
|
|
48
|
+
|
|
49
|
+
- **Icon now uses the actual ModPackQT brand PNG** instead of the
|
|
50
|
+
earlier hand-drawn SVG approximation. Pixel-identical to the
|
|
51
|
+
modpackqt.com logo.
|
|
52
|
+
|
|
7
53
|
## [3.3.24] — 2026-05-10
|
|
8
54
|
|
|
9
55
|
### Changed
|
|
Binary file
|
|
@@ -18,7 +18,7 @@
|
|
|
18
18
|
const http = require('http');
|
|
19
19
|
const { URL } = require('url');
|
|
20
20
|
|
|
21
|
-
const PALETTE_VERSION = '3.3.
|
|
21
|
+
const PALETTE_VERSION = '3.3.26';
|
|
22
22
|
const DEFAULT_PORT = parseInt(process.env.MODPACKQT_PROBE_PORT, 10) || 8502;
|
|
23
23
|
const BIND_HOST = process.env.MODPACKQT_PROBE_HOST || '127.0.0.1';
|
|
24
24
|
const PORT_RETRY = 5;
|
|
@@ -17,6 +17,9 @@
|
|
|
17
17
|
slaveHost: { value: '0.0.0.0' },
|
|
18
18
|
slavePort: { value: 1502, validate: RED.validators.number() }
|
|
19
19
|
},
|
|
20
|
+
credentials: {
|
|
21
|
+
apiKey: { type: 'password' }
|
|
22
|
+
},
|
|
20
23
|
label: function () {
|
|
21
24
|
const t = this.masterMode === 'rtu'
|
|
22
25
|
? `RTU ${this.serialPort || '?'} #${this.unitId || 1}`
|
|
@@ -25,6 +28,9 @@
|
|
|
25
28
|
return this.name && this.name !== 'ModPackQT Device' ? `${this.name} (${t})` : `${t}${s}`;
|
|
26
29
|
},
|
|
27
30
|
oneditprepare: function () {
|
|
31
|
+
const node = this;
|
|
32
|
+
|
|
33
|
+
// ── Transport toggle
|
|
28
34
|
const toggleRtu = () => {
|
|
29
35
|
const isRtu = $('#node-config-input-masterMode').val() === 'rtu';
|
|
30
36
|
$('.modpackqt-rtu-only').toggle(isRtu);
|
|
@@ -32,6 +38,60 @@
|
|
|
32
38
|
};
|
|
33
39
|
$('#node-config-input-masterMode').on('change', toggleRtu);
|
|
34
40
|
toggleRtu();
|
|
41
|
+
|
|
42
|
+
// ── My Connections picker — uses this config node's own Cloud Gateway Key
|
|
43
|
+
const $sel = $('#node-config-input-modpackqt-conn-picker');
|
|
44
|
+
const $hint = $('#modpackqt-config-conn-picker-hint');
|
|
45
|
+
const reload = () => {
|
|
46
|
+
$sel.empty().append('<option value="">— Loading… —</option>');
|
|
47
|
+
if (!node.id) {
|
|
48
|
+
$sel.empty().append('<option value="">— Save this node first, then reopen —</option>');
|
|
49
|
+
$hint.text('Click Done once to save the node, then reopen to load your connections.');
|
|
50
|
+
return;
|
|
51
|
+
}
|
|
52
|
+
$.getJSON('modpackqt/connections?config=' + encodeURIComponent(node.id))
|
|
53
|
+
.done((rows) => {
|
|
54
|
+
$sel.empty().append('<option value="">— Manual entry —</option>');
|
|
55
|
+
(rows || []).forEach((c) => {
|
|
56
|
+
const label = (c.name || '(unnamed)') + (c.connectionType === 'rtu'
|
|
57
|
+
? ' — RTU'
|
|
58
|
+
: ' — ' + (c.host || '?') + ':' + (c.port || 502) + ' #' + (c.unitId || 1));
|
|
59
|
+
$('<option>').val(c.id).text(label).data('conn', c).appendTo($sel);
|
|
60
|
+
});
|
|
61
|
+
$hint.text(
|
|
62
|
+
(rows && rows.length)
|
|
63
|
+
? 'Pick a saved connection to auto-fill the fields below.'
|
|
64
|
+
: 'No saved connections yet — add one at modpackqt.com → Connections.'
|
|
65
|
+
);
|
|
66
|
+
// Pre-select if the current host/port matches a saved connection
|
|
67
|
+
(rows || []).some((c) => {
|
|
68
|
+
if (c.host === (node.targetHost || '') &&
|
|
69
|
+
Number(c.port) === Number(node.targetPort || 0)) {
|
|
70
|
+
$sel.val(c.id); return true;
|
|
71
|
+
}
|
|
72
|
+
return false;
|
|
73
|
+
});
|
|
74
|
+
})
|
|
75
|
+
.fail((xhr) => {
|
|
76
|
+
const msg = (xhr.responseJSON && xhr.responseJSON.error) || ('HTTP ' + xhr.status);
|
|
77
|
+
$sel.empty().append($('<option>').val('').text('— ' + msg + ' —'));
|
|
78
|
+
$hint.text('Check the Cloud Gateway Key above.');
|
|
79
|
+
});
|
|
80
|
+
};
|
|
81
|
+
$('#modpackqt-config-conn-picker-refresh').on('click', (e) => { e.preventDefault(); reload(); });
|
|
82
|
+
$sel.on('change', function () {
|
|
83
|
+
const c = $(this).find('option:selected').data('conn');
|
|
84
|
+
if (!c) return;
|
|
85
|
+
if (c.host) $('#node-config-input-targetHost').val(c.host);
|
|
86
|
+
if (c.port) $('#node-config-input-targetPort').val(c.port);
|
|
87
|
+
if (c.unitId) $('#node-config-input-unitId').val(c.unitId);
|
|
88
|
+
if (c.connectionType === 'rtu') $('#node-config-input-masterMode').val('rtu').trigger('change');
|
|
89
|
+
else $('#node-config-input-masterMode').val('tcp').trigger('change');
|
|
90
|
+
if (!$('#node-config-input-name').val() || $('#node-config-input-name').val() === 'ModPackQT Device') {
|
|
91
|
+
if (c.name) $('#node-config-input-name').val(c.name);
|
|
92
|
+
}
|
|
93
|
+
});
|
|
94
|
+
reload();
|
|
35
95
|
}
|
|
36
96
|
});
|
|
37
97
|
</script>
|
|
@@ -42,6 +102,24 @@
|
|
|
42
102
|
<input type="text" id="node-config-input-name" placeholder="e.g. Inverter A">
|
|
43
103
|
</div>
|
|
44
104
|
|
|
105
|
+
<div class="form-row">
|
|
106
|
+
<label for="node-config-input-apiKey"><i class="fa fa-key"></i> Cloud Gateway Key</label>
|
|
107
|
+
<input type="password" id="node-config-input-apiKey" placeholder="Paste your ModPackQT tunnel key (optional)">
|
|
108
|
+
</div>
|
|
109
|
+
<div class="form-tips" style="font-size:11px;color:#6b7280;margin:-8px 0 12px 105px">
|
|
110
|
+
Optional. Generate at <a href="https://modpackqt.com/settings" target="_blank" rel="noopener">modpackqt.com → Settings → Cloud Gateways</a>.
|
|
111
|
+
Lets you pick a saved connection from the dropdown below instead of typing host/port manually.
|
|
112
|
+
</div>
|
|
113
|
+
|
|
114
|
+
<div class="form-row">
|
|
115
|
+
<label for="node-config-input-modpackqt-conn-picker"><i class="fa fa-cloud-download"></i> My Connections</label>
|
|
116
|
+
<select id="node-config-input-modpackqt-conn-picker" style="width:65%"></select>
|
|
117
|
+
<button type="button" id="modpackqt-config-conn-picker-refresh" class="red-ui-button" title="Reload" style="margin-left:4px"><i class="fa fa-refresh"></i></button>
|
|
118
|
+
</div>
|
|
119
|
+
<div id="modpackqt-config-conn-picker-hint" class="form-tips" style="font-size:11px;color:#6b7280;margin:-8px 0 12px 105px">
|
|
120
|
+
Pick a saved connection to auto-fill the fields below, or leave on Manual entry and type them yourself.
|
|
121
|
+
</div>
|
|
122
|
+
|
|
45
123
|
<h4 style="margin-top:18px">Target Device</h4>
|
|
46
124
|
<div class="form-row modpackqt-tcp-only">
|
|
47
125
|
<label for="node-config-input-targetHost"><i class="fa fa-plug"></i> IP Address</label>
|
|
@@ -138,6 +216,16 @@
|
|
|
138
216
|
only need to know the function code, address, and quantity.
|
|
139
217
|
</p>
|
|
140
218
|
|
|
219
|
+
<h3>Cloud Gateway Key + My Connections</h3>
|
|
220
|
+
<p>
|
|
221
|
+
Paste your ModPackQT tunnel key (generate one at
|
|
222
|
+
<a href="https://modpackqt.com/settings" target="_blank" rel="noopener">modpackqt.com → Settings → Cloud Gateways</a>)
|
|
223
|
+
to unlock the <b>My Connections</b> picker. Selecting a saved connection auto-fills
|
|
224
|
+
the IP address, port, unit ID, and transport mode — no manual typing needed.
|
|
225
|
+
The key is stored as a Node-RED credential (never sent to the browser).
|
|
226
|
+
Leave the key blank to enter host/port manually as before.
|
|
227
|
+
</p>
|
|
228
|
+
|
|
141
229
|
<h3>Master mode</h3>
|
|
142
230
|
<ul>
|
|
143
231
|
<li><b>Modbus TCP</b> — connects to the configured Host/Port.</li>
|
|
@@ -19,12 +19,13 @@ module.exports = function (RED) {
|
|
|
19
19
|
RED._modpackqtAdminRoutesRegistered = true;
|
|
20
20
|
function makeProxy(fetchFn) {
|
|
21
21
|
return function (req, res) {
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
22
|
+
// Accept ?config=<id> (config node) or ?probe=<id> (probe node) — same key field.
|
|
23
|
+
const nodeId = (req.query && (req.query.config || req.query.probe)) || '';
|
|
24
|
+
if (!nodeId) return res.status(400).json({ error: 'config or probe query param required' });
|
|
25
|
+
const node = RED.nodes.getNode(nodeId);
|
|
26
|
+
if (!node) return res.status(404).json({ error: 'Node not found (deploy first?)' });
|
|
27
|
+
const key = node.credentials && node.credentials.apiKey;
|
|
28
|
+
if (!key) return res.status(400).json({ error: 'No Cloud Gateway Key set on this node.' });
|
|
28
29
|
fetchFn(key, function (err, data) {
|
|
29
30
|
if (err) return res.status(502).json({ error: err.message });
|
|
30
31
|
res.json(data);
|
|
@@ -14,7 +14,7 @@
|
|
|
14
14
|
},
|
|
15
15
|
inputs: 0,
|
|
16
16
|
outputs: 0,
|
|
17
|
-
icon: 'modpackqt.
|
|
17
|
+
icon: 'modpackqt.png',
|
|
18
18
|
label: function () {
|
|
19
19
|
return this.name || `slave probe :${this.bindPort} #${this.unitId}`;
|
|
20
20
|
},
|
|
@@ -97,12 +97,12 @@
|
|
|
97
97
|
</div>
|
|
98
98
|
|
|
99
99
|
<div class="form-row">
|
|
100
|
-
<label for="node-input-apiKey"><i class="fa fa-key"></i>
|
|
100
|
+
<label for="node-input-apiKey"><i class="fa fa-key"></i> Cloud Gateway Key</label>
|
|
101
101
|
<input type="password" id="node-input-apiKey" placeholder="Paste your ModPackQT tunnel key">
|
|
102
102
|
</div>
|
|
103
103
|
<div class="form-tips" style="font-size:11px;color:#6b7280;margin:-8px 0 12px 105px">
|
|
104
104
|
Generate at <a href="https://modpackqt.com/settings" target="_blank" rel="noopener">modpackqt.com → Settings → Cloud Gateways</a>.
|
|
105
|
-
|
|
105
|
+
Loads your saved slave configs in the picker below and lets the web console authenticate to this probe.
|
|
106
106
|
</div>
|
|
107
107
|
|
|
108
108
|
<div class="form-row">
|
|
@@ -138,11 +138,11 @@
|
|
|
138
138
|
</script>
|
|
139
139
|
|
|
140
140
|
<script type="text/html" data-help-name="modpackqt-slave-probe">
|
|
141
|
-
<p>Live simulator probe for a single Modbus TCP slave.
|
|
142
|
-
optionally pick a saved slave config from
|
|
141
|
+
<p>Live simulator probe for a single Modbus TCP slave. Paste your Cloud Gateway Key,
|
|
142
|
+
optionally pick a saved slave config from the <b>My Slaves</b> dropdown, then click
|
|
143
143
|
<b>Open in ModPackQT Console</b> to launch the web register editor.</p>
|
|
144
144
|
|
|
145
|
-
<h3>
|
|
145
|
+
<h3>Cloud Gateway Key</h3>
|
|
146
146
|
<p>Paste your ModPackQT tunnel key here — generate one at
|
|
147
147
|
<a href="https://modpackqt.com/settings" target="_blank" rel="noopener">modpackqt.com → Settings → Cloud Gateways</a>.
|
|
148
148
|
The key lets the web console authenticate to this probe and loads your saved slave configs
|
|
@@ -15,7 +15,7 @@
|
|
|
15
15
|
outputs: 1,
|
|
16
16
|
outputLabels: ['external client read/write events'],
|
|
17
17
|
align: 'right',
|
|
18
|
-
icon: 'modpackqt.
|
|
18
|
+
icon: 'modpackqt.png',
|
|
19
19
|
label: function () {
|
|
20
20
|
const tag = `:${this.bindPort || 1502} #${this.unitId || 1}`;
|
|
21
21
|
return this.name ? `${this.name} — Modbus TCP Slave ${tag}`
|
|
@@ -38,9 +38,14 @@
|
|
|
38
38
|
.done(function (rows) {
|
|
39
39
|
$sel.empty().append('<option value="">— Manual entry —</option>');
|
|
40
40
|
(rows || [])
|
|
41
|
-
.filter(function (s) { return s
|
|
41
|
+
.filter(function (s) { return !!s; })
|
|
42
42
|
.forEach(function (s) {
|
|
43
|
-
const
|
|
43
|
+
const isRtu = s.protocol === 'rtu';
|
|
44
|
+
const proto = isRtu ? ' [RTU]' : ' [TCP]';
|
|
45
|
+
const conn = isRtu
|
|
46
|
+
? ' — unit #' + (s.unitId || 1)
|
|
47
|
+
: ' — :' + (s.port || 1502) + ' #' + (s.unitId || 1);
|
|
48
|
+
const label = (s.name || '(unnamed)') + conn + proto;
|
|
44
49
|
$('<option>').val(s.id).text(label).data('row', s).appendTo($sel);
|
|
45
50
|
});
|
|
46
51
|
})
|
|
@@ -54,8 +59,9 @@
|
|
|
54
59
|
const row = $(this).find('option:selected').data('row');
|
|
55
60
|
if (!row) return;
|
|
56
61
|
if (!node.name) $('#node-input-name').val(row.name || '');
|
|
57
|
-
|
|
58
|
-
$('#node-input-
|
|
62
|
+
// RTU slaves have no port — keep whatever TCP port is already set
|
|
63
|
+
if (row.protocol !== 'rtu' && row.port) $('#node-input-bindPort').val(row.port);
|
|
64
|
+
$('#node-input-unitId').val(row.unitId || 1);
|
|
59
65
|
buildLink();
|
|
60
66
|
});
|
|
61
67
|
reload();
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "node-red-contrib-modbus-modpackqt",
|
|
3
|
-
"version": "3.3.
|
|
3
|
+
"version": "3.3.27",
|
|
4
4
|
"description": "Modbus commissioning, testing & analysis tools for Node-RED. Embedded Modbus TCP/RTU master + slave server, FC1/FC2/FC3/FC4 reads, FC5/FC6/FC15/FC16 writes, built-in slave register store, and a passive traffic monitor for debugging. 100% free, MIT, no usage limits. By ModPackQT — open the matching web console at modpackqt.com for register decoding, simulation and AI assistance.",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"node-red",
|
|
@@ -1,8 +0,0 @@
|
|
|
1
|
-
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 100 100">
|
|
2
|
-
<rect width="100" height="100" rx="22" fill="#16a34a"/>
|
|
3
|
-
<path d="M20 72 L34 30 L50 58 L66 30 L80 72"
|
|
4
|
-
fill="none" stroke="#ffffff" stroke-width="11"
|
|
5
|
-
stroke-linecap="round" stroke-linejoin="round"/>
|
|
6
|
-
<circle cx="20" cy="72" r="6" fill="#ffffff"/>
|
|
7
|
-
<circle cx="80" cy="72" r="6" fill="#ffffff"/>
|
|
8
|
-
</svg>
|