node-red-contrib-modbus-modpackqt 3.3.9 → 3.3.11

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,31 @@ 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.11] — 2026-05-10
8
+
9
+ ### Changed
10
+
11
+ - **Config dialog is now picker-only.** Hand-edited Target Device
12
+ (IP / Port / Unit ID), Transport (Mode / Serial / Baud / Parity /
13
+ Timeout) and the collapsible embedded-slave-server section have all
14
+ been removed from the runtime config dialog. Everything is set by
15
+ the **My Connections** picker, which auto-fills hidden fields the
16
+ runtime still uses. Same dialog appears regardless of whether you
17
+ open it from a master-read, master-write, or master-probe node.
18
+ To run an embedded Modbus TCP slave, drop a `modpackqt-slave-server`
19
+ node onto the canvas (it has its own My Slaves picker).
20
+
21
+ ## [3.3.10] — 2026-05-10
22
+
23
+ ### Removed
24
+
25
+ - **`modpackqt-traffic` node removed.** The matching modpackqt.com web
26
+ console already surfaces a live, decoded view of every Modbus op for
27
+ any device a master or slave node is attached to — the in-flow
28
+ passive sniffer was redundant. Existing flows referencing
29
+ `modpackqt-traffic` will show an "unknown node type" warning and can
30
+ be deleted.
31
+
7
32
  ## [3.3.9] — 2026-05-10
8
33
 
9
34
  ### Added
package/README.md CHANGED
@@ -15,7 +15,6 @@ By [ModPackQT](https://modpackqt.com).
15
15
 
16
16
  - **Modbus master** — read (FC1–FC4) and write (FC5/FC6/FC15/FC16) over **TCP** or **RTU (serial)**
17
17
  - **Embedded Modbus TCP slave server** — push values from any flow, let PLCs / SCADA / HMIs read them
18
- - **Passive traffic monitor** — see every Modbus op (timing, values, errors) in real time
19
18
  - **Cloud profile pickers** — paste your ModPackQT Account Key into the runtime config and a **My Connections** dropdown loads your saved devices, auto-filling host / port / unit. The slave server node has a matching **My Slaves** picker. No more retyping IPs across nodes.
20
19
  - **Outputs raw register values** — pair with [`node-red-contrib-bytes-modpackqt`](https://www.npmjs.com/package/node-red-contrib-bytes-modpackqt) to decode int / float / string / bitmask
21
20
  - **Zero external dependencies** — Modbus runs inside the Node-RED process
@@ -18,7 +18,7 @@
18
18
  const http = require('http');
19
19
  const { URL } = require('url');
20
20
 
21
- const PALETTE_VERSION = '3.3.9';
21
+ const PALETTE_VERSION = '3.3.11';
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;
@@ -28,20 +28,6 @@
28
28
  return this.name && this.name !== 'ModPackQT Device' ? `${this.name} (${t})` : `${t}${s}`;
29
29
  },
30
30
  oneditprepare: function () {
31
- const toggleRtu = () => {
32
- const isRtu = $('#node-config-input-masterMode').val() === 'rtu';
33
- $('.modpackqt-rtu-only').toggle(isRtu);
34
- $('.modpackqt-tcp-only').toggle(!isRtu);
35
- };
36
- const toggleSlave = () => {
37
- const on = $('#node-config-input-slaveEnabled').is(':checked');
38
- $('.modpackqt-slave-only').toggle(on);
39
- };
40
- $('#node-config-input-masterMode').on('change', toggleRtu);
41
- $('#node-config-input-slaveEnabled').on('change', toggleSlave);
42
- toggleRtu();
43
- toggleSlave();
44
-
45
31
  // ── My Connections picker
46
32
  const nodeId = this.id;
47
33
  const $sel = $('#node-config-input-modpackqt-conn-picker');
@@ -79,26 +65,9 @@
79
65
  if (c.host) $('#node-config-input-targetHost').val(c.host);
80
66
  if (c.port) $('#node-config-input-targetPort').val(c.port);
81
67
  if (c.unitId) $('#node-config-input-unitId').val(c.unitId);
82
- if (c.connectionType === 'rtu') {
83
- $('#node-config-input-masterMode').val('rtu').trigger('change');
84
- }
68
+ $('#node-config-input-masterMode').val(c.connectionType === 'rtu' ? 'rtu' : 'tcp');
85
69
  });
