Skip to content

Commit 6d8394f

Browse files
init commit
0 parents  commit 6d8394f

File tree

694 files changed

+71822
-0
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

694 files changed

+71822
-0
lines changed

.babelrc

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
{
2+
"presets": [
3+
['@babel/preset-env', {targets: {node: 'current'}}],
4+
'@babel/preset-typescript',
5+
],
6+
"plugins": []
7+
}

.eslintrc.js

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
module.exports = {
2+
extends: [
3+
'airbnb',
4+
'airbnb-typescript'
5+
],
6+
parserOptions: {
7+
project: './tsconfig.json'
8+
},
9+
rules: {
10+
"import/prefer-default-export": 0,
11+
"@typescript-eslint/indent": 0,
12+
"max-len": "warn"
13+
}
14+
};

.github/workflows/publish.yml

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
name: "publish npm"
2+
3+
on:
4+
release:
5+
types: [ created ]
6+
7+
jobs:
8+
9+
build:
10+
runs-on: ubuntu-latest
11+
steps:
12+
- uses: actions/checkout@v3
13+
- uses: actions/setup-node@v3
14+
with:
15+
node-version: 16
16+
- run: npm ci
17+
- run: npm test
18+
publish:
19+
needs: build
20+
runs-on: ubuntu-latest
21+
steps:
22+
- name: checkout
23+
uses: actions/checkout@v3
24+
- name: node
25+
uses: actions/setup-node@v3
26+
with:
27+
node-version: 16
28+
registry-url: https://registry.npmjs.org
29+
- name: publish
30+
run: npm publish --access public
31+
env:
32+
NODE_AUTH_TOKEN: ${{secrets.NPM_AUTH_TOKEN}}

.gitignore

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
node_modules/
2+
.idea

.npmignore

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
dist/

README.md

