kirby-ocpp 0.1.2 → 0.3.0

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.
Files changed (122) hide show
  1. package/LICENSE +1 -1
  2. package/README.md +192 -88
  3. package/THIRD_PARTY_NOTICES.md +6 -2
  4. package/bin/kirby-ocpp.js +77 -0
  5. package/package.json +23 -44
  6. package/dist/cli.d.ts +0 -2
  7. package/dist/cli.js +0 -72
  8. package/dist/config.d.ts +0 -2
  9. package/dist/config.js +0 -53
  10. package/dist/ocpp/client.d.ts +0 -59
  11. package/dist/ocpp/client.js +0 -299
  12. package/dist/ocpp/schema-validator.d.ts +0 -15
  13. package/dist/ocpp/schema-validator.js +0 -75
  14. package/dist/ocpp/schemas/json/Authorize.json +0 -16
  15. package/dist/ocpp/schemas/json/AuthorizeResponse.json +0 -40
  16. package/dist/ocpp/schemas/json/BootNotification.json +0 -49
  17. package/dist/ocpp/schemas/json/BootNotificationResponse.json +0 -30
  18. package/dist/ocpp/schemas/json/CancelReservation.json +0 -15
  19. package/dist/ocpp/schemas/json/CancelReservationResponse.json +0 -20
  20. package/dist/ocpp/schemas/json/ChangeAvailability.json +0 -24
  21. package/dist/ocpp/schemas/json/ChangeAvailabilityResponse.json +0 -21
  22. package/dist/ocpp/schemas/json/ChangeConfiguration.json +0 -21
  23. package/dist/ocpp/schemas/json/ChangeConfigurationResponse.json +0 -22
  24. package/dist/ocpp/schemas/json/ClearCache.json +0 -8
  25. package/dist/ocpp/schemas/json/ClearCacheResponse.json +0 -20
  26. package/dist/ocpp/schemas/json/ClearChargingProfile.json +0 -27
  27. package/dist/ocpp/schemas/json/ClearChargingProfileResponse.json +0 -20
  28. package/dist/ocpp/schemas/json/DataTransfer.json +0 -23
  29. package/dist/ocpp/schemas/json/DataTransferResponse.json +0 -25
  30. package/dist/ocpp/schemas/json/DiagnosticsStatusNotification.json +0 -22
  31. package/dist/ocpp/schemas/json/DiagnosticsStatusNotificationResponse.json +0 -8
  32. package/dist/ocpp/schemas/json/FirmwareStatusNotification.json +0 -25
  33. package/dist/ocpp/schemas/json/FirmwareStatusNotificationResponse.json +0 -8
  34. package/dist/ocpp/schemas/json/GetCompositeSchedule.json +0 -27
  35. package/dist/ocpp/schemas/json/GetCompositeScheduleResponse.json +0 -79
  36. package/dist/ocpp/schemas/json/GetConfiguration.json +0 -16
  37. package/dist/ocpp/schemas/json/GetConfigurationResponse.json +0 -40
  38. package/dist/ocpp/schemas/json/GetDiagnostics.json +0 -30
  39. package/dist/ocpp/schemas/json/GetDiagnosticsResponse.json +0 -13
  40. package/dist/ocpp/schemas/json/GetLocalListVersion.json +0 -8
  41. package/dist/ocpp/schemas/json/GetLocalListVersionResponse.json +0 -15
  42. package/dist/ocpp/schemas/json/Heartbeat.json +0 -8
  43. package/dist/ocpp/schemas/json/HeartbeatResponse.json +0 -16
  44. package/dist/ocpp/schemas/json/MeterValues.json +0 -151
  45. package/dist/ocpp/schemas/json/MeterValuesResponse.json +0 -8
  46. package/dist/ocpp/schemas/json/RemoteStartTransaction.json +0 -127
  47. package/dist/ocpp/schemas/json/RemoteStartTransactionResponse.json +0 -20
  48. package/dist/ocpp/schemas/json/RemoteStopTransaction.json +0 -15
  49. package/dist/ocpp/schemas/json/RemoteStopTransactionResponse.json +0 -20
  50. package/dist/ocpp/schemas/json/ReserveNow.json +0 -33
  51. package/dist/ocpp/schemas/json/ReserveNowResponse.json +0 -23
  52. package/dist/ocpp/schemas/json/Reset.json +0 -20
  53. package/dist/ocpp/schemas/json/ResetResponse.json +0 -20
  54. package/dist/ocpp/schemas/json/SendLocalList.json +0 -68
  55. package/dist/ocpp/schemas/json/SendLocalListResponse.json +0 -22
  56. package/dist/ocpp/schemas/json/SetChargingProfile.json +0 -124
  57. package/dist/ocpp/schemas/json/SetChargingProfileResponse.json +0 -21
  58. package/dist/ocpp/schemas/json/StartTransaction.json +0 -32
  59. package/dist/ocpp/schemas/json/StartTransactionResponse.json +0 -44
  60. package/dist/ocpp/schemas/json/StatusNotification.json +0 -70
  61. package/dist/ocpp/schemas/json/StatusNotificationResponse.json +0 -8
  62. package/dist/ocpp/schemas/json/StopTransaction.json +0 -177
  63. package/dist/ocpp/schemas/json/StopTransactionResponse.json +0 -37
  64. package/dist/ocpp/schemas/json/TriggerMessage.json +0 -27
  65. package/dist/ocpp/schemas/json/TriggerMessageResponse.json +0 -21
  66. package/dist/ocpp/schemas/json/UnlockConnector.json +0 -15
  67. package/dist/ocpp/schemas/json/UnlockConnectorResponse.json +0 -21
  68. package/dist/ocpp/schemas/json/UpdateFirmware.json +0 -27
  69. package/dist/ocpp/schemas/json/UpdateFirmwareResponse.json +0 -8
  70. package/dist/ocpp/types.d.ts +0 -106
  71. package/dist/ocpp/types.js +0 -1
  72. package/dist/station/authorization.d.ts +0 -20
  73. package/dist/station/authorization.js +0 -78
  74. package/dist/station/charging-profile-registry.d.ts +0 -18
  75. package/dist/station/charging-profile-registry.js +0 -125
  76. package/dist/station/configuration-commands.d.ts +0 -11
  77. package/dist/station/configuration-commands.js +0 -29
  78. package/dist/station/configuration-registry.d.ts +0 -13
  79. package/dist/station/configuration-registry.js +0 -39
  80. package/dist/station/configuration.d.ts +0 -7
  81. package/dist/station/configuration.js +0 -88
  82. package/dist/station/connector-registry.d.ts +0 -25
  83. package/dist/station/connector-registry.js +0 -89
  84. package/dist/station/connector-status.d.ts +0 -7
  85. package/dist/station/connector-status.js +0 -66
  86. package/dist/station/local-auth-list.d.ts +0 -8
  87. package/dist/station/local-auth-list.js +0 -31
  88. package/dist/station/maintenance-lifecycle.d.ts +0 -25
  89. package/dist/station/maintenance-lifecycle.js +0 -110
  90. package/dist/station/meter-value-scheduler.d.ts +0 -21
  91. package/dist/station/meter-value-scheduler.js +0 -78
  92. package/dist/station/metering.d.ts +0 -15
  93. package/dist/station/metering.js +0 -138
  94. package/dist/station/ocpp-message.d.ts +0 -2
  95. package/dist/station/ocpp-message.js +0 -9
  96. package/dist/station/offline-transaction-sync.d.ts +0 -29
  97. package/dist/station/offline-transaction-sync.js +0 -99
  98. package/dist/station/payload.d.ts +0 -4
  99. package/dist/station/payload.js +0 -12
  100. package/dist/station/persistence.d.ts +0 -28
  101. package/dist/station/persistence.js +0 -206
  102. package/dist/station/reservation-rules.d.ts +0 -3
  103. package/dist/station/reservation-rules.js +0 -18
  104. package/dist/station/reservation-scheduler.d.ts +0 -11
  105. package/dist/station/reservation-scheduler.js +0 -33
  106. package/dist/station/smart-charging.d.ts +0 -45
  107. package/dist/station/smart-charging.js +0 -251
  108. package/dist/station/status-scheduler.d.ts +0 -16
  109. package/dist/station/status-scheduler.js +0 -58
  110. package/dist/station/transaction-message-retry.d.ts +0 -3
  111. package/dist/station/transaction-message-retry.js +0 -7
  112. package/dist/station/transaction-rules.d.ts +0 -7
  113. package/dist/station/transaction-rules.js +0 -34
  114. package/dist/station/trigger-message.d.ts +0 -2
  115. package/dist/station/trigger-message.js +0 -11
  116. package/dist/station-store.d.ts +0 -41
  117. package/dist/station-store.js +0 -104
  118. package/dist/station.d.ts +0 -113
  119. package/dist/station.js +0 -1204
  120. package/dist/tui.d.ts +0 -96
  121. package/dist/tui.js +0 -1656
  122. package/docs/OCPP_1.6J_CONFORMANCE.md +0 -61
