@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 +24 -35
- package/nodes/ocpp-command.js +9 -0
- package/nodes/ocpp-in.html +15 -0
- package/nodes/ocpp-in.js +16 -0
- package/package.json +1 -1
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
|
-
**
|
|
107
|
-
|
|
108
|
-
|
|
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
|
```
|
package/nodes/ocpp-command.js
CHANGED
|
@@ -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,
|
package/nodes/ocpp-in.html
CHANGED
|
@@ -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
|