A library for fast, type-aware validation of OCPP 1.6, OCPP 2.0 and OCPP 2.1 messages, built against the official JSON Schema documents published by the Open Charge Alliance.
typed-ocpp
This library exports all functions and typings related to OCPP 1.6,
OCPP 2.0 and OCPP 2.1 under, respectively, the OCPP16
, OCPP20
, OCPP21
namespaces:
import { OCPP16, OCPP20, OCPP21 } from 'typed-ocpp';
All three namespaces export identical APIs while typings and schemas differ according to the differences in the respective OCPP versions. All of the examples below apply to all namespaces.
The validate()
function is a user-defined, validating type guard which
returns true
if the provided value is a spec-compliant OCPP message and
false
otherwise.
import {Â OCPP16 } from 'typed-ocpp';
const value = [2,"test","BootNotification",{"chargePointModel":"model","chargePointVendor":"vendor"}];
if (OCPP16.validate(value)) {
// valid
}
If validate()
returns true
, the TS compiler will infer the provided value
to be of one of the following types:
OCPP16.Call // Union of all types of Call messages
OCPP16.CallError // Type for Call Error messages
OCPP16.UncheckedCallResult // Type for "unchecked" Call Result messages
The TS compiler will then be able to use known types to infer others:
const value = [2,"test","BootNotification",{"chargePointModel":"model","chargePointVendor":"vendor"}];
if (OCPP16.validate(value)) {
if (OCPP16.isCall(value)) {
value[2]; // TS gives type "OCPP.Action"
if (value[2] === OCPP16.Action.BootNotification) {
// TS infers the shape of the call payload based on the action
value[3].chargePointModel; // TS gives type "string"
value[3].randomProp; // TS compilation error
}
}
}
If validation fails and validate()
returns false
, validation errors will
be stored in the validate.errors
array:
const value = 'foobar';
if (!OCPP16.validate(value)) {
// prints: [ 'Invalid OCPP message: invalid message type or not an array' ]
console.log(OCPP16.validate.errors);
}
The validateCall()
, validateCallError()
and validateCallResult()
functions are user-defined, validating type guards specific to each type of
message defined by the OCPP specs: call, call error and
call result.
These functions behave in the same way as the validate()
function but only
return true
when provided with values of the corresponding message type.
Validation errors can be retrieved via the .errors
property, just as with
validate()
.
The isCall()
, isCallResult()
and isCallError()
functions are
user-defined, non-validating type guards that facilitate identifying the type
of a valid message:
if (OCPP16.validate(message)) {
if (OCPP16.isCall(message)) {
// TS infers that parsed is of type OCPP16.Call
}
if (OCPP16.isCallResult(message)) {
// TS infers that parsed is of type OCPP16.UncheckedCallResult
}
if (OCPP16.isCallError(message)) {
// TS infers that parsed is of type OCPP16.CallError
}
}
Post-validation, call result messages are inferred by the TS compiler to
be of the UncheckedCallResult
type, which is a generic type that does not
constrain the call result's payload to any specific shape as doing so
requires matching against the originating call message.
Complete validation of a call result message against its originating
call message can be done through the checkCallResult()
user-defined,
validating type guard.
If checkCallResult()
returns true
, the provided call result message
is guaranteed to match the provided call message in terms of:
- Matching the expected type, incl. the payload (for example: being of type
OCPP16.BootNotificationCallResult
given an originatingOCPP16.BootNotificationCall
essage). - Sharing the same call identifier with the originating call message.
const call = [
2,
"test",
"BootNotification",
{"chargePointModel":"model","chargePointVendor":"vendor"},
] satisfies OCPP16.Call;
const result = [
3,
"test",
{ status: "Accepted", currentTime: "1970-01-01T00:00:00.000Z", interval: 10 },
] satisfies OCPP16.UncheckedCallResult<any>;
// Narrows the type of `call` to `OCPP16.BootNotificationCall`
if (call[2] === OCPP16.Action.BootNotification) {
// Validates `result` against `call`, narrowing the type of
// `result` to `OCPP16.BootNotificationCallResult`
if (OCPP16.checkCallResult(result, call)) {
// Inferred as "Accepted" | "Pending" | "Rejected"
result[2].status;
} else {
// The `result` message does not match the originating `call` message
console.log(OCPP16.checkCallResult.errors);
}
}
Just like validate()
, when checkCallResult()
returns false
it stores
validation errors in the checkCallResult.errors
array.
Within both the OCPP16
, OCPP20
and OCPP21
namespaces, typed-ocpp
provides a set of typings and schemas that covers most aspects of OCPP
messages.
// Union of all types of Call messages
OCPP16.Call
// Union of all Call Result message types
OCPP16.CallResult
// Type for Call Error messages
OCPP16.CallError
// Type for "unchecked" Call Result messages
OCPP16.UncheckedCallResult
// Generic type of Call Result message that resolves to the specific type of
// Call Result message matching the provided type of Call message "C"
OCPP16.CheckedCallResult<C extends OCPP16.Call>
// Message-specific types
OCPP16.AuthorizationCall
OCPP16.AuthorizationCallResult
OCPP16.BootNotificationCall
OCPP16.BootNotificationCallResult
/* ... */
Specific types for Call and Call Result messages making up the
OCPP16.Call
and OCPP16.CallResult
unions use the Call
and CallResult
suffixes:
OCPP16.MeterValuesCall
OCPP16.MeterValuesCallResult
/* ... and so on ...*/
For each of these Call
and CallResult
types, the respective Request
and
Response
types model their specific payloads and map directly to the official
JSON schemas:
OCPP16.MeterValuesRequest
OCPP16.MeterValuesResponse
/* ... and so on ...*/
When returning true
, the checkCallResult()
function leads the TS compiler
to infer the generic type CheckedCallResult<C extends Call>
, which resolves
to the specific type of call result message that corresponds to the type
of call message provided as the C
type argument.
The generic CheckedCallResult<C extends Call>
type can also be used on its
own to model a call result message after a known or inferred type of
call message:
const result: OCPP16.CheckedCallResult<OCPP16.GetConfigurationCall> = [
OCPP16.MessageType.CALLRESULT,
'<call_id>',
{
configurationKey: [
{ key: 'some_key', value: 'some_value', readonly: true },
],
// the TS compiler will return an error here due to unsupported property
// "foobar" in OCPP16.GetConfigurationCallResult payloads.
foobar: 42,
},
];
OCPP16.MessageType // enum of message types (CALL = 2, CALLRESULT = 3, CALLERROR = 4)
OCPP16.Action // enum of actions in Call messages ("Authorize", "BootNotification", ...)
OCPP16.ErrorCode // enum of error code in Call Error messages ("NotImplemented", "NotSupported", ...)
OCPP20.MessageType // enum of message types (CALL = 2, CALLRESULT = 3, CALLERROR = 4)
OCPP20.Action // enum of actions in Call messages ("Authorize", "BootNotification", ...)
OCPP20.ErrorCode // enum of error code in Call Error messages ("NotImplemented", "NotSupported", ...)
OCPP21.MessageType // enum of message types (CALL = 2, CALLRESULT = 3, CALLERROR = 4, CALLRESULTERROR = 5, SEND = 6)
OCPP21.Action // enum of actions in Call messages ("Authorize", "BootNotification", ...)
OCPP21.ErrorCode // enum of error code in Call Error messages ("NotImplemented", "NotSupported", ...)
OCPP 2.1 introduced two new types of messages: CALLRESULTERROR
and SEND
,
using the values 5
and 6
respectively.
This library allows OCPP 2.1 Call messages to have either the CALL
or
the SEND
type, regardless of the specific action.
This library also allows OCPP 2.1 Call Error messages to have either the
CALLERROR
or the CALLRESULTERROR
type.
The following types may be used to model value descriptors within
MeterValues
Call messages:
OCPP16.Context // sampling context ("Transaction.Begin", "Sample.Periodic", ...)
OCPP16.Measurand // value measurand ("Power.Active.Import", "Frequency", ...)
OCPP16.Phase // AC phase ("L1", "L2", "L1-N", ...)
OCPP16.Location // sampling location ("Inlet", "Outlet", ...)
OCPP16.Unit // value unit ("Wh", "kWh", ...)
OCPP16.Format // value format ("Raw" or "SignedData")
OCPP16.SampledValue // individual entry of the "sampledValue" array
OCPP16.MeterValue // individual entry of the "meterValue" array
The following types may be used to model value descriptors within
StatusNotification
Call messages:
OCPP16.Status // status ("Available", "Reserved", ...)
OCPP20.ConnectorStatus // connector status ("Available", "Occupied", ...)
OCPP20.ChargingState // charging status ("Charging", "EVConnected", ...)
OCPP21.ConnectorStatus // connector status ("Available", "Occupied", ...)
OCPP21.ChargingState // charging status ("Charging", "EVConnected", ...)
WARNING: experimental!
The
ChargingManager
class is experimental and unstable. It is only available in its OCPP 1.6 variant under the respective namespace, though the plan is to eventually provide implementations for OCPP 2.0 and OCPP 2.1.
A charging schedule is an array of charging periods, each defined by a start date, an end date and a set of charging limits:
export type ChargingSchedule = {
start: Date;
end: Date;
data: {
charging: {
min: number;
max: number;
phases: { qty: number; };
};
discharging: {
min: number;
max: number;
phases: { qty: number; };
};
shouldDischarge: boolean;
unit: 'W' | 'A';
},
}[]
The OCPP16.ChargingManager
class implements a repository of charging
profiles that can merge all of its entries into charging schedules computed
on-demand. Overlapping charging intervals will be merged together according to
the stack level and purpose of the respective profiles.
import { OCPP16, Models } from 'typed-ocpp';
// Instantiate the manager
const manager = new OCPP16.ChargingManager();
// Add a new profile by passing the payload of a SetChargingProfile call.
const setProfileCall = {} as OCPP16.SetChargingProfileCall;
manager.setChargingProfile(setProfileCall[3]);
// Clear profiles by passing the payload of a ClearChargingProfiles call.
const clearProfileCall = {} as OCPP16.ClearChargingProfilesCall;
manager.clearChargingProfile(clearProfileCall[3]);
// Get an absolute schedule for the entire station
const schedule = manager.getStationSchedule(
new Date(), // start date
new Date(Date.now() + 14_400_000), // end date
'W', // charging rate unit ("W" or "A")
new Models.ACChargingStation(230), // model used for unit conversions
);
// Get charging limits for the entire station at the given date
const limits = manager.getStationLimitsAtDate(
new Date(), // reference date
'W', // charging rate unit ("W" or "A")
new Models.ACChargingStation(230), // model used for unit conversions
);
// Get the charging schedule for connector 1
const schedule = manager.getConnectorSchedule(
new Date(), // start date
new Date(Date.now() + 14_400_000), // end date
1, // connector id
'W', // charging rate unit ("W" or "A")
new Models.DCChargingSession(400), // model used for unit conversions
);
// Get charging limits for connector 1 at the given date
const limits = manager.getConnectorLimitsAtDate(
new Date(), // reference date
1, // connector id
'W', // charging rate unit ("W" or "A")
new Models.DCChargingSession(400), // model used for unit conversions
);
// Get the composite charging schedule for connector 1, suitable to be used
// within OCPP16.GetCompositeScheduleCallResult messages
const schedule = manager.getConnectorCompositeSchedule(
new Date(), // start date
new Date(Date.now() + 14_400_000), // end date
1, // connector id
'W', // charging rate unit ("W" or "A")
new Models.DCChargingSession(400), // model used for unit conversions
);
// Uses the `getConnectorCompositeSchedule()` to get the composite schedule for
// connector 1 and return it as the payload for an OCPP16.SetChargingProfileCall
// message
const limits = manager.getConnectorCompositeProfile(
new Date(), // start date
new Date(Date.now() + 14_400_000), // end date
1, // connector id
'W', // charging rate unit ("W" or "A")
new Models.DCChargingSession(400), // model used for unit conversions
);
The OCPP16
, OCPP20
and OCPP21
namespaces export the official JSON Schema
documents provided by the OCPP Alliance as ready-to-use objects, slightly
tweaked to maximize compat with different versions and configurations of Ajv:
import { OCPP16, OCPP20, OCPP21 } from 'typed-ocpp';
OCPP16.schemas.AuthorizeRequest;
OCPP16.schemas.AuthorizeResponse;
/* ... */
OCPP20.schemas.AuthorizeRequest;
OCPP20.schemas.AuthorizeResponse;
/* ... */
OCPP21.schemas.AuthorizeRequest;
OCPP21.schemas.AuthorizeResponse;
/* ... */
See BUILD.md file.
MIT. See LICENSE file.