ocpp-ws-io 2.1.15 → 2.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
@@ -1,25 +1,61 @@
1
1
  <p align="center">
2
- <img src="https://raw.githubusercontent.com/rohittiwari-dev/ocpp-ws-io/main/assets/banner.svg" alt="ocpp-ws-io" width="420" />
2
+ <img src="https://raw.githubusercontent.com/rohittiwari-dev/ocpp-ws-io/main/assets/banner.svg" alt="ocpp-ws-io - OCPP RPC WebSocket Client and Server" width="420" />
3
3
  </p>
4
4
 
5
- > built with TypeScript — supports OCPP 1.6, 2.0.1, and 2.1 with optional JSON schema validation, all security profiles, clustering support, and structured logging powered by [voltlog-io](https://ocpp-ws-io.rohittiwari.me/docs/voltlog-io).
5
+ # 🔌 OCPP RPC WebSocket Client & Server Library
6
+
7
+ The **production-ready OCPP RPC implementation** for building type-safe EV charging platforms, CSMS backends, charge points, and simulators. Supports OCPP 1.6J, 2.0.1, and 2.1 with full protocol compliance, all security profiles, Redis clustering, strict JSON schema validation, and structured logging.
8
+
9
+ > **Built with TypeScript** for EV charging, CSMS, CPO/EMSP platforms. Zero-dependency WebSocket RPC framework supporting OCPP versions 1.6, 2.0.1, and 2.1 with optional JSON schema validation, bidirectional messaging, all security profiles (0–3), Redis pub/sub clustering, and structured logging powered by [voltlog-io](https://ocpp-ws-io.rohittiwari.me/docs/voltlog-io).
6
10
 
7
11
  [![npm version](https://img.shields.io/npm/v/ocpp-ws-io.svg)](https://www.npmjs.com/package/ocpp-ws-io)
8
12
  [![License](https://img.shields.io/npm/l/ocpp-ws-io.svg)](https://github.com/rohittiwari-dev/ocpp-ws-io/blob/main/LICENSE)
9
13
 
10
- ## 📚 Documentation
14
+ ## 📚 Full Documentation & API Reference
11
15
 
12
16
  For full API reference, advanced usage, and guides, visit the **[Official Documentation](https://ocpp-ws-io.rohittiwari.me)**.
13
17
 
14
- ## ✨ Features
18
+ ## ✨ Key Features for OCPP RPC WebSocket Development
19
+
20
+ The library provides enterprise-grade features for building OCPP-compliant EV charging platforms:
21
+
22
+ - ⚡ **Full OCPP-J RPC Support** — Complete JSON-RPC 2.0 OCPP message framing for 1.6J, 2.0.1, and 2.1
23
+ - 🔒 **All Security Profiles** — Supports Plain WS, Basic Authentication, TLS/SSL, and mutual TLS (mTLS)
24
+ - 🎯 **Type-Safe TypeScript** — Auto-generated, fully-typed interfaces for all OCPP versions and methods
25
+ - 📐 **Strict Schema Validation** — Optional JSON schema validation with AJV for data integrity
26
+ - 📡 **Redis Clustering & Pub/Sub** — Built-in Redis adapter for distributed CSMS deployments
27
+ - 🌐 **Browser-Ready Client** — Zero-dependency WebSocket client for EV charging simulators
28
+ - ⚡ **CLI Toolkit** — `ocpp-ws-cli` for type generation, load testing, fuzzing, and virtual charge point simulation
29
+ - 🛡️ **DDoS & Rate Limiting** — Token bucket rate limiting and adaptive throttling for charging station protection
30
+ - 🧩 **19 Built-in Plugins** — PII redaction, circuit breakers, deduplication, Kafka streaming, OpenTelemetry, and more
31
+
32
+ ## 🧩 Plugin Ecosystem
33
+
34
+ 19 built-in plugins organized into a **4-level power hierarchy**. Register from highest to lowest:
35
+
36
+ ```
37
+ Level 4 — Middleware pii-redactor · schema-versioning
38
+ Level 3 — Interceptor message-dedup · replay-buffer
39
+ Level 2 — Lifecycle Controller connection-guard · anomaly · circuit-breaker
40
+ Level 1 — Passive Hook kafka · webhook · metrics · otel · session-log · heartbeat · rate-limit-notifier
41
+ ```
15
42
 
16
- - ⚡ **Full OCPP-J RPC** — Compliant message framing
17
- - 🔒 **Security Profiles 0–3** — Plain WS, Basic Auth, TLS, mTLS
18
- - 🎯 **Type-Safe** — Auto-generated types for all OCPP messages
19
- - 📐 **Strict Mode** — Optional JSON schema validation
20
- - 📡 **Clustering** — Redis adapter support
21
- - 🌐 **Browser Client** — Zero-dependency client for simulators
22
- - ⚡ **CLI Ecosystem** — Built-in `ocpp-ws-cli` for generating types, load testing, fuzzing, and simulating virtual charge points
43
+ ```typescript
44
+ import {
45
+ piiRedactorPlugin, messageDedupPlugin,
46
+ circuitBreakerPlugin, metricsPlugin, otelPlugin,
47
+ } from "ocpp-ws-io/plugins";
48
+
49
+ server.plugin(
50
+ piiRedactorPlugin(), // L4: transform payloads
51
+ messageDedupPlugin({ redis }), // L3: drop duplicates
52
+ circuitBreakerPlugin(), // L2: protect against flapping stations
53
+ metricsPlugin(), // L1: observe
54
+ otelPlugin(), // L1: trace
55
+ );
56
+ ```
57
+
58
+ > See the full [Plugin Documentation](https://ocpp-ws-io.rohittiwari.me/docs/ocpp-ws-io/plugins) for options, examples, and the `redisStyle` guide for Redis client compatibility.
23
59
 
24
60
  ## 📦 Installation
25
61
 
@@ -27,68 +63,72 @@ For full API reference, advanced usage, and guides, visit the **[Official Docume
27
63
  npm install ocpp-ws-io
28
64
  ```
29
65
 
30
- ## 🚀 Quick Start
66
+ ## 🚀 Quick Start: OCPP RPC Client & Server Examples
67
+
68
+ Get up and running with OCPP WebSocket RPC in minutes. Choose your role: EV charging station (client) or CSMS backend (server).
31
69
 
32
- ### Client (Charging Station Simulator)
70
+ ### OCPP RPC Client: Charge Point / Charging Station Simulator
33
71
 
34
72
  ```typescript
35
73
  import { OCPPClient } from "ocpp-ws-io";
36
74
 
37
75
  const client = new OCPPClient({
38
- endpoint: "ws://localhost:3000",
39
- identity: "CP001",
40
- protocols: ["ocpp1.6"],
76
+ endpoint: "ws://localhost:3000",
77
+ identity: "CP001",
78
+ protocols: ["ocpp1.6"],
41
79
  });
42
80
 
43
81
  await client.connect();
44
82
 
45
83
  // Fully typed call
46
84
  const response = await client.call("ocpp1.6", "BootNotification", {
47
- chargePointVendor: "VendorX",
48
- chargePointModel: "ModelY",
85
+ chargePointVendor: "VendorX",
86
+ chargePointModel: "ModelY",
49
87
  });
50
88
 
51
89
  console.log("Status:", response.status);
52
90
  ```
53
91
 
54
- ### Server (Central System)
92
+ ### OCPP RPC Server: Central System / CSMS Backend
55
93
 
56
94
  ```typescript
57
95
  import { OCPPServer } from "ocpp-ws-io";
58
96
 
59
97
  const server = new OCPPServer({
60
- protocols: ["ocpp1.6", "ocpp2.0.1"],
61
- logging: { prettify: true, exchangeLog: true, level: "info" },
98
+ protocols: ["ocpp1.6", "ocpp2.0.1"],
99
+ logging: { prettify: true, exchangeLog: true, level: "info" },
62
100
  });
63
101
 
64
102
  // Optional: Add authentication ringfence
65
103
  server.auth((ctx) => {
66
- console.log(
67
- `Connection from ${ctx.handshake.identity} at path ${ctx.handshake.pathname}`,
68
- );
69
- ctx.accept({ session: { authorized: true } });
104
+ console.log(
105
+ `Connection from ${ctx.handshake.identity} at path ${ctx.handshake.pathname}`,
106
+ );
107
+ ctx.accept({ session: { authorized: true } });
70
108
  });
71
109
 
72
110
  server.on("client", (client) => {
73
- console.log(`${client.identity} connected`);
74
-
75
- // Version-aware handler
76
- client.handle("ocpp1.6", "BootNotification", ({ params }) => {
77
- console.log("Boot from:", params.chargePointVendor);
78
- return {
79
- status: "Accepted",
80
- currentTime: new Date().toISOString(),
81
- interval: 300,
82
- };
83
- });
111
+ console.log(`${client.identity} connected`);
112
+
113
+ // Version-aware handler
114
+ client.handle("ocpp1.6", "BootNotification", ({ params }) => {
115
+ console.log("Boot from:", params.chargePointVendor);
116
+ return {
117
+ status: "Accepted",
118
+ currentTime: new Date().toISOString(),
119
+ interval: 300,
120
+ };
121
+ });
84
122
  });
85
123
 
86
124
  await server.listen(3000);
87
125
  ```
88
126
 
89
- ## ⚙️ Configuration
127
+ ## ⚙️ OCPP RPC Configuration & Options
128
+
129
+ Configure your OCPP WebSocket RPC client and server with extensive options for protocol negotiation, security, validation, and performance.
90
130
 
91
- ### `OCPPClient` Options
131
+ ### OCPP WebSocket RPC Client Configuration
92
132
 
93
133
  | Option | Type | Default | Description |
94
134
  | ------------------- | --------------------- | ---------- | --------------------------------------- |
@@ -110,7 +150,7 @@ When invoking `client.call()` you can safely decouple dynamically generated mess
110
150
  await client.call("ocpp1.6", "BootNotification", { ... }, { idempotencyKey: "unique-boot-123" });
111
151
  ```
112
152
 
113
- ### `OCPPServer` Options
153
+ ### OCPP WebSocket RPC Server Configuration
114
154
 
115
155
  | Option | Type | Default | Description |
116
156
  | -------------------- | ------------------ | --------- | ------------------------------------------ |
@@ -123,28 +163,86 @@ await client.call("ocpp1.6", "BootNotification", { ... }, { idempotencyKey: "uni
123
163
  | `rateLimit` | `RateLimitOptions` | — | Token bucket socket & method rate-limiter |
124
164
  | `healthEndpoint` | `boolean` | `false` | Expose HTTP `/health` and `/metrics` APIs |
125
165
 
126
- ## 🛠️ Advanced Server Configuration
166
+ ## Message Observability & Event Handling (v3.0.0+)
167
+
168
+ Unified message event API with direction tracking and rich contextual metadata for complete visibility into OCPP message flow.
169
+
170
+ ### Observe All Messages with Direction & Latency
171
+
172
+ ```typescript
173
+ const client = new OCPPClient({ ... });
174
+
175
+ // Single unified "message" event with direction, context, and metadata
176
+ client.on("message", ({ message, direction, ctx }) => {
177
+ console.log({
178
+ direction, // "IN" (from server) | "OUT" (to server)
179
+ method: ctx.method, // "BootNotification", "MeterValues", etc.
180
+ type: ctx.type, // "incoming_call", "outgoing_call", etc.
181
+ messageId: ctx.messageId, // Unique message ID
182
+ timestamp: ctx.timestamp, // ISO 8601 timestamp
183
+ latencyMs: ctx.latencyMs, // Response latency (if available)
184
+ protocol: ctx.protocol, // "ocpp1.6", "ocpp2.0.1", etc.
185
+ });
186
+ });
187
+
188
+ // Server-side observation (for each connected client)
189
+ const server = new OCPPServer({ ... });
190
+ server.on("client", (client) => {
191
+ client.on("message", ({ direction, ctx }) => {
192
+ console.log(`[${client.identity}] ${direction} ${ctx.method} (${ctx.latencyMs}ms)`);
193
+ });
194
+ });
195
+ ```
196
+
197
+ ### Message Event Payload Structure
198
+
199
+ ```typescript
200
+ interface MessageEventPayload {
201
+ message: OCPPMessage; // Original OCPP message tuple
202
+ direction: "IN" | "OUT"; // Message direction
203
+ ctx: MessageEventContext; // Enriched context with metadata
204
+ }
205
+
206
+ interface MessageEventContext {
207
+ type:
208
+ | "incoming_call"
209
+ | "outgoing_call"
210
+ | "incoming_result"
211
+ | "incoming_error";
212
+ messageId: string;
213
+ method?: string;
214
+ params?: unknown;
215
+ payload?: unknown;
216
+ timestamp: string; // ISO 8601
217
+ latencyMs?: number; // Response latency
218
+ protocol?: string;
219
+ }
220
+ ```
221
+
222
+ ## �🛠️ Advanced OCPP RPC Server Configuration & WebSocket Handshake
223
+
224
+ Build sophisticated OCPP server implementations with fine-tuned WebSocket upgrade handling, authentication, and message routing.
127
225
 
128
- ### Handshake & Upgrades
226
+ ### OCPP WebSocket Handshake, Upgrade & Authentication
129
227
 
130
228
  You can fine-tune how the server handles the WebSocket upgrade process, including timeouts for custom auth logic.
131
229
 
132
230
  ```typescript
133
231
  const server = new OCPPServer({
134
- // ...
135
- handshakeTimeoutMs: 5000, // Timeout if auth callback takes too long (default 30s)
232
+ // ...
233
+ handshakeTimeoutMs: 5000, // Timeout if auth callback takes too long (default 30s)
136
234
  });
137
235
 
138
236
  server.on("upgradeAborted", ({ identity, reason, socket }) => {
139
- console.warn(`Handshake aborted for ${identity}: ${reason}`);
237
+ console.warn(`Handshake aborted for ${identity}: ${reason}`);
140
238
  });
141
239
 
142
240
  server.on("upgradeError", ({ error, socket }) => {
143
- console.error("Upgrade failed:", error);
241
+ console.error("Upgrade failed:", error);
144
242
  });
145
243
  ```
146
244
 
147
- ### Server & Router Execution Flow
245
+ ### OCPP Server & Router Execution Flow (Connection & Message Phases)
148
246
 
149
247
  The `OCPPServer` and its internal `OCPPRouter` handle connections and messages in a strict, two-phase execution hierarchy:
150
248
 
@@ -160,7 +258,7 @@ Executes before the WebSocket connection is officially accepted.
160
258
 
161
259
  Executes after the connection is accepted and messages start flowing. 4. **Message Middleware (`client.use` / `server.use`)**: Intercepts every outgoing/incoming message for logging, schema validation, or metric tracking. 5. **Message Handlers (`client.handle` / `server.handle`)**: The **final piece of business logic** where the system reacts to a specific OCPP action (e.g., `BootNotification`).
162
260
 
163
- ### NOREPLY Suppression
261
+ ### NOREPLY Suppression in OCPP RPC Message Handlers
164
262
 
165
263
  Return `NOREPLY` directly from any message handler to safely suppress the automatic outbound `CALLRESULT` without violating strict internal tracking specifications.
166
264
 
@@ -168,53 +266,53 @@ Return `NOREPLY` directly from any message handler to safely suppress the automa
168
266
  import { NOREPLY } from "ocpp-ws-io";
169
267
 
170
268
  client.handle("StatusNotification", ({ params }) => {
171
- return NOREPLY;
269
+ return NOREPLY;
172
270
  });
173
271
  ```
174
272
 
175
- ## 📝 Logging
273
+ ## 📝 Structured Logging for OCPP RPC WebSocket Applications
176
274
 
177
- `ocpp-ws-io` includes **built-in structured JSON logging** powered by [voltlog-io](https://ocpp-ws-io.rohittiwari.me/docs/voltlog-io), designed for high-throughput WebSocket environments.
275
+ `ocpp-ws-io` provides **battle-tested structured JSON logging** optimized for high-throughput EV charging and OCPP WebSocket environments, powered by [voltlog-io](https://ocpp-ws-io.rohittiwari.me/docs/voltlog-io).
178
276
 
179
- ### Default Behavior
277
+ ### Default JSON Logging Behavior
180
278
 
181
279
  By default (`logging: true`), logs are output as structured JSON to `stdout`.
182
280
 
183
281
  ```json
184
282
  {
185
- "level": 30,
186
- "time": 1678900000000,
187
- "msg": "Client connected",
188
- "component": "OCPPServer",
189
- "identity": "CP001"
283
+ "level": 30,
284
+ "time": 1678900000000,
285
+ "msg": "Client connected",
286
+ "component": "OCPPServer",
287
+ "identity": "CP001"
190
288
  }
191
289
  ```
192
290
 
193
- ### Pretty Printing & Exchange Logs
291
+ ### Pretty Printing & OCPP Message Exchange Logs (Development Mode)
194
292
 
195
293
  Enable `prettify` for development to see colored output with icons.
196
294
  Enable `exchangeLog` to log all OCPP messages with direction (`IN`/`OUT`) and metadata.
197
295
 
198
296
  ```typescript
199
297
  const client = new OCPPClient({
200
- // ...
201
- logging: {
202
- enabled: true,
203
- prettify: true, // 🌈 Colors & icons
204
- exchangeLog: true, // ⚡ Log all OCPP messages
205
- level: "debug", // Default: 'info'
206
- },
298
+ // ...
299
+ logging: {
300
+ enabled: true,
301
+ prettify: true, // 🌈 Colors & icons
302
+ exchangeLog: true, // ⚡ Log all OCPP messages
303
+ level: "debug", // Default: 'info'
304
+ },
207
305
  });
208
306
  ```
209
307
 
210
308
  **Output:**
211
309
 
212
- ```
310
+ ```sh
213
311
  ⚡ CP-101 → BootNotification [OUT]
214
312
  ✅ CP-101 ← BootNotification [IN] { latencyMs: 45 }
215
313
  ```
216
314
 
217
- ### Custom Logger
315
+ ### Custom Logger Integration (Pino, Winston, etc.)
218
316
 
219
317
  You can bring your own logger (Pino, Winston, etc.) by implementing `LoggerLike`:
220
318
 
@@ -222,31 +320,33 @@ You can bring your own logger (Pino, Winston, etc.) by implementing `LoggerLike`
222
320
  import pino from "pino";
223
321
 
224
322
  const client = new OCPPClient({
225
- logging: {
226
- handler: pino(), // Use existing logger instance
227
- },
323
+ logging: {
324
+ handler: pino(), // Use existing logger instance
325
+ },
228
326
  });
229
327
  ```
230
328
 
231
- ## 🛡️ Safety & Reliability
329
+ ## 🛡️ OCPP RPC Safety, Reliability & Error Handling
330
+
331
+ Build fault-tolerant EV charging applications with safe call patterns, automatic error handling, and idempotency support.
232
332
 
233
- ### Safe Calls (`safeCall`)
333
+ ### Safe RPC Calls (`safeCall`) - Error Handling Without Try/Catch
234
334
 
235
335
  Perform RPC calls without `try/catch` blocks. Returns the response data on success, or `undefined` on failure while automatically logging the error. You can also pass per-call config options like timeouts.
236
336
 
237
337
  ```typescript
238
338
  const result = await client.safeCall(
239
- "ocpp1.6",
240
- "Heartbeat",
241
- {},
242
- {
243
- timeoutMs: 15000, // Finely control the timeout specifically for this request
244
- },
339
+ "ocpp1.6",
340
+ "Heartbeat",
341
+ {},
342
+ {
343
+ timeoutMs: 15000, // Finely control the timeout specifically for this request
344
+ },
245
345
  );
246
346
 
247
347
  if (result) {
248
- // Checked for undefined
249
- console.log("Heartbeat accepted:", result.currentTime);
348
+ // Checked for undefined
349
+ console.log("Heartbeat accepted:", result.currentTime);
250
350
  }
251
351
  ```
252
352
 
@@ -262,16 +362,16 @@ Throws an error if the client responds with a `CALLERROR` or if the timeout is r
262
362
 
263
363
  ```typescript
264
364
  try {
265
- const result = await server.sendToClient(
266
- "CP001",
267
- "ocpp1.6",
268
- "GetConfiguration",
269
- { key: ["ClockAlignedDataInterval"] },
270
- { timeoutMs: 10000 },
271
- );
272
- console.log("Configuration:", result);
365
+ const result = await server.sendToClient(
366
+ "CP001",
367
+ "ocpp1.6",
368
+ "GetConfiguration",
369
+ { key: ["ClockAlignedDataInterval"] },
370
+ { timeoutMs: 10000 },
371
+ );
372
+ console.log("Configuration:", result);
273
373
  } catch (error) {
274
- console.error("Failed to get configuration:", error);
374
+ console.error("Failed to get configuration:", error);
275
375
  }
276
376
  ```
277
377
 
@@ -281,15 +381,15 @@ Returns the response on success, or `undefined` on error, automatically logging
281
381
 
282
382
  ```typescript
283
383
  const result = await server.safeSendToClient(
284
- "CP001",
285
- "ocpp1.6",
286
- "GetConfiguration",
287
- { key: ["ClockAlignedDataInterval"] },
288
- { timeoutMs: 10000 },
384
+ "CP001",
385
+ "ocpp1.6",
386
+ "GetConfiguration",
387
+ { key: ["ClockAlignedDataInterval"] },
388
+ { timeoutMs: 10000 },
289
389
  );
290
390
 
291
391
  if (result) {
292
- console.log("Configuration:", result);
392
+ console.log("Configuration:", result);
293
393
  }
294
394
  ```
295
395
 
@@ -299,15 +399,15 @@ For intercepting HTTP WebSocket Upgrade requests before they become an OCPP Clie
299
399
 
300
400
  ```typescript
301
401
  const rateLimiter = defineMiddleware(async (ctx) => {
302
- const ip = ctx.handshake.remoteAddress;
303
- if (isRateLimited(ip)) {
304
- // Instantly aborts the WebSocket connection with an HTTP 429 status
305
- ctx.reject(429, "Too Many Requests");
306
- } else {
307
- // Or proceed down the execution chain. You can optionally pass an object
308
- // to next(), which will automatically be shallow-merged into `ctx.state`.
309
- await ctx.next({ rateLimitRemaining: 99 });
310
- }
402
+ const ip = ctx.handshake.remoteAddress;
403
+ if (isRateLimited(ip)) {
404
+ // Instantly aborts the WebSocket connection with an HTTP 429 status
405
+ ctx.reject(429, "Too Many Requests");
406
+ } else {
407
+ // Or proceed down the execution chain. You can optionally pass an object
408
+ // to next(), which will automatically be shallow-merged into `ctx.state`.
409
+ await ctx.next({ rateLimitRemaining: 99 });
410
+ }
311
411
  });
312
412
 
313
413
  server.use(rateLimiter);
@@ -321,101 +421,102 @@ The Rate Limiter safely throttles connections without dropping the connection im
321
421
 
322
422
  ```typescript
323
423
  const server = new OCPPServer({
324
- protocols: ["ocpp1.6"],
325
- rateLimit: {
326
- limit: 100, // Global limit
327
- windowMs: 60000, // per 60 seconds
328
- onLimitExceeded: "disconnect", // or "ignore", or a Custom Callback
329
- methods: {
330
- MeterValues: { limit: 10, windowMs: 60000 },
331
- Heartbeat: { limit: 2, windowMs: 60000 },
332
- },
424
+ protocols: ["ocpp1.6"],
425
+ rateLimit: {
426
+ limit: 100, // Global limit
427
+ windowMs: 60000, // per 60 seconds
428
+ onLimitExceeded: "disconnect", // or "ignore", or a Custom Callback
429
+ methods: {
430
+ MeterValues: { limit: 10, windowMs: 60000 },
431
+ Heartbeat: { limit: 2, windowMs: 60000 },
333
432
  },
433
+ },
334
434
  });
335
435
  ```
336
436
 
337
- ## 🧩 Middleware
437
+ ## 🧩 OCPP Message Middleware & Interceptor Pattern
338
438
 
339
- Intercept and modify OCPP messages using the middleware stack.
439
+ Intercept, validate, and modify OCPP RPC messages across the middleware stack for logging, schema validation, and metric tracking.
340
440
 
341
441
  ```typescript
342
442
  // Add logging middleware (enabled by default)
343
443
  client.use(async (ctx, next) => {
344
- console.log(`Processing ${ctx.method}`);
345
- await next();
444
+ console.log(`Processing ${ctx.method}`);
445
+ await next();
346
446
  });
347
447
  ```
348
448
 
349
- ## 📡 Clustering (Redis)
449
+ ## 📡 Redis Clustering & Distributed CSMS Architecture
450
+
451
+ Scale OCPP RPC servers across multiple nodes with Redis pub/sub, Streams, and presence tracking for enterprise CSMS deployments.
350
452
 
351
- Scale your OCPP server across multiple nodes using Redis.
453
+ ### Set Up Redis Clustering
352
454
 
353
- 1. **Install Adapter**:
455
+ 1. **Install Redis Adapter**:
354
456
 
355
- ```bash
356
- npm install ioredis
357
- ```
457
+ ```bash
458
+ npm install ioredis
459
+ ```
358
460
 
359
- 2. **Configure Server**:
461
+ 2. **Configure OCPP Server with Redis Adapter**:
360
462
 
361
- ```typescript
362
- import { OCPPServer } from "ocpp-ws-io";
363
- import { RedisAdapter } from "ocpp-ws-io/adapters/redis";
364
- import Redis from "ioredis";
463
+ ```typescript
464
+ import { OCPPServer } from "ocpp-ws-io";
465
+ import { RedisAdapter } from "ocpp-ws-io/adapters/redis";
466
+ import Redis from "ioredis";
365
467
 
366
- const redis = new Redis(process.env.REDIS_URL);
367
- const server = new OCPPServer({
468
+ const redis = new Redis(process.env.REDIS_URL);
469
+ const server = new OCPPServer({
368
470
  protocols: ["ocpp1.6"],
369
- });
471
+ });
370
472
 
371
- // Uses Redis Streams for clustering reliability
372
- await server.setAdapter(new RedisAdapter(redis));
373
- ```
473
+ // Uses Redis Streams for clustering reliability
474
+ await server.setAdapter(new RedisAdapter(redis));
475
+ ```
374
476
 
375
- **Features:**
477
+ **OCPP Redis Clustering Features:**
376
478
 
377
- - **Unicast Routing**: Send messages to any client on any node.
378
- - **Presence**: Track connected clients across the cluster.
379
- - **Reliability**: Uses Redis Streams for durable message delivery.
380
- - **Batch Processing**: Use `server.broadcastBatch` to combine multi-node calls effortlessly.
479
+ - **Unicast Routing**: Send OCPP RPC messages to any charge point on any cluster node.
480
+ - **Presence Tracking**: Real-time discovery of connected clients across the cluster.
481
+ - **Durability**: Redis Streams for guaranteed message delivery.
482
+ - **Batch Operations**: Use `server.broadcastBatch` for efficient multi-node requests.
381
483
 
382
- ### Custom Adapters (`EventAdapterInterface`)
484
+ ### Custom Clustering Adapters (RabbitMQ, Kafka, PubSub)
383
485
 
384
- You can build custom clustering adapters (e.g., RabbitMQ, Kafka, Postgres PUB/SUB) by implementing the exported `EventAdapterInterface`:
486
+ Build custom OCPP clustering solutions beyond Redis by implementing the `EventAdapterInterface`. This enables distributed CSMS architectures with your preferred message broker or database backend.
385
487
 
386
488
  ```typescript
387
489
  import type { EventAdapterInterface } from "ocpp-ws-io";
388
490
 
389
491
  export class CustomAdapter implements EventAdapterInterface {
390
- async publish(channel: string, data: unknown): Promise<void> {
391
- /* ... */
392
- }
393
- async subscribe(
394
- channel: string,
395
- handler: (data: unknown) => void,
396
- ): Promise<void> {
397
- /* ... */
398
- }
399
- async unsubscribe(channel: string): Promise<void> {
400
- /* ... */
401
- }
402
- async disconnect(): Promise<void> {
403
- /* ... */
404
- }
405
-
406
- // Optional primitives for advanced routing:
407
- async setPresence?(
408
- identity: string,
409
- nodeId: string,
410
- ttl: number,
411
- ): Promise<void>;
412
- async getPresence?(identity: string): Promise<string | null>;
413
- async removePresence?(identity: string): Promise<void>;
414
- async getPresenceBatch?(identities: string[]): Promise<(string | null)[]>;
415
- async publishBatch?(
416
- messages: { channel: string; data: unknown }[],
417
- ): Promise<void>;
418
- async metrics?(): Promise<Record<string, unknown>>;
492
+ async publish(channel: string, data: unknown): Promise<void> {
493
+ /* ... */
494
+ }
495
+ async subscribe(
496
+ channel: string,
497
+ handler: (data: unknown) => void,
498
+ ): Promise<void> {
499
+ /* ... */
500
+ }
501
+ async unsubscribe(channel: string): Promise<void> {
502
+ /* ... */
503
+ }
504
+ async disconnect(): Promise<void> {
505
+ /* ... */
506
+ }
507
+
508
+ // Optional primitives for advanced routing:
509
+ async setPresence?(
510
+ identity: string,
511
+ nodeId: string,
512
+ ttl: number,
513
+ ): Promise<void>;
514
+ async getPresence?(identity: string): Promise<string | null>;
515
+ async removePresence?(identity: string): Promise<void>;
516
+ async getPresenceBatch?(identities: string[]): Promise<(string | null)[]>;
517
+ async publishBatch?(
518
+ messages: { channel: string; data: unknown }[],
519
+ ): Promise<void>;
520
+ async metrics?(): Promise<Record<string, unknown>>;
419
521
  }
420
522
  ```
421
-
@@ -1,5 +1,5 @@
1
- import '../types-BZXEmDQ1.mjs';
2
- export { a as RedisAdapter, b as RedisAdapterOptions } from '../index-C0mn42-8.mjs';
1
+ import '../types-BHIHsj__.mjs';
2
+ export { a as RedisAdapter, b as RedisAdapterOptions } from '../index-Defn9aOS.mjs';
3
3
  import 'ws';
4
4
  import 'node:https';
5
5
  import 'node:http';
@@ -1,5 +1,5 @@
1
- import '../types-BZXEmDQ1.js';
2
- export { a as RedisAdapter, b as RedisAdapterOptions } from '../index-s9f97CmV.js';
1
+ import '../types-BHIHsj__.js';
2
+ export { a as RedisAdapter, b as RedisAdapterOptions } from '../index-BefjKqkS.js';
3
3
  import 'ws';
4
4
  import 'node:https';
5
5
  import 'node:http';