@blokcert/node-red-contrib-ocpp 1.2.0 → 1.2.1

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 CHANGED
@@ -89,7 +89,7 @@ msg.ocpp.errorDescription = "Action not supported";
89
89
 
90
90
  ### ocpp command
91
91
 
92
- Sends CSMS-initiated commands to charging stations.
92
+ Sends CSMS-initiated commands to charging stations and receives responses.
93
93
 
94
94
  **Supported Commands:**
95
95
  - Reset
@@ -103,9 +103,29 @@ Sends CSMS-initiated commands to charging stations.
103
103
  - ClearCache
104
104
  - And more...
105
105
 
106
- **Outputs:**
107
- - Output 1: Command sent confirmation
108
- - Output 2: Device response or timeout error (when "Wait for Response" is enabled)
106
+ **Output:**
107
+ Single output that emits when the device responds (or when a timeout occurs).
108
+
109
+ **Response Message:**
110
+ ```javascript
111
+ {
112
+ payload: { /* device response payload */ },
113
+ ocpp: {
114
+ messageId: "abc123",
115
+ action: "GetConfiguration", // Original command
116
+ tenant: "operator1",
117
+ deviceId: "charger001",
118
+ identity: "operator1:charger001",
119
+ response: true,
120
+ error: false, // true for CALLERROR or timeout
121
+ errorCode: null, // Set for errors
122
+ errorDescription: null, // Set for errors
123
+ sentAt: 1699999999999,
124
+ receivedAt: 1699999999150,
125
+ latency: 150 // Round-trip time in ms
126
+ }
127
+ }
128
+ ```
109
129
 
110
130
  **Example:**
111
131
  ```javascript
@@ -120,37 +140,6 @@ msg.payload = {
120
140
  return msg;
121
141
  ```
122
142
 
123
- ### ocpp response
124
-
125
- Routes CALLRESULT/CALLERROR responses back to the originating ocpp-command nodes.
126
-
127
- **Features:**
128
- - Automatically matches responses to pending commands
129
- - Notifies ocpp-command nodes of responses
130
- - Calculates round-trip latency
131
-
132
- **Setup:**
133
- Connect this node to an `ocpp in` node (optionally filtered by `_Response`) to receive device responses:
134
-
135
- ```
136
- [ocpp in (filter: _Response)] → [ocpp response] → [debug]
137
- ```
138
-
139
- **Output:**
140
- ```javascript
141
- {
142
- payload: { /* device response */ },
143
- ocpp: {
144
- messageId: "abc123",
145
- action: "GetConfiguration", // Original command
146
- response: true,
147
- error: false,
148
- latency: 150, // Round-trip time in ms
149
- handled: true // Whether an ocpp-command node was notified
150
- }
151
- }
152
- ```
153
-
154
143
  ## Example Flow
155
144
 
