Skip to content

A library for type-aware validation of OCPP 1.6, OCPP 2.0.1 and OCPP 2.1 messages, built against the official JSON Schema documents published by the Open Charge Alliance.

License

Notifications You must be signed in to change notification settings

jacoscaz/typed-ocpp

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

typed-ocpp

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.

Table of Contents

Usage

OCPP16, OCPP20, OCPP21 namespaces

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.

validate()

The validate() function is a user-defined, validating type guard which returns trueif 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);
}

validateCall(), validateCallError() and validateCallResult()

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().

isCall(), isCallResult() and isCallError()

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
  }
}

checkCallResult()

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 originating OCPP16.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.

Types

Primary types

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
/* ... */

Types for specific messages

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 ...*/

The CheckedCallResult<C extends Call> type

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,
  },
];

Utility enums

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", ...)

Message types SEND and CALLRESULTERROR in OCPP 2.1

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.

Utility types for OCPP 1.6

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", ...)

Utility types for OCPP 2.0

OCPP20.ConnectorStatus      // connector status ("Available", "Occupied", ...)
OCPP20.ChargingState        // charging status ("Charging", "EVConnected", ...)

Utility types for OCPP 2.1

OCPP21.ConnectorStatus      // connector status ("Available", "Occupied", ...)
OCPP21.ChargingState        // charging status ("Charging", "EVConnected", ...)

ChargingManager class

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
);

JSON Schema(s)

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;
/* ... */

Building and testing

See BUILD.md file.

License

MIT. See LICENSE file.

About

A library for type-aware validation of OCPP 1.6, OCPP 2.0.1 and OCPP 2.1 messages, built against the official JSON Schema documents published by the Open Charge Alliance.

Topics

Resources

License

Stars

Watchers

Forks