node-red-contrib-modbus-modpackqt 3.3.25 → 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 CHANGED
@@ -4,6 +4,44 @@ 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
+
7
45
  ## [3.3.25] — 2026-05-10
8
46
 
9
47
  ### Changed
@@ -18,7 +18,7 @@
18
18
  const http = require('http');
19
19
  const { URL } = require('url');
20
20
 
21
- const PALETTE_VERSION = '3.3.25';
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
- const probeId = req.query && req.query.probe;
23
- if (!probeId) return res.status(400).json({ error: 'probe query param required' });
24
- const probe = RED.nodes.getNode(probeId);
25
- if (!probe) return res.status(404).json({ error: 'Probe node not found (deploy first?)' });
26
- const key = probe.credentials && probe.credentials.apiKey;
27
- if (!key) return res.status(400).json({ error: 'No Account Key set on this probe node.' });
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);
@@ -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> Account Key</label>
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
- Used only by the modpackqt.com web console to sync with this probe.
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. Set your Account Key,
142
- optionally pick a saved slave config from modpackqt.com, then click
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>Account Key</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
@@ -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 && (s.protocol === 'tcp' || !s.protocol); })
41
+ .filter(function (s) { return !!s; })
42
42
  .forEach(function (s) {
43
- const label = (s.name || '(unnamed)') + ' — :' + (s.port || 1502) + ' #' + (s.unitId || 1);
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
- $('#node-input-bindPort').val(row.port || 1502);
58
- $('#node-input-unitId').val(row.unitId || 1);
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.25",
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",