86
70
  loadConnections();
87
-
88
- // ── Embedded slave server section (collapsed by default)
89
- const $slaveSection = $('.modpackqt-slave-section');
90
- const $slaveToggle = $('.modpackqt-slave-section-toggle');
91
- const setSlaveOpen = (open) => {
92
- $slaveSection.toggle(open);
93
- $slaveToggle.html(open
94
- ? '▾ Hide embedded slave server'
95
- : '▸ Embedded slave server (only for modpackqt-slave-read / -write nodes)');
96
- };
97
- setSlaveOpen(this.slaveEnabled === true || this.slaveEnabled === 'true');
98
- $slaveToggle.off('click.modpackqt').on('click.modpackqt', (e) => {
99
- e.preventDefault();
100
- setSlaveOpen($slaveSection.is(':hidden'));
101
- });
102
71
  }
103
72
  });
104
73
  </script>
@@ -127,86 +96,27 @@
127
96
  Picks a saved connection from modpackqt.com — auto-fills Host, Port, and Unit ID below.
128
97
  </div>
129
98
 
130
- <h4 style="margin-top:18px">Target Device</h4>
131
- <div class="form-row modpackqt-tcp-only">
132
- <label for="node-config-input-targetHost"><i class="fa fa-plug"></i> IP Address</label>
133
- <input type="text" id="node-config-input-targetHost" placeholder="e.g. 192.168.1.10 — IP/hostname of the Modbus device">
134
- </div>
135
- <div class="form-tips modpackqt-tcp-only" style="font-size:11px;color:#6b7280;margin:-8px 0 12px 105px">
136
- The IP address (or hostname) of the target Modbus device on your network.
137
- </div>
138
- <div class="form-row modpackqt-tcp-only">
139
- <label for="node-config-input-targetPort"><i class="fa fa-hashtag"></i> Port</label>
140
- <input type="number" id="node-config-input-targetPort" min="1" max="65535" placeholder="502 (Modbus TCP default)">
141
- </div>
142
- <div class="form-row">
143
- <label for="node-config-input-unitId"><i class="fa fa-id-card"></i> Unit ID</label>
144
- <input type="number" id="node-config-input-unitId" min="1" max="247" placeholder="1 (slave address on the bus)">
145
- </div>
146
-
147
- <h4 style="margin-top:18px">Transport</h4>
148
- <div class="form-row">
149
- <label for="node-config-input-masterMode"><i class="fa fa-cogs"></i> Mode</label>
150
- <select id="node-config-input-masterMode">
151
- <option value="tcp">Modbus TCP</option>
152
- <option value="rtu">Modbus RTU (Serial)</option>
153
- </select>
154
- </div>
155
- <div class="form-row modpackqt-rtu-only">
156
- <label for="node-config-input-serialPort"><i class="fa fa-plug"></i> Serial Port</label>
157
- <input type="text" id="node-config-input-serialPort" placeholder="/dev/ttyUSB0 or COM3">
158
- </div>
159
- <div class="form-row modpackqt-rtu-only">
160
- <label for="node-config-input-baudRate"><i class="fa fa-tachometer"></i> Baud</label>
161
- <input type="number" id="node-config-input-baudRate" placeholder="9600">
162
- </div>
163
- <div class="form-row modpackqt-rtu-only">
164
- <label for="node-config-input-parity"><i class="fa fa-list"></i> Parity</label>
165
- <select id="node-config-input-parity">
166
- <option value="none">None</option>
167
- <option value="even">Even</option>
168
- <option value="odd">Odd</option>
169
- </select>
170
- </div>
171
- <div class="form-row modpackqt-rtu-only">
172
- <label for="node-config-input-dataBits"><i class="fa fa-hashtag"></i> Data Bits</label>
173
- <select id="node-config-input-dataBits">
174
- <option value="8">8</option><option value="7">7</option>
175
- </select>
176
- </div>
177
- <div class="form-row modpackqt-rtu-only">
178
- <label for="node-config-input-stopBits"><i class="fa fa-hashtag"></i> Stop Bits</label>
179
- <select id="node-config-input-stopBits">
180
- <option value="1">1</option><option value="2">2</option>
181
- </select>
182
- </div>
183
- <div class="form-row">
184
- <label for="node-config-input-timeoutMs"><i class="fa fa-clock-o"></i> Timeout (ms)</label>
185
- <input type="number" id="node-config-input-timeoutMs" placeholder="3000">
186
- </div>
187
-
188
- <div class="form-row" style="margin:14px 0 4px 0;border-top:1px solid #e5e7eb;padding-top:10px">
189
- <a href="#" class="modpackqt-slave-section-toggle" style="font-size:12px;color:#6b7280;text-decoration:none;cursor:pointer">▸ Embedded slave server (only for modpackqt-slave-read / -write nodes)</a>
190
- </div>
191
- <div class="modpackqt-slave-section" style="display:none">
192
- <div class="form-row">
193
- <label for="node-config-input-slaveEnabled" style="width:auto">
194
- <input type="checkbox" id="node-config-input-slaveEnabled" style="width:auto;vertical-align:middle">
195
- Enable embedded Modbus TCP slave server
196
- </label>
197
- </div>
198
- <div class="form-row modpackqt-slave-only">
199
- <label for="node-config-input-slaveHost"><i class="fa fa-globe"></i> Bind Host</label>
200
- <input type="text" id="node-config-input-slaveHost" placeholder="0.0.0.0 (all interfaces)">
201
- </div>
202
- <div class="form-row modpackqt-slave-only">
203
- <label for="node-config-input-slavePort"><i class="fa fa-hashtag"></i> Slave Port</label>
204
- <input type="number" id="node-config-input-slavePort" placeholder="1502">
205
- </div>
206
- </div>
99
+ <!-- All connection details (host/port/unit/mode/timeout/embedded-slave) are
100
+ set by the My Connections picker above. They live as hidden inputs so
101
+ the runtime still has them, but they are no longer hand-edited here. -->
102
+ <input type="hidden" id="node-config-input-targetHost">
103
+ <input type="hidden" id="node-config-input-targetPort">
104
+ <input type="hidden" id="node-config-input-unitId">
105
+ <input type="hidden" id="node-config-input-masterMode">
106
+ <input type="hidden" id="node-config-input-serialPort">
107
+ <input type="hidden" id="node-config-input-baudRate">
108
+ <input type="hidden" id="node-config-input-parity">
109
+ <input type="hidden" id="node-config-input-dataBits">
110
+ <input type="hidden" id="node-config-input-stopBits">
111
+ <input type="hidden" id="node-config-input-timeoutMs">
112
+ <input type="hidden" id="node-config-input-slaveEnabled">
113
+ <input type="hidden" id="node-config-input-slaveHost">
114
+ <input type="hidden" id="node-config-input-slavePort">
207
115
 
