{"id":30754897,"url":"https://github.com/wfvining/ocpp","last_synced_at":"2026-01-30T16:59:12.574Z","repository":{"id":186854592,"uuid":"596805989","full_name":"wfvining/ocpp","owner":"wfvining","description":"Open Charge Point Protocol","archived":false,"fork":false,"pushed_at":"2025-02-24T00:30:31.000Z","size":284,"stargazers_count":11,"open_issues_count":1,"forks_count":1,"subscribers_count":6,"default_branch":"main","last_synced_at":"2025-09-04T10:22:49.469Z","etag":null,"topics":["electric-vehicles","erlang","erlang-otp","ocpp","ocpp201"],"latest_commit_sha":null,"homepage":"","language":"Erlang","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"apache-2.0","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/wfvining.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null,"zenodo":null,"notice":null,"maintainers":null,"copyright":null,"agents":null,"dco":null,"cla":null}},"created_at":"2023-02-03T00:44:59.000Z","updated_at":"2025-08-17T04:41:37.000Z","dependencies_parsed_at":"2023-08-08T01:57:10.700Z","dependency_job_id":"2e9487bc-255d-4fcc-8dc6-d2a80d994552","html_url":"https://github.com/wfvining/ocpp","commit_stats":{"total_commits":164,"total_committers":1,"mean_commits":164.0,"dds":0.0,"last_synced_commit":"6080d4f16ea75df9fb13585217a79fb77cee36b8"},"previous_names":["wfvining/ocpp"],"tags_count":0,"template":false,"template_full_name":null,"purl":"pkg:github/wfvining/ocpp","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/wfvining%2Focpp","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/wfvining%2Focpp/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/wfvining%2Focpp/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/wfvining%2Focpp/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/wfvining","download_url":"https://codeload.github.com/wfvining/ocpp/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/wfvining%2Focpp/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":28915942,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-01-30T16:37:38.804Z","status":"ssl_error","status_checked_at":"2026-01-30T16:37:37.878Z","response_time":66,"last_error":"SSL_connect returned=1 errno=0 peeraddr=140.82.121.5:443 state=error: unexpected eof while reading","robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":false,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["electric-vehicles","erlang","erlang-otp","ocpp","ocpp201"],"created_at":"2025-09-04T10:01:17.004Z","updated_at":"2026-01-30T16:59:12.548Z","avatar_url":"https://github.com/wfvining.png","language":"Erlang","funding_links":[],"categories":["Tools and Resources"],"sub_categories":["OCPP"],"readme":"# Open Charge Point Porotcol\n\nAn application implementating the Open Charge Point Protocol, version\n2.0.1. This application is designed to be used as the communication\nlayer for a charging station management system (CSMS). The application\nis designed to minimize, as much as possible, the implementation\nburden for a CSMS by handling requests automatically wherever possible\n(e.g. `StatusNotification` of `Heartbeat` messages). Furthermore, it\nendeavors to enforce compliant behavior with respect to the OCPP\nstandard; a CSMS implemented using this application should not need to\nbe concerned with message ordering or low-level semantics, only with\nthe business logic of managing charging stations.\n\n## Usage\n\nThe `ocpp` application doesn't do anything on its own. It is designed\nto be used as the protocol layer in release consisting of a websocket\nserver and a CSMS application. I'll try to provide a description of\nhow the `ocpp` application should be used in this context. The example\napplication has three layers. First there is a communication layer\nthat handles the interaction (via the websocket-based OCPP-j standard)\nbetween the charging stations and the CSMS. At the other end of the\nstack there is a CSMS application that provides a management interface\nfor use by a charging station/network operator. Between these two\nlayers sits the `ocpp` application. It's role is to act as a middle\nman between the two applications, enforcing the OCPP standard and\nmanaging a model of the state of the stations in the charging network.\n\n### Websocket layer\n\nThe websocket layer could be implemented using `cowboy` in a\n`websocket_handler`. The websocket handler needs to declare to the\n`ocpp` application that it is connected to the station by calling\n`ocpp_station:connect/1`. Once connected, the station process will\nforward any outgoing messages to the handler so they can be sent to\nthe station (more on this later). Whenever an OCPP-j RPC message is\nreceived it is the application's responsibility to decode the message\nand construct an `ocpp_message:message()` term. The `ocpp` application\nprovides library functions in the `ocpp_rpc`, `ocpp_message`, and\n`ocpp_error` modules to support this. Once the message is parsed it\ncan be forwarded to the `ocpp` application using the\n`ocpp_station:rpccall/2`, `ocpp_station:rpcreply/2`, or\n`ocpp_station:rpcerror/2` function that corresponds to the message\ntype. For example, using `cowboy`.\n\n```erlang\nwebsocket_handle(Frame = {text, Msg}, State) -\u003e\n    case ocpp_rpc:decode(Msg) of\n        {ok, {call, _MessageId, OCPPMessage}} -\u003e\n            ocpp_station:rpccall(State#state.station, OCPPMessage),\n            {ok, State};\n        {ok, {callresult, _MessageId, OCPPMessage}} -\u003e\n            ocpp_station:rpcreply(State#state.station, OCPPMessage),\n            {ok, State};\n        {ok, {callerror, _MessageId, OCPPError}} -\u003e\n            ocpp_station:rpcerror(State#state, OCPPError),\n            {ok, State};\n        {error, DecodeError} -\u003e\n            %% construct an ocpp error and send the appropriate\n            %% CALLERROR message to the station.\n            %% ...\n    end.\n```\n\nWhen the CSMS needs to send a message to the station (like a reply to\nan RPCCALL) it will send an Erlang message to the connected process\nwith the form `{ocpp, {rpcreply | rpcerror | rpccall, Message}}`. In\nthe cowboy example these can be handled in the `websocket_info/2`\ncallback.\n\n```erlang\n%% NOTE the ocpp_rpc api shown below is likely to change.\nwebsocket_info({rpcreply, Message}, State) -\u003e\n    {[{text, ocpp_rpc:callresult(\n                 ocpp_message:id(Message, ocpp_message:to_map(Message)))}],\n     State};\n%% ...\n```\n\n### OCPP layer\n\nThe main point to make about the `ocpp` layer is that the stations\nmust exist (i.e. have been started by the CSMS layer) prior to the\nwebsocket handler attempting to call `ocpp_station:connect/1`.\n\n### CSMS layer\n\nThe CSMS layer is much more nebulous than the websocket layer. It\nconsists of some application that starts processes for the stations\nthat are supposed to be managed using `ocpp_manager:add_station/3`\n(again, this API will probably changed—or at the very least be moved\ninto the `ocpp` module). This function serves two purposes. First, it\nspins up the processes needed to manage the station within the `ocpp`\napplication. Second, it installs a module implementing the\n`ocpp_handler` behavior as the handler for that station. The handler\nmodule is used, via callbacks, to inform the CSMS when OCPP messages\nthat require a response arrive and to inform the CSMS about certain\nimportant events. The callback api is fairly simple and consists of\njust a few required callbacks.\n\n```erlang\n-callback init(InitArg :: any()) -\u003e {ok, State :: any()} |\n                                    {error, Reason :: any()}.\n%% Initialize any internal state the handler will need when responding\n%% to requests.\n\n-callback handle_ocpp(MsgType :: ocpp_message:messagetype(),\n                      Message :: ocpp_message:message(),\n                      State :: any()) -\u003e\n    {reply, Message :: ocpp_message:message(), NewState :: any()} |\n    {error, Reason :: ocpp_error:error(), NewState :: any()} |\n    {noreply, NewState :: any()}.\n%% Handle a message sent by the charging station to the CSMS.\n\n-callback handle_info(Info, State :: any()) -\u003e\n    {ok, NewState :: any()}\n        when Info :: reboot_required |\n                     {report_received, ReportId :: integer()} |\n                     station_connected |\n                     station_disconnected |\n                     station_ready.\n%% Handle an event other than receipt of an OCPP message.\n```\n\nTo handle a BootNotificationRequest, for example, you would implement\n\n```erlang\nhandle_ocpp('BootNotification', Msg, State) -\u003e\n    Response = #{\n        currentTime =\u003e \u003c\u003c\"2023-10-08T19:41:33Z\"\u003e\u003e, %% TODO don't hardcode the time\n        status =\u003e \u003c\u003c\"Accepted\"\u003e\u003e,\n        interval =\u003e 3600\n    }\n    {reply, ocpp_message:new_response('BootNotification',\n     Response, ocpp_message:id(Msg))}.\n```\n\n## Architecture\n\nCharging stations are modeled in the application as a collection of\nprocesses, the primary process being the `ocpp_station` state\nmachine. This process indirectly manages all communication between the\nCSMS and the charging station. This module exposes an API that is used\nby the process that directly manages the websocket connection between\nthe CSMS and the charging station. All communication between the\n`ocpp_station` process and the websocket process takes place over\nErlang messaging and consists of Erlang representations of OCPP\nmessages.\n\nWhenever the station sends a message that cannot be handled\nautomatically the message is forwarded to an event manager which\nnotifies the CSMS that there is a message which requires its\nattention. Once the CSMS has processed the message it notifies the\n`ocpp_station` process what the response to the station should be.\n\n## The `ocpp_handler` behavior\n\nThe `ocpp_handler` behavior is the integration point between a CSMS\napplication and the `ocpp` application. It is used by CSMS\nimplementers to provide callbacks that are invoked for each OCPP\nmessage type. The values returned from the callbacks are used by the\n`ocpp_station` to construct the response message.\n\n## Future work\n\n- Websocket server\n- Client API\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fwfvining%2Focpp","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fwfvining%2Focpp","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fwfvining%2Focpp/lists"}