https://github.com/aliml92/ocpp
Golang implementation of the Open Charge Point Protocol (OCPP).
https://github.com/aliml92/ocpp
client electric-vehicles ocpp ocpp16j ocpp16j-security server
Last synced: 12 days ago
JSON representation
Golang implementation of the Open Charge Point Protocol (OCPP).
- Host: GitHub
- URL: https://github.com/aliml92/ocpp
- Owner: aliml92
- License: mit
- Created: 2022-05-23T07:37:23.000Z (over 3 years ago)
- Default Branch: master
- Last Pushed: 2023-01-31T04:44:29.000Z (almost 3 years ago)
- Last Synced: 2025-09-04T10:22:54.877Z (5 months ago)
- Topics: client, electric-vehicles, ocpp, ocpp16j, ocpp16j-security, server
- Language: Go
- Homepage:
- Size: 278 KB
- Stars: 25
- Watchers: 2
- Forks: 2
- Open Issues: 0
-
Metadata Files:
- Readme: README.md
- Contributing: CONTRIBUTING.md
- License: LICENSE
Awesome Lists containing this project
- awesome-ev-charging - aliml92/ocpp
README
# ocpp
[](https://github.com/tterb/atomic-design-ui/blob/master/LICENSEs)
Golang package implementing the JSON version of the Open Charge Point Protocol (OCPP). Currently OCPP 1.6 and 2.0.1 is supported.
The project is initially inspired by [mobility/ocpp](https://github.com/mobilityhouse/ocpp)
## Installation
Go version 1.18+ is required
```bash
go get github.com/aliml92/ocpp
```
## Features
- [x] ocpp1.6 and ocpp2.0.1 support
- [x] logging
- [x] ping/pong customization on `WebSocketPingInterval`
- [x] server initiated ping activation
## Roadmap
- [ ] add unit/integration tests
- [x] improve logging
- [ ] add validation disabling feature
- [ ] add better queque implementation
## Usage
### Cental System (Server)
```go
package main
import (
"net/http"
"strings"
"time"
"go.uber.org/zap"
"github.com/aliml92/ocpp"
v16 "github.com/aliml92/ocpp/v16"
)
var csms *ocpp.Server
// log replaces standard log
var log *zap.SugaredLogger
func main()
logger, _ := zap.NewDevelopment()
log = logger.Sugar()
defer log.Sync()
// set ocpp library's logger to zap logger
ocpp.SetLogger(log)
// start csms server with default configurations
csms = ocpp.NewServer()
csms.AddSubProtocol("ocpp1.6")
csms.SetCheckOriginHandler(func(r *http.Request) bool { return true })
csms.SetPreUpgradeHandler(customPreUpgradeHandler)
csms.SetCallQueueSize(32)
// register charge-point-initiated action handlers
csms.On("BootNotification", BootNotificationHandler)
csms.After("BootNotification", SendChangeConfigration)
csms.On("Authorize", AuthorizationHandler)
csms.Start("0.0.0.0:8999", "/ws/", nil)
}
func SendChangeConfigration(cp *ocpp.ChargePoint, payload ocpp.Payload) {
var req ocpp.Payload = v16.ChangeConfigurationReq{
Key: "WebSocketPingInterval",
Value: "30",
}
res, err := cp.Call("ChangeConfiguration", req)
if err != nil {
log.Debug(err)
}
log.Debug(res)
}
func customPreUpgradeHandler(w http.ResponseWriter, r *http.Request) bool {
u, p, ok := r.BasicAuth()
if !ok {
log.Debug("error parsing basic auth")
w.WriteHeader(401)
return false
}
path := strings.Split(r.URL.Path, "/")
id := path[len(path)-1]
log.Debugf("%s is trying to connect with %s:%s", id, u, p)
if u != id {
log.Debug("username provided is correct: %s", u)
w.WriteHeader(401)
return false
}
return true
}
func BootNotificationHandler(cp *ocpp.ChargePoint, p ocpp.Payload) ocpp.Payload {
req := p.(*v16.BootNotificationReq)
log.Debugf("\nid: %s\nBootNotification: %v", cp.Id, req)
var res ocpp.Payload = &v16.BootNotificationConf{
CurrentTime: time.Now().Format("2006-01-02T15:04:05.000Z"),
Interval: 60 ,
Status: "Accepted",
}
return res
}
func AuthorizationHandler(cp *ocpp.ChargePoint, p ocpp.Payload) ocpp.Payload {
req := p.(*v16.AuthorizeReq)
log.Debugf("\nid: %s\nAuthorizeReq: %v", cp.Id, req)
var res ocpp.Payload = &v16.AuthorizeConf{
IdTagInfo: v16.IdTagInfo{
Status: "Accepted",
},
}
return res
}
```
`ChargePoint` represents a single Charge Point (CP) connected to Central System
and after initializing `*ocpp.Server` , register CP initiated call handlers using `csms.On` method.
Making a Call can be done by excuting `cp.Call` method.
### Charge Point (Client)
```go
package main
import (
"fmt"
"time"
"github.com/aliml92/ocpp"
v16 "github.com/aliml92/ocpp/v16"
"go.uber.org/zap"
)
var client *ocpp.Client
// log replaces standard log
var log *zap.SugaredLogger
// initialize zap logger
// for deveplopment only
func initLogger() {
logger, _ := zap.NewDevelopment()
log = logger.Sugar()
}
func main() {
initLogger()
defer log.Sync()
// set ocpp library's logger to zap logger
ocpp.SetLogger(log)
// create client
client = ocpp.NewClient()
id := "client00"
client.SetID(id)
client.AddSubProtocol("ocpp1.6")
client.SetBasicAuth(id, "dummypass")
client.SetCallQueueSize(32)
client.On("ChangeAvailability", ChangeAvailabilityHandler)
client.On("GetLocalListVersion", GetLocalListVersionHandler)
client.On("ChangeConfiguration", ChangeConfigurationHandler)
cp, err := client.Start("ws://localhost:8999", "/ws")
if err != nil {
fmt.Printf("error dialing: %v\n", err)
return
}
sendBootNotification(cp)
defer cp.Shutdown()
log.Debugf("charge point status %v", cp.IsConnected())
select {}
}
func ChangeConfigurationHandler(cp *ocpp.ChargePoint, p ocpp.Payload) ocpp.Payload {
req := p.(*v16.ChangeConfigurationReq)
log.Debugf("ChangeConfigurationReq: %v\n", req)
var res ocpp.Payload = &v16.ChangeConfigurationConf{
Status: "Accepted",
}
return res
}
Later use
func ChangeAvailabilityHandler(cp *ocpp.ChargePoint, p ocpp.Payload) ocpp.Payload {
req := p.(*v16.ChangeAvailabilityReq)
log.Debugf("ChangeAvailability: %v\n", req)
var res ocpp.Payload = &v16.ChangeAvailabilityConf{
Status: "Accepted",
}
return res
}
func GetLocalListVersionHandler(cp *ocpp.ChargePoint, p ocpp.Payload) ocpp.Payload {
req := p.(*v16.GetLocalListVersionReq)
log.Debugf("GetLocalListVersionReq: %v\n", req)
var res ocpp.Payload = &v16.GetLocalListVersionConf{
ListVersion: 1,
}
return res
}
func sendBootNotification(c *ocpp.ChargePoint) {
req := &v16.BootNotificationReq{
ChargePointModel: "client00",
ChargePointVendor: "VendorX",
}
res, err := c.Call("BootNotification", req)
if err != nil {
fmt.Printf("error dialing: %v\n", err)
return
}
fmt.Printf("BootNotificationConf: %v\n", res)
}
func sendAuthorize(c *ocpp.ChargePoint) {
req := &v16.AuthorizeReq{
IdTag: "safdasdfdsa",
}
res, err := c.Call("Authorize", req, 10)
if err != nil {
fmt.Printf("error dialing: %v\n", err)
return
}
fmt.Printf("AuthorizeConf: %v\n", res)
}
```
After creating `*ocpp.Client` instance, register CS (Central System) initiated call handlers.
Making a call to CS is same as the above snippet where just call `cp.Call` method.
## Contributing
Contributions are always welcome!
Implementing higher versions of ocpp is highly appreciated!
See `CONTRIBUTING.md` for ways to get started.