208
- <div class="form-tips">
209
- <b>One runtime = one device.</b> For multiple devices, create one runtime config per device — the read/write nodes then just pick which device to talk to.
116
+ <div class="form-tips" style="margin-top:14px">
117
+ <b>One device per config.</b> Use the picker above to choose which saved
118
+ Modbus device this config talks to. For more devices, create another
119
+ config and pick a different one.
210
120
  </div>
211
121
  <div class="form-row" style="margin-top:18px;padding-top:14px;border-top:1px solid #e5e7eb;text-align:center;">
212
122
  <a href="https://modpackqt.com" target="_blank" rel="noopener noreferrer"
@@ -233,16 +143,12 @@
233
143
  only need to know the function code, address, and quantity.
234
144
  </p>
235
145
 
236
- <h3>Master mode</h3>
237
- <ul>
238
- <li><b>Modbus TCP</b> — connects to the configured Host/Port.</li>
239
- <li><b>Modbus RTU</b> — opens the configured serial port. Host/Port are ignored; only Unit ID matters.</li>
240
- </ul>
241
-
242
- <h3>Embedded slave server</h3>
146
+ <h3>Where do the connection details come from?</h3>
243
147
  <p>
244
- When enabled, this config opens a Modbus TCP server on the configured port (default
245
- <code>1502</code>). Use the <code>modpackqt-slave-write</code> node to push values into
246
- its register store; external Modbus masters read whatever you last wrote.
148
+ Paste your <b>Account Key</b>, then pick a saved device from <b>My
149
+ Connections</b>. The host, port, unit ID, and transport mode are all
150
+ pulled from your modpackqt.com profile no hand-editing needed here.
151
+ To run an embedded Modbus TCP slave server, drop a
152
+ <code>modpackqt-slave-server</code> node onto the canvas instead.
247
153
  </p>