156
145
  ```
@@ -59,8 +59,13 @@ module.exports = function (RED) {
59
59
  await subRedis.subscribe(respChannel);
60
60
  node.log(`Subscribed to ${respChannel}`);
61
61
 
62
+ subRedis.on('subscribe', (channel, count) => {
63
+ node.log(`Subscribe confirmed: ${channel} (total: ${count})`);
64
+ });
65
+
62
66
  // Handle incoming messages
63
67
  subRedis.on('message', (channel, message) => {
68
+ node.log(`Pub/Sub message received on ${channel}`);
64
69
  try {
65
70
  const data = JSON.parse(message);
66
71
  const messageId = data.messageId || data.uniqueID;
@@ -70,8 +75,10 @@ module.exports = function (RED) {
70
75
  return;
71
76
  }
72
77
 
78
+ node.log(`Looking up pending for messageId: ${messageId}`);
73
79
  const pending = pendingCommands.get(messageId);
74
80
  if (!pending) {
81
+ node.log(`No pending command found for ${messageId}, pendingCommands size: ${pendingCommands.size}`);
75
82
  // Not our command, ignore
76
83
  return;
77
84
  }
@@ -94,6 +101,8 @@ module.exports = function (RED) {
94
101
  text: `${pending.action}: ${isError ? 'error' : 'ok'}`,
95
102
  });
96
103
 
104
+ node.log(`Found pending command for ${messageId}, sending to output`);
105
+
97
106
  // Build output message with device response
98
107
  const responseMsg = {
99
108
  payload: data.payload || data,
@@ -10,6 +10,7 @@
10
10
  autoAck: { value: true },
11
11
  normalizePayload: { value: true },
12
12
  filterAction: { value: '' },
13
+ includeResponses: { value: false },
13
14
  },
14
15
  inputs: 0,
15
16
  outputs: 1,
@@ -58,6 +59,11 @@
58
59
  <label for="node-input-filterAction"><i class="fa fa-filter"></i> Filter Action</label>
59
60
  <input type="text" id="node-input-filterAction" placeholder="(all actions)">
60
61
  </div>
62
+ <div class="form-row">
63
+ <label for="node-input-includeResponses"><i class="fa fa-reply"></i> Include Responses</label>
64
+ <input type="checkbox" id="node-input-includeResponses" style="width: auto; margin-left: 0;">
65
+ <span style="margin-left: 10px;">Include CallResult/CallError (normally handled by ocpp-command)</span>
66
+ </div>
61
67
  </script>
62
68
 
63
69
  <script type="text/html" data-help-name="ocpp-in">
@@ -108,12 +114,21 @@
108
114
  <dt>Filter Action <span class="property-type">string</span></dt>
109
115
  <dd>Optional. Only output messages matching this filter.
110
116
  Leave empty to receive all messages.</dd>
117
+
118
+ <dt>Include Responses <span class="property-type">boolean</span></dt>
119
+ <dd>If enabled, includes CallResult (3) and CallError (4) messages.
120
+ By default, these are skipped because <code>ocpp-command</code> nodes handle responses via Pub/Sub.
121
+ Enable this only if you need to monitor or log all response traffic.</dd>
111
122
  </dl>
112
123
 
113
124
  <h3>Details</h3>
114
125
  <p>This node reads from the <code>ws:inbound</code> Redis Stream using consumer groups,
115
126
  allowing multiple Node-RED instances to share the message load.</p>
116
127
 
128
+ <p><strong>Note:</strong> By default, this node only outputs CALL messages (requests from charging stations).
129
+ Response messages (CallResult/CallError) are handled by <code>ocpp-command</code> nodes via Redis Pub/Sub.
130
+ Enable "Include Responses" only if you need to monitor or log all traffic.</p>
131
+
117
132
  <h3>Filter Options</h3>
118
133
  <p>The Filter Action field supports these values:</p>
119
134
  <ul>
package/nodes/ocpp-in.js CHANGED
@@ -24,6 +24,7 @@ module.exports = function (RED) {
24
24
  node.autoAck = config.autoAck !== false; // Default true
25
25
  node.normalizePayload = config.normalizePayload !== false; // Default true
26
26
  node.filterAction = config.filterAction || ''; // Optional action filter
27
+ node.includeResponses = config.includeResponses === true; // Default false - skip CallResult/CallError
27
28
 
28
29
  // State
29
30
  let running = false;
@@ -111,6 +112,21 @@ module.exports = function (RED) {
111
112
  let msg = parseStreamMessage(data);
112
113
  msg.streamId = streamId;
113
114
 
115
+ // Skip CallResult (3) and CallError (4) messages by default
116
+ // These responses should be handled by ocpp-command nodes via Pub/Sub
117
+ // Only allow them through if explicitly configured with includeResponses option
118
+ // Check both: ocppMessageType (numeric: 3, 4) and messageType (string: 'result', 'error')
119
+ const isResponse = msg.ocppMessageType === 3 || msg.ocppMessageType === 4 ||
120
+ msg.messageType === 'result' || msg.messageType === 'error';
121
+
122
+ if (isResponse && !node.includeResponses) {
123
+ // Auto-ack response messages silently
124
+ if (node.autoAck) {
125
+ await redis.xack(streamKey, consumerGroup, streamId);
126
+ }
127
+ return;
128
+ }
129
+
114
130
  // Apply action filter if configured
115
131
  // Special filter values:
116
132
  // - "_CallResult" - only CALLRESULT responses
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@blokcert/node-red-contrib-ocpp",
3
- "version": "1.2.0",
3
+ "version": "1.2.1",
4
4
  "description": "Node-RED nodes for OCPP (Open Charge Point Protocol) message handling via Redis Streams",
5
5
  "keywords": [
6
6
  "node-red",