Lines changed: 113 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,113 @@
1+
![Maintaner](https://img.shields.io/badge/maintainer-extrawest.com-blue)
2+
# Extrawest OCPP 2.0.1 package
3+
4+
Typescript package implementing the JSON version of the Open Charge Point Protocol (OCPP). Currently OCPP 2.0.1 is
5+
supported.
6+
7+
Open Charge Point Protocol (OCPP, <http://www.openchargealliance.org/>) is a communication protocol between multiple
8+
charging stations ("charge points") and a single management software ("central system").
9+
10+
## Installation
11+
12+
```
13+
npm i @extrawest/node-ts-ocpp
14+
```
15+
16+
## Usage
17+
18+
### Central System
19+
20+
```ts
21+
import {
22+
OcppServer, OcppClientConnection, BootNotificationRequest, BootNotificationResponse,
23+
} from 'ocpp-ts';
24+
25+
const centralSystemSimple = new OcppServer();
26+
centralSystemSimple.listen(9220);
27+
centralSystemSimple.on('connection', (client: OcppClientConnection) => {
28+
console.log(`Client ${client.getCpId()} connected`);
29+
client.on('close', (code: number, reason: Buffer) => {
30+
console.log(`Client ${client.getCpId()} closed connection`, code, reason.toString());
31+
});
32+
33+
client.on('BootNotification', (request: BootNotificationRequest, cb: (response: BootNotificationResponse) => void) => {
34+
const response: BootNotificationResponse = {
35+
status: 'Accepted',
36+
currentTime: new Date().toISOString(),
37+
interval: 60,
38+
};
39+
cb(response);
40+
});
41+
});
42+
```
43+
44+
### Charging Point
45+
46+
```ts
47+
import {
48+
BootNotificationRequest,
49+
BootNotificationResponse,
50+
OcppClient, OcppError,
51+
} from 'ocpp-ts';
52+
53+
const chargingPointSimple = new OcppClient('CP1111');
54+
chargingPointSimple.on('error', (err: Error) => {
55+
console.log(err.message);
56+
});
57+
chargingPointSimple.on('close', () => {
58+
console.log('Connection closed');
59+
});
60+
61+
chargingPointSimple.on('connect', async () => {
62+
const boot: BootNotificationRequest = {
63+
chargingStation: {
64+
model: "eParking",
65+
vendorName: "NECU-T2"
66+
},
67+
reason: "Unknown"
68+
};
69+
70+
try {
71+
const bootResp: BootNotificationResponse = await chargingPointSimple.callRequest('BootNotification', boot);
72+
if (bootResp.status === 'Accepted') {
73+
console.log('Bootnotification accepted');
74+
}
75+
} catch (e) {
76+
if (e instanceof Error || e instanceof OcppError) {
77+
console.error(e.message);
78+
}
79+
}
80+
});
81+
chargingPointSimple.connect('ws://localhost:9220/');
82+
```
83+
84+
## Security
85+
86+
Add required certificates for Central System, note from OCPP protocol:
87+
88+
*As some Charge Point implementations are using embedded systems with limited computing
89+
resources, we impose an additional restriction on the TLS configuration on the server side:*
90+
91+
* The TLS certificate SHALL be an RSA certificate with a size no greater than 2048 bytes
92+
93+
```ts
94+
centralSystemSimple.on('authorization', (cbId: string, req: IncomingMessage, cb: (err?: Error) => void) => {
95+
console.log('authorization', cbId, req.headers.authorization);
96+
// validate authorization header
97+
// cb(new Error('Unathorized')); // Deny
98+
cb(); // Accept
99+
});
100+
centralSystemSimple.listen(9220, {
101+
cert: fs.readFileSync('cert.pem'),
102+
key: fs.readFileSync('key.pem'),
103+
});
104+
```
105+
106+
If the central system requires authorization, an authorization header can be placed as the second parameter.
107+
108+
```ts
109+
chargingPointSimple.connect('wss://eparking.fi/ocpp/', {
110+
Authorization: getBasicAuth(),
111+
});
112+
```
113+

__tests__/OcppProtocolTest.ts

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
import { Protocol } from '../src/impl/Protocol';
2+
import { OcppServer } from "../src";
3+
import { Server } from '../src/impl/Server';
4+
5+
describe('OcppProtocol', () => {
6+
it('should extract cp id from the url', () => {
7+
const cpId = Server.getCpIdFromUrl('ws://localhost/ocpp/service/CP5612')
8+
expect(cpId).toBe('CP5612');
9+
});
10+
11+
it('should extract cp and decode correctly', () => {
12+
const cpId = Server.getCpIdFromUrl('ws://eparking.fi/ocpp/service/CP%205612')
13+
expect(cpId).toBe('CP 5612');
14+
});
15+
16+
it('should strip query parameters from uri', () => {
17+
const cpId = Server.getCpIdFromUrl('ws://sub.eparking.fi/ocpp/service/CP%205612?foo=bar')
18+
expect(cpId).toBe('CP 5612');
19+
});
20+
21+
it('should return undefined cp id if provided with undefined input', () => {
22+
const cpId = Server.getCpIdFromUrl(undefined)
23+
expect(cpId).toBe(undefined);
24+
});
25+
});

__tests__/OcppSchemaTest.ts

Lines changed: 74 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,74 @@
1+
import { Server } from "../src/impl/Server";
2+
import { SchemaValidator } from "../src/impl/SchemaValidator";
3+
import BootNotification from "../src/schemas/BootNotification.json"
4+
5+
import {
6+
ERROR_FORMATIONVIOLATION,
7+
ERROR_PROPERTYCONSTRAINTVIOLATION, ERROR_PROTOCOLERROR, ERROR_TYPECONSTRAINTVIOLATION,
8+
OcppError,
9+
} from "../src/impl/OcppError";
10+
11+
describe('OcppSchema', () => {
12+
it('should throw format violation', () => {
13+
const validator = new SchemaValidator(BootNotification);
14+
const t = () => {
15+
validator.validate({test: 'foo'})
16+
}
17+
expect(t).toThrow(ERROR_FORMATIONVIOLATION)
18+
});
19+
20+
it('should throw type contstrain violation', () => {
21+
const validator = new SchemaValidator(BootNotification);
22+
const t = () => {
23+
validator.validate({
24+
chargingStation: {
25+
model: 90,
26+
vendorName: "sad"
27+
},
28+
reason: "sdfsdf"
29+
})
30+
}
31+
expect(t).toThrow(ERROR_TYPECONSTRAINTVIOLATION)
32+
});
33+
34+
it('should throw property constrain violation for long string', () => {
35+
const validator = new SchemaValidator(BootNotification);
36+
const t = () => {
37+
validator.validate({
38+
chargingStation: {
39+
model: "sdassssssssssssssssssssssssssssssssssssssssssssssssss",
40+
vendorName: "sad"
41+
},
42+
reason: "sdfsdf"
43+
})
44+
}
45+
expect(t).toThrow(ERROR_PROPERTYCONSTRAINTVIOLATION)
46+
});
47+
48+
it('should throw protocol error for missing required attribute', () => {
49+
const validator = new SchemaValidator(BootNotification);
50+
const t = () => {
51+
validator.validate({
52+
chargingStation: {
53+
model: "sda",
54+
vendorName: "sad"
55+
}
56+
})
57+
}
58+
expect(t).toThrow(ERROR_PROTOCOLERROR)
59+
});
60+
61+
it('should throw property violation for invalid enum value', () => {
62+
const validator = new SchemaValidator(BootNotification);
63+
const t = () => {
64+
validator.validate({
65+
chargingStation: {
66+
model: "sda",
67+
vendorName: "sad"
68+
},
69+
reason: "sdfsdf"
70+
})
71+
}
72+
expect(t).toThrow(ERROR_PROPERTYCONSTRAINTVIOLATION)
73+
});
74+
});
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
export {};
Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
"use strict";
2+
var __importDefault = (this && this.__importDefault) || function (mod) {
3+
return (mod && mod.__esModule) ? mod : { "default": mod };
4+
};
5+
Object.defineProperty(exports, "__esModule", { value: true });
6+
var fs_1 = __importDefault(require("fs"));
7+
var src_1 = require("../src/");
8+
var cs = new src_1.OcppServer();
9+
cs.on('connection', function (client) {
10+
console.log("Client ".concat(client.getCpId(), " connected"));
11+
client.on('close', function (code, reason) {
12+
console.log("Client ".concat(client.getCpId(), " closed connection"), code, reason.toString());
13+
});
14+
client.on('BootNotification', function (request, cb) {
15+
var response = {
16+
status: 'Accepted',
17+
currentTime: new Date().toISOString(),
18+
interval: 60,
19+
};
20+
cb(response);
21+
});
22+
});
23+
cs.on('authorization', function (cbId, req, cb) {
24+
console.log('authorization', cbId, req.headers.authorization);
25+
// validate authorization header
26+
// cb(new Error('Unathorized')); // Deny
27+
cb(); // Accept
28+
});
29+
cs.listen(9220, {
30+
cert: fs_1.default.readFileSync('cert.pem'),
31+
key: fs_1.default.readFileSync('key.pem'),
32+
});

0 commit comments

Comments
 (0)