package/dist/config.js DELETED
@@ -1,53 +0,0 @@
1
- import { z } from "zod";
2
- const configSchema = z.object({
3
- centralSystemUrl: z
4
- .string({
5
- required_error: "Use --url or OCPP_URL to set the Central System WebSocket URL"
6
- })
7
- .url(),
8
- chargePointId: z.string().min(1),
9
- vendor: z.string().min(1),
10
- model: z.string().min(1),
11
- connectorId: z.coerce.number().int().positive(),
12
- connectorCount: z.coerce.number().int().positive(),
13
- heartbeatIntervalSeconds: z.coerce.number().int().positive(),
14
- persistState: z.coerce.boolean(),
15
- stateDirectory: z.string().min(1).optional(),
16
- webSocketSubprotocol: z.string().min(1),
17
- webSocketPingIntervalSeconds: z.coerce.number().int().nonnegative(),
18
- webSocketReconnectEnabled: z.coerce.boolean(),
19
- webSocketReconnectInitialDelayMs: z.coerce.number().int().nonnegative(),
20
- webSocketReconnectMaxDelayMs: z.coerce.number().int().nonnegative(),
21
- webSocketReconnectMaxAttempts: z.coerce.number().int().nonnegative(),
22
- callTimeoutMs: z.coerce.number().int().positive(),
23
- tlsRejectUnauthorized: z.coerce.boolean(),
24
- tlsCaFile: z.string().min(1).optional(),
25
- tlsCertFile: z.string().min(1).optional(),
26
- tlsKeyFile: z.string().min(1).optional(),
27
- tlsServerName: z.string().min(1).optional()
28
- });
29
- export function parseConfig(input) {
30
- return configSchema.parse({
31
- centralSystemUrl: input.centralSystemUrl ?? process.env.OCPP_URL,
32
- chargePointId: input.chargePointId ?? process.env.OCPP_CHARGE_POINT_ID ?? "CP-001",
33
- vendor: input.vendor ?? process.env.OCPP_VENDOR ?? "archhaze24",
34
- model: input.model ?? process.env.OCPP_MODEL ?? "KIRBY",
35
- connectorId: input.connectorId ?? 1,
36
- connectorCount: input.connectorCount ?? input.connectorId ?? 1,
37
- heartbeatIntervalSeconds: input.heartbeatIntervalSeconds ?? 30,
38
- persistState: input.persistState ?? process.env.KIRBY_OCPP_NO_PERSIST !== "1",
39
- stateDirectory: input.stateDirectory ?? process.env.KIRBY_OCPP_STATE_DIR,
40
- webSocketSubprotocol: input.webSocketSubprotocol ?? process.env.OCPP_WS_SUBPROTOCOL ?? "ocpp1.6",
41
- webSocketPingIntervalSeconds: input.webSocketPingIntervalSeconds ?? process.env.OCPP_WS_PING_INTERVAL ?? 30,
42
- webSocketReconnectEnabled: input.webSocketReconnectEnabled ?? process.env.OCPP_RECONNECT !== "0",
43
- webSocketReconnectInitialDelayMs: input.webSocketReconnectInitialDelayMs ?? process.env.OCPP_RECONNECT_INITIAL_MS ?? 1_000,
44
- webSocketReconnectMaxDelayMs: input.webSocketReconnectMaxDelayMs ?? process.env.OCPP_RECONNECT_MAX_MS ?? 30_000,
45
- webSocketReconnectMaxAttempts: input.webSocketReconnectMaxAttempts ?? process.env.OCPP_RECONNECT_MAX_ATTEMPTS ?? 0,
46
- callTimeoutMs: input.callTimeoutMs ?? process.env.OCPP_CALL_TIMEOUT_MS ?? 30_000,
47
- tlsRejectUnauthorized: input.tlsRejectUnauthorized ?? process.env.OCPP_TLS_SKIP_VERIFY !== "1",
48
- tlsCaFile: input.tlsCaFile ?? process.env.OCPP_TLS_CA_FILE,
49
- tlsCertFile: input.tlsCertFile ?? process.env.OCPP_TLS_CERT_FILE,
50
- tlsKeyFile: input.tlsKeyFile ?? process.env.OCPP_TLS_KEY_FILE,
51
- tlsServerName: input.tlsServerName ?? process.env.OCPP_TLS_SERVER_NAME
52
- });
53
- }
@@ -1,59 +0,0 @@
1
- import EventEmitter from "node:events";
2
- import type { CallResponse, OcppMessage } from "./types.js";
3
- interface OcppClientEvents {
4
- connected: [];
5
- disconnected: [code?: number, reason?: string];
6
- error: [error: Error];
7
- log: [direction: "in" | "out", message: OcppMessage];
8
- call: [messageId: string, action: string, payload: Record<string, unknown>];
9
- }
10
- export interface OcppClientOptions {
11
- subprotocol?: string;
12
- pingIntervalSeconds?: number;
13
- tlsRejectUnauthorized?: boolean;
14
- tlsCaFile?: string;
15
- tlsCertFile?: string;
16
- tlsKeyFile?: string;
17
- tlsServerName?: string;
18
- }
19
- export declare interface OcppClient {
20
- on<K extends keyof OcppClientEvents>(event: K, listener: (...args: OcppClientEvents[K]) => void): this;
21
- emit<K extends keyof OcppClientEvents>(event: K, ...args: OcppClientEvents[K]): boolean;
22
- }
23
- export declare class OcppClient extends EventEmitter {
24
- private readonly centralSystemUrl;
25
- private readonly chargePointId;
26
- private readonly options;
27
- private socket?;
28
- private pingTimer?;
29
- private awaitingPong;
30
- private readonly pending;
31
- private readonly incomingCalls;
32
- private readonly schemaValidator;
33
- constructor(centralSystemUrl: string, chargePointId: string, options?: OcppClientOptions);
34
- get isConnected(): boolean;
35
- connect(): void;
36
- close(): void;
37
- call(action: string, payload: Record<string, unknown>, timeoutMs?: number): Promise<CallResponse>;
38
- reply(messageId: string, payload: Record<string, unknown>): void;
39
- replyError(messageId: string, code: string, description: string, details?: Record<string, unknown>): void;
40
- private send;
41
- private handleMessage;
42
- private resolveCall;
43
- private rejectCall;
44
- private rejectPending;
45
- private toError;
46
- private assertValidRequest;
47
- private assertValidResponse;
48
- private validationErrorCode;
49
- private isValidCall;
50
- private isValidCallResult;
51
- private isValidCallError;
52
- private isObjectPayload;
53
- private readMessageId;
54
- private startPingTimer;
55
- private stopPingTimer;
56
- private buildChargePointUrl;
57
- private buildWebSocketOptions;
58
- }
59
- export {};
@@ -1,299 +0,0 @@
1
- import EventEmitter from "node:events";
2
- import { readFileSync } from "node:fs";
3
- import { nanoid } from "nanoid";
4
- import WebSocket from "ws";
5
- import { OcppSchemaValidator } from "./schema-validator.js";
6
- export class OcppClient extends EventEmitter {
7
- centralSystemUrl;
8
- chargePointId;
9
- options;
10
- socket;
11
- pingTimer;
12
- awaitingPong = false;
13
- pending = new Map();
14
- incomingCalls = new Map();
15
- schemaValidator = new OcppSchemaValidator();
16
- constructor(centralSystemUrl, chargePointId, options = {}) {
17
- super();
18
- this.centralSystemUrl = centralSystemUrl;
19
- this.chargePointId = chargePointId;
20
- this.options = options;
21
- }
22
- get isConnected() {
23
- return this.socket?.readyState === WebSocket.OPEN;
24
- }
25
- connect() {
26
- this.close();
27
- const url = this.buildChargePointUrl();
28
- const subprotocol = this.options.subprotocol ?? "ocpp1.6";
29
- this.socket = new WebSocket(url, [subprotocol], this.buildWebSocketOptions());
30
- this.socket.on("open", () => {
31
- if (this.socket?.protocol !== subprotocol) {
32
- const error = new Error(`Central System did not negotiate required WebSocket subprotocol ${subprotocol}`);
33
- this.emit("error", error);
34
- this.socket?.close(1002, "OCPP subprotocol required");
35
- return;
36
- }
37
- this.startPingTimer();
38
- this.emit("connected");
39
- });
40
- this.socket.on("message", (data) => this.handleMessage(data.toString()));
41
- this.socket.on("pong", () => {
42
- this.awaitingPong = false;
43
- });
44
- this.socket.on("close", (code, reason) => {
45
- this.stopPingTimer();
46
- this.rejectPending(new Error(`Connection closed (${code})`));
47
- this.emit("disconnected", code, reason.toString());
48
- });
49
- this.socket.on("error", (error) => this.emit("error", error));
50
- }
51
- close() {
52
- if (!this.socket) {
53
- return;
54
- }
55
- this.rejectPending(new Error("Connection closed"));
56
- this.stopPingTimer();
57
- const socket = this.socket;
58
- socket.removeAllListeners();
59
- socket.on("error", () => { });
60
- if (socket.readyState === WebSocket.OPEN) {
61
- socket.close();
62
- }
63
- else if (socket.readyState === WebSocket.CONNECTING) {
64
- socket.terminate();
65
- }
66
- this.socket = undefined;
67
- }
68
- async call(action, payload, timeoutMs = 30_000) {
69
- if (!this.isConnected || !this.socket) {
70
- throw new Error("Not connected to Central System");
71
- }
72
- this.assertValidRequest(action, payload);
73
- const messageId = nanoid(12);
74
- const message = [2, messageId, action, payload];
75
- const response = new Promise((resolve, reject) => {
76
- const timeout = setTimeout(() => {
77
- this.pending.delete(messageId);
78
- reject(new Error(`${action} timed out after ${timeoutMs}ms`));
79
- }, timeoutMs);
80
- this.pending.set(messageId, { action, resolve, reject, timeout });
81
- });
82
- this.send(message);
83
- return response;
84
- }
85
- reply(messageId, payload) {
86
- const action = this.incomingCalls.get(messageId);
87
- if (!action) {
88
- throw new Error(`Cannot reply to unknown CALL ${messageId}`);
89
- }
90
- this.assertValidResponse(action, payload);
91
- this.incomingCalls.delete(messageId);
92
- this.send([3, messageId, payload]);
93
- }
94
- replyError(messageId, code, description, details = {}) {
95
- this.incomingCalls.delete(messageId);
96
- this.send([4, messageId, code, description, details]);
97
- }
98
- send(message) {
99
- if (!this.socket || this.socket.readyState !== WebSocket.OPEN) {
100
- throw new Error("WebSocket is not open");
101
- }
102
- this.socket.send(JSON.stringify(message));
103
- this.emit("log", "out", message);
104
- }
105
- handleMessage(raw) {
106
- let message;
107
- try {
108
- message = JSON.parse(raw);
109
- }
110
- catch (error) {
111
- this.emit("error", error instanceof Error ? error : new Error(String(error)));
112
- return;
113
- }
114
- this.emit("log", "in", message);
115
- if (!Array.isArray(message)) {
116
- this.emit("error", new Error("Invalid OCPP message: expected array"));
117
- return;
118
- }
119
- if (message[0] === 2) {
120
- if (!this.isValidCall(message)) {
121
- const messageId = this.readMessageId(message);
122
- if (messageId) {
123
- this.replyError(messageId, "FormationViolation", "Invalid CALL frame");
124
- }
125
- this.emit("error", new Error("Invalid CALL frame"));
126
- return;
127
- }
128
- const call = message;
129
- if (!this.schemaValidator.hasRequestSchema(call[2])) {
130
- this.replyError(call[1], "NotImplemented", `${call[2]} is not an OCPP 1.6J action`);
131
- return;
132
- }
133
- const validation = this.schemaValidator.validateRequest(call[2], call[3]);
134
- if (!validation.valid) {
135
- this.replyError(call[1], this.validationErrorCode(validation.errorNames), validation.errors.join("; "));
136
- return;
137
- }
138
- this.incomingCalls.set(call[1], call[2]);
139
- this.emit("call", call[1], call[2], call[3] ?? {});
140
- return;
141
- }
142
- if (message[0] === 3) {
143
- if (!this.isValidCallResult(message)) {
144
- this.emit("error", new Error("Invalid CALLRESULT frame"));
145
- return;
146
- }
147
- this.resolveCall(message);
148
- return;
149
- }
150
- if (message[0] === 4) {
151
- if (!this.isValidCallError(message)) {
152
- this.emit("error", new Error("Invalid CALLERROR frame"));
153
- return;
154
- }
155
- this.rejectCall(message);
156
- return;
157
- }
158
- this.emit("error", new Error(`Unsupported OCPP message type: ${message[0]}`));
159
- }
160
- resolveCall(message) {
161
- const [, messageId, payload] = message;
162
- const pending = this.pending.get(messageId);
163
- if (!pending) {
164
- return;
165
- }
166
- clearTimeout(pending.timeout);
167
- this.pending.delete(messageId);
168
- const validation = this.schemaValidator.validateResponse(pending.action, payload);
169
- if (!validation.valid) {
170
- pending.reject(new Error(`${pending.action} response violates OCPP 1.6J schema (${this.validationErrorCode(validation.errorNames)}): ${validation.errors.join("; ")}`));
171
- return;
172
- }
173
- pending.resolve({ messageId, payload });
174
- }
175
- rejectCall(message) {
176
- const error = this.toError(message);
177
- const pending = this.pending.get(error.messageId);
178
- if (!pending) {
179
- this.emit("error", new Error(error.description || error.code));
180
- return;
181
- }
182
- clearTimeout(pending.timeout);
183
- this.pending.delete(error.messageId);
184
- pending.reject(new Error(`${pending.action} failed: ${error.code} ${error.description}`.trim()));
185
- }
186
- rejectPending(error) {
187
- for (const [messageId, pending] of this.pending) {
188
- clearTimeout(pending.timeout);
189
- pending.reject(error);
190
- this.pending.delete(messageId);
191
- }
192
- }
193
- toError(message) {
194
- const [, messageId, code, description, details] = message;
195
- return { messageId, code, description, details };
196
- }
197
- assertValidRequest(action, payload) {
198
- const validation = this.schemaValidator.validateRequest(action, payload);
199
- if (!validation.valid) {
200
- throw new Error(`${action} request violates OCPP 1.6J schema (${this.validationErrorCode(validation.errorNames)}): ${validation.errors.join("; ")}`);
201
- }
202
- }
203
- assertValidResponse(action, payload) {
204
- const validation = this.schemaValidator.validateResponse(action, payload);
205
- if (!validation.valid) {
206
- throw new Error(`${action} response violates OCPP 1.6J schema (${this.validationErrorCode(validation.errorNames)}): ${validation.errors.join("; ")}`);
207
- }
208
- }
209
- validationErrorCode(errorNames) {
210
- if (errorNames.includes("required")) {
211
- return "OccurenceConstraintViolation";
212
- }
213
- if (errorNames.includes("type")) {
214
- return "TypeConstraintViolation";
215
- }
216
- if (errorNames.some((name) => ["additionalProperties", "enum", "maxLength", "minLength", "minimum", "maximum", "multipleOf", "format"].includes(name))) {
217
- return "PropertyConstraintViolation";
218
- }
219
- return "FormationViolation";
220
- }
221
- isValidCall(message) {
222
- return (message.length === 4 &&
223
- message[0] === 2 &&
224
- typeof message[1] === "string" &&
225
- typeof message[2] === "string" &&
226
- this.isObjectPayload(message[3]));
227
- }
228
- isValidCallResult(message) {
229
- return message.length === 3 && message[0] === 3 && typeof message[1] === "string" && this.isObjectPayload(message[2]);
230
- }
231
- isValidCallError(message) {
232
- return (message.length === 5 &&
233
- message[0] === 4 &&
234
- typeof message[1] === "string" &&
235
- typeof message[2] === "string" &&
236
- typeof message[3] === "string" &&
237
- this.isObjectPayload(message[4]));
238
- }
239
- isObjectPayload(value) {
240
- return Boolean(value) && typeof value === "object" && !Array.isArray(value);
241
- }
242
- readMessageId(message) {
243
- return typeof message[1] === "string" ? message[1] : undefined;
244
- }
245
- startPingTimer() {
246
- this.stopPingTimer();
247
- const intervalSeconds = this.options.pingIntervalSeconds ?? 30;
248
- if (intervalSeconds <= 0) {
249
- return;
250
- }
251
- this.awaitingPong = false;
252
- this.pingTimer = setInterval(() => {
253
- if (this.socket?.readyState === WebSocket.OPEN) {
254
- if (this.awaitingPong) {
255
- this.emit("error", new Error("WebSocket pong timed out"));
256
- this.socket.terminate();
257
- return;
258
- }
259
- this.awaitingPong = true;
260
- this.socket.ping();
261
- }
262
- }, intervalSeconds * 1000);
263
- this.pingTimer.unref();
264
- }
265
- stopPingTimer() {
266
- if (!this.pingTimer) {
267
- return;
268
- }
269
- clearInterval(this.pingTimer);
270
- this.pingTimer = undefined;
271
- this.awaitingPong = false;
272
- }
273
- buildChargePointUrl() {
274
- const base = this.centralSystemUrl.replace(/\/+$/, "");
275
- const id = encodeURIComponent(this.chargePointId);
276
- if (base.endsWith(`/${id}`)) {
277
- return base;
278
- }
279
- return `${base}/${id}`;
280
- }
281
- buildWebSocketOptions() {
282
- const options = {
283
- rejectUnauthorized: this.options.tlsRejectUnauthorized ?? true
284
- };
285
- if (this.options.tlsCaFile) {
286
- options.ca = readFileSync(this.options.tlsCaFile);
287
- }
288
- if (this.options.tlsCertFile) {
289
- options.cert = readFileSync(this.options.tlsCertFile);
290
- }
291
- if (this.options.tlsKeyFile) {
292
- options.key = readFileSync(this.options.tlsKeyFile);
293
- }
294
- if (this.options.tlsServerName) {
295
- options.servername = this.options.tlsServerName;
296
- }
297
- return options;
298
- }
299
- }
@@ -1,15 +0,0 @@
1
- export interface OcppSchemaValidationResult {
2
- valid: boolean;
3
- errors: string[];
4
- errorNames: string[];
5
- }
6
- export declare class OcppSchemaValidator {
7
- private readonly validator;
8
- private readonly schemas;
9
- constructor(schemaDirectory?: string);
10
- hasRequestSchema(action: string): boolean;
11
- hasResponseSchema(action: string): boolean;
12
- validateRequest(action: string, payload: unknown): OcppSchemaValidationResult;
13
- validateResponse(action: string, payload: unknown): OcppSchemaValidationResult;
14
- private validate;
15
- }
@@ -1,75 +0,0 @@
1
- import { existsSync, readdirSync, readFileSync } from "node:fs";
2
- import { dirname, join } from "node:path";
3
- import { fileURLToPath } from "node:url";
4
- import { Validator } from "jsonschema";
5
- export class OcppSchemaValidator {
6
- validator = new Validator();
7
- schemas = new Map();
8
- constructor(schemaDirectory = findSchemaDirectory()) {
9
- for (const file of readdirSync(schemaDirectory)) {
10
- if (!file.endsWith(".json")) {
11
- continue;
12
- }
13
- const schema = JSON.parse(readFileSync(join(schemaDirectory, file), "utf8"));
14
- const name = file.replace(/\.json$/, "");
15
- this.schemas.set(name, stripSchemaMetadata(schema, true));
16
- }
17
- }
18
- hasRequestSchema(action) {
19
- return this.schemas.has(action);
20
- }
21
- hasResponseSchema(action) {
22
- return this.schemas.has(`${action}Response`);
23
- }
24
- validateRequest(action, payload) {
25
- return this.validate(action, payload);
26
- }
27
- validateResponse(action, payload) {
28
- return this.validate(`${action}Response`, payload);
29
- }
30
- validate(schemaName, payload) {
31
- const schema = this.schemas.get(schemaName);
32
- if (!schema) {
33
- return {
34
- valid: false,
35
- errors: [`No JSON schema found for ${schemaName}`],
36
- errorNames: ["schema"]
37
- };
38
- }
39
- const result = this.validator.validate(payload, schema);
40
- return {
41
- valid: result.valid,
42
- errors: result.errors.map((error) => error.stack),
43
- errorNames: result.errors.map((error) => error.name)
44
- };
45
- }
46
- }
47
- function stripSchemaMetadata(value, root = false) {
48
- if (Array.isArray(value)) {
49
- return value.map((item) => stripSchemaMetadata(item));
50
- }
51
- if (!value || typeof value !== "object") {
52
- return value;
53
- }
54
- const output = {};
55
- for (const [key, child] of Object.entries(value)) {
56
- if (key === "$schema" || key === "title" || (root && key === "id")) {
57
- continue;
58
- }
59
- output[key] = stripSchemaMetadata(child);
60
- }
61
- return output;
62
- }
63
- function findSchemaDirectory() {
64
- const currentDir = dirname(fileURLToPath(import.meta.url));
65
- const candidates = [
66
- join(currentDir, "schemas/json"),
67
- join(currentDir, "../../src/ocpp/schemas/json")
68
- ];
69
- for (const candidate of candidates) {
70
- if (existsSync(candidate)) {
71
- return candidate;
72
- }
73
- }
74
- throw new Error("OCPP 1.6J JSON schemas were not found");
75
- }
@@ -1,16 +0,0 @@
1
- {
2
- "$schema": "http://json-schema.org/draft-04/schema#",
3
- "id": "urn:OCPP:1.6:2019:12:AuthorizeRequest",
4
- "title": "AuthorizeRequest",
5
- "type": "object",
6
- "properties": {
7
- "idTag": {
8
- "type": "string",
9
- "maxLength": 20
10
- }
11
- },
12
- "additionalProperties": false,
13
- "required": [
14
- "idTag"
15
- ]
16
- }
@@ -1,40 +0,0 @@
1
- {
2
- "$schema": "http://json-schema.org/draft-04/schema#",
3
- "id": "urn:OCPP:1.6:2019:12:AuthorizeResponse",
4
- "title": "AuthorizeResponse",
5
- "type": "object",
6
- "properties": {
7
- "idTagInfo": {
8
- "type": "object",
9
- "properties": {
10
- "expiryDate": {
11
- "type": "string",
12
- "format": "date-time"
13
- },
14
- "parentIdTag": {
15
- "type": "string",
16
- "maxLength": 20
17
- },
18
- "status": {
19
- "type": "string",
20
- "additionalProperties": false,
21
- "enum": [
22
- "Accepted",
23
- "Blocked",
24
- "Expired",
25
- "Invalid",
26
- "ConcurrentTx"
27
- ]
28
- }
29
- },
30
- "additionalProperties": false,
31
- "required": [
32
- "status"
33
- ]
34
- }
35
- },
36
- "additionalProperties": false,
37
- "required": [
38
- "idTagInfo"
39
- ]
40
- }
@@ -1,49 +0,0 @@
1
- {
2
- "$schema": "http://json-schema.org/draft-04/schema#",
3
- "id": "urn:OCPP:1.6:2019:12:BootNotificationRequest",
4
- "title": "BootNotificationRequest",
5
- "type": "object",
6
- "properties": {
7
- "chargePointVendor": {
8
- "type": "string",
9
- "maxLength": 20
10
- },
11
- "chargePointModel": {
12
- "type": "string",
13
- "maxLength": 20
14
- },
15
- "chargePointSerialNumber": {
16
- "type": "string",
17
- "maxLength": 25
18
- },
19
- "chargeBoxSerialNumber": {
20
- "type": "string",
21
- "maxLength": 25
22
- },
23
- "firmwareVersion": {
24
- "type": "string",
25
- "maxLength": 50
26
- },
27
- "iccid": {
28
- "type": "string",
29
- "maxLength": 20
30
- },
31
- "imsi": {
32
- "type": "string",
33
- "maxLength": 20
34
- },
35
- "meterType": {
36
- "type": "string",
37
- "maxLength": 25
38
- },
39
- "meterSerialNumber": {
40
- "type": "string",
41
- "maxLength": 25
42
- }
43
- },
44
- "additionalProperties": false,
45
- "required": [
46
- "chargePointVendor",
47
- "chargePointModel"
48
- ]
49
- }
@@ -1,30 +0,0 @@
1
- {
2
- "$schema": "http://json-schema.org/draft-04/schema#",
3
- "id": "urn:OCPP:1.6:2019:12:BootNotificationResponse",
4
- "title": "BootNotificationResponse",
5
- "type": "object",
6
- "properties": {
7
- "status": {
8
- "type": "string",
9
- "additionalProperties": false,
10
- "enum": [
11
- "Accepted",
12
- "Pending",
13
- "Rejected"
14
- ]
15
- },
16
- "currentTime": {
17
- "type": "string",
18
- "format": "date-time"
19
- },
20
- "interval": {
21
- "type": "integer"
22
- }
23
- },
24
- "additionalProperties": false,
25
- "required": [
26
- "status",
27
- "currentTime",
28
- "interval"
29
- ]
30
- }
@@ -1,15 +0,0 @@
1
- {
2
- "$schema": "http://json-schema.org/draft-04/schema#",
3
- "id": "urn:OCPP:1.6:2019:12:CancelReservationRequest",
4
- "title": "CancelReservationRequest",
5
- "type": "object",
6
- "properties": {
7
- "reservationId": {
8
- "type": "integer"
9
- }
10
- },
11
- "additionalProperties": false,
12
- "required": [
13
- "reservationId"
14
- ]
15
- }
@@ -1,20 +0,0 @@
1
- {
2
- "$schema": "http://json-schema.org/draft-04/schema#",
3
- "id": "urn:OCPP:1.6:2019:12:CancelReservationResponse",
4
- "title": "CancelReservationResponse",
5
- "type": "object",
6
- "properties": {
7
- "status": {
8
- "type": "string",
9
- "additionalProperties": false,
10
- "enum": [
11
- "Accepted",
12
- "Rejected"
13
- ]
14
- }
15
- },
16
- "additionalProperties": false,
17
- "required": [
18
- "status"
19
- ]
20
- }