248
154
  </script>
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "node-red-contrib-modbus-modpackqt",
3
- "version": "3.3.9",
3
+ "version": "3.3.11",
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",
@@ -49,7 +49,6 @@
49
49
  "modpackqt-master-write": "nodes/modpackqt-master-write.js",
50
50
  "modpackqt-slave-read": "nodes/modpackqt-slave-read.js",
51
51
  "modpackqt-slave-write": "nodes/modpackqt-slave-write.js",
52
- "modpackqt-traffic": "nodes/modpackqt-traffic.js",
53
52
  "modpackqt-master-probe": "nodes/modpackqt-master-probe.js",
54
53
  "modpackqt-slave-server": "nodes/modpackqt-slave-server.js"
55
54
  }
@@ -1,127 +0,0 @@
1
- <script type="text/javascript">
2
- RED.nodes.registerType('modpackqt-traffic', {
3
- category: 'ModPackQT',
4
- color: '#22c55e',
5
- defaults: {
6
- name: { value: '' },
7
- server: { value: '', type: 'modpackqt-config', required: true },
8
- filterDirection: { value: 'any' },
9
- filterKind: { value: 'any' },
10
- filterFc: { value: 'any' },
11
- filterTarget: { value: '' },
12
- format: { value: 'object' },
13
- alsoLog: { value: false }
14
- },
15
- inputs: 0,
16
- outputs: 1,
17
- icon: 'font-awesome/fa-eye',
18
- label: function () {
19
- const parts = [];
20
- if (this.filterKind !== 'any') parts.push(this.filterKind);
21
- if (this.filterDirection !== 'any') parts.push(this.filterDirection);
22
- if (this.filterFc !== 'any') parts.push('FC' + this.filterFc);
23
- if (this.filterTarget) parts.push(this.filterTarget);
24
- return this.name || ('traffic' + (parts.length ? ' · ' + parts.join(' ') : ''));
25
- },
26
- paletteLabel: 'modbus traffic'
27
- });
28
- </script>
29
-
30
- <script type="text/html" data-template-name="modpackqt-traffic">
31
- <div class="form-row"><label for="node-input-name"><i class="fa fa-tag"></i> Name</label>
32
- <input type="text" id="node-input-name" placeholder="Name"></div>
33
- <div class="form-row"><label for="node-input-server"><i class="fa fa-cog"></i> Runtime</label>
34
- <input type="text" id="node-input-server"></div>
35
-
36
- <h4 style="margin-top:14px">Filters (optional)</h4>
37
- <div class="form-row"><label for="node-input-filterKind"><i class="fa fa-server"></i> Kind</label>
38
- <select id="node-input-filterKind">
39
- <option value="any">Any</option>
40
- <option value="master">Master only</option>
41
- <option value="slave">Slave only</option>
42
- </select>
43
- </div>
44
- <div class="form-row"><label for="node-input-filterDirection"><i class="fa fa-exchange"></i> Direction</label>
45
- <select id="node-input-filterDirection">
46
- <option value="any">Any</option>
47
- <option value="read">Read only</option>
48
- <option value="write">Write only</option>
49
- </select>
50
- </div>
51
- <div class="form-row"><label for="node-input-filterFc"><i class="fa fa-list"></i> Function Code</label>
52
- <select id="node-input-filterFc">
53
- <option value="any">Any</option>
54
- <option value="1">FC1 — Read Coils</option>
55
- <option value="2">FC2 — Read Discrete Inputs</option>
56
- <option value="3">FC3 — Read Holding</option>
57
- <option value="4">FC4 — Read Input</option>
58
- <option value="5">FC5 — Write Coil</option>
59
- <option value="6">FC6 — Write Register</option>
60
- <option value="15">FC15 — Write Coils</option>
61
- <option value="16">FC16 — Write Registers</option>
62
- </select>
63
- </div>
64
- <div class="form-row"><label for="node-input-filterTarget"><i class="fa fa-filter"></i> Target contains</label>
65
- <input type="text" id="node-input-filterTarget" placeholder="e.g. 192.168.1.10 or rtu:/dev/ttyUSB0"></div>
66
-
67
- <h4 style="margin-top:14px">Output</h4>
68
- <div class="form-row"><label for="node-input-format"><i class="fa fa-code"></i> Payload</label>
69
- <select id="node-input-format">
70
- <option value="object">Full event object</option>
71
- <option value="summary">One-line summary string</option>
72
- </select>
73
- </div>
74
- <div class="form-row"><label for="node-input-alsoLog" style="width:auto">
75
- <input type="checkbox" id="node-input-alsoLog" style="width:auto;vertical-align:middle">
76
- Also write to Node-RED log
77
- </label></div>
78
-
79
- <div class="form-tips">
80
- <b>Passive monitor.</b> No input wire — just listens to all Modbus ops on the runtime
81
- and emits one message each. Free.
82
- </div>
83
- <div class="form-row" style="margin-top:18px;padding-top:14px;border-top:1px solid #e5e7eb;text-align:center;">
84
- <a href="https://modpackqt.com" target="_blank" rel="noopener noreferrer"
85
- style="display:inline-block;padding:8px 20px;background:#2563eb;color:#fff;
86
- text-decoration:none;border-radius:6px;font-weight:600;font-size:13px;
87
- box-shadow:0 1px 2px rgba(0,0,0,0.08);">
88
- Premium Advanced Modbus Tester <i class="fa fa-external-link" style="margin-left:6px;"></i>
89
- </a>
90
- <div style="margin-top:6px;font-size:11px;color:#6b7280;">by ModPackQT &mdash; full-featured web tester, simulator &amp; AI helper</div>
91
- </div>
92
- </script>
93
-
94
- <script type="text/html" data-help-name="modpackqt-traffic">
95
- <p>
96
- Passive Modbus traffic monitor. Subscribes to all Modbus operations performed by any
97
- master or slave node sharing the same runtime config and emits one message per op.
98
- </p>
99
-
100
- <h3>Output payload (object format)</h3>
101
- <pre>{
102
- ts: "2026-05-09T14:23:01.234Z",
103
- direction: "read" | "write",
104
- kind: "master" | "slave",
105
- target: "192.168.1.10:502" | "local:1502" | "rtu:/dev/ttyUSB0",
106
- unitId: 1,
107
- fc: 3, // null for slave events
108
- registerType: "holding",// only set for slave events
109
- address: 100,
110
- quantity: 2,
111
- values: [16828, 0], // raw register values
112
- durationMs: 12,
113
- ok: true,
114
- error: null // populated on failure
115
- }</pre>
116
-
117
- <h3>Use cases</h3>
118
- <ul>
119
- <li><b>Debug:</b> wire to a Debug node — see exactly what every Modbus op did.</li>
120
- <li><b>Alerting:</b> filter <code>ok=false</code> → email/Slack on errors.</li>
121
- <li><b>Charting:</b> count ops/sec, response-time histograms, per-target latency.</li>
122
- <li><b>Audit trail:</b> store every op in a database for compliance.</li>
123
- </ul>
124
-
125
- <h3>Pricing</h3>
126
- <p><b>Free</b> — full visibility into every Modbus operation, no usage limits.</p>
127
- </script>
@@ -1,68 +0,0 @@
1
- /**
2
- * ModPackQT Traffic — passive Modbus traffic monitor.
3
- *
4
- * Subscribes to the config node's traffic event bus and emits one message
5
- * per Modbus operation (master read/write or slave read/write).
6
- *
7
- * Free for everyone — debug visibility costs nothing.
8
- */
9
- module.exports = function (RED) {
10
- function ModPackQTTrafficNode(config) {
11
- RED.nodes.createNode(this, config);
12
- const node = this;
13
- const server = RED.nodes.getNode(config.server);
14
-
15
- node.filterDirection = config.filterDirection || 'any'; // any | read | write
16
- node.filterKind = config.filterKind || 'any'; // any | master | slave
17
- node.filterFc = config.filterFc || 'any'; // any | 1 | 2 | 3 | 4 | 5 | 6 | 15 | 16
18
- node.filterTarget = (config.filterTarget || '').trim(); // substring match
19
- node.format = config.format || 'object'; // object | summary
20
- node.alsoLog = config.alsoLog === true || config.alsoLog === 'true';
21
-
22
- let count = 0;
23
-
24
- if (!server) {
25
- node.status({ fill: 'red', shape: 'ring', text: 'no config' });
26
- return;
27
- }
28
-
29
- function summarize(evt) {
30
- const tag = evt.kind === 'slave'
31
- ? `slave ${evt.direction} ${evt.registerType}@${evt.address}×${evt.quantity}`
32
- : `${evt.direction} FC${evt.fc} ${evt.target} u${evt.unitId} @${evt.address}×${evt.quantity}`;
33
- const status = evt.ok ? `${evt.durationMs}ms` : `ERR ${evt.error || ''}`;
34
- return `${tag} · ${status}`;
35
- }
36
-
37
- function onTraffic(evt) {
38
- // Filters
39
- if (node.filterDirection !== 'any' && evt.direction !== node.filterDirection) return;
40
- if (node.filterKind !== 'any' && evt.kind !== node.filterKind) return;
41
- if (node.filterFc !== 'any' && String(evt.fc) !== String(node.filterFc)) return;
42
- if (node.filterTarget && !(evt.target || '').includes(node.filterTarget)) return;
43
-
44
- count += 1;
45
- const summary = summarize(evt);
46
- node.status({
47
- fill: evt.ok ? 'green' : 'red', shape: 'dot',
48
- text: `modpackqt · ${count} ops · ${summary.slice(0, 50)}`
49
- });
50
-
51
- if (node.alsoLog) node.log(summary);
52
-
53
- const msg = (node.format === 'summary')
54
- ? { payload: summary, traffic: evt }
55
- : { payload: evt };
56
- msg.topic = `modpackqt/traffic/${evt.kind}/${evt.direction}`;
57
- node.send(msg);
58
- }
59
-
60
- server.traffic.on('op', onTraffic);
61
- node.status({ fill: 'grey', shape: 'ring', text: 'modpackqt · waiting…' });
62
-
63
- node.on('close', function () {
64
- try { server.traffic.removeListener('op', onTraffic); } catch (_) {}
65
- });
66
- }
67
- RED.nodes.registerType('modpackqt-traffic', ModPackQTTrafficNode);
68
- };