EV charging station (OCPP protocol)

Hello,

A more discussion-oriented topic to start with and gauge interest: would an « EV Charging Station » integration be of interest to anyone?

Several questions then arise from this project:

  • OK, but for what? Initially, I think it would mainly be for « data consumption »… but maybe that’s not enough?
  • OK, but what kind of integration?
    • Fully integrated with Gladys: Gladys hosts the code on its side… not sure if that’s a great idea, but why not in absolute terms. Maybe it’s the most integrated in the end?
    • In ocpp2mqtt mode: a bit like Zigbee and Z-Wave: we rely on another project that acts as a CSMS and publishes messages on an MQTT broker. The broker can also be used to send commands to the charging station. In this mode, the charging station no longer communicates with the station’s native cloud server.
    • In Relay 2 mqtt mode: in this mode, a program acts as an intermediary to transmit messages to the MQTT broker while allowing messages to pass to the native cloud. Gladys can, a priori, only receive messages, not send them to the charging station (in absolute terms, yes, but desynchronized with the cloud CSMS… not sure about the impacts).
    • Via Node-RED? In which case, there’s nothing really to do in Gladys, more of a tutorial question? (I just thought of this solution while writing the post, I admit)

I’ve started some initial research, I’m still having a bit of trouble identifying everything that could be done with the protocol (knowing that there are 2/3 versions of it). Receiving data seems more or less OK. It’s more in sending commands that I haven’t dug into what can be done, but in principle, why not. Hence this post: are people interested? What information can be retrieved? What commands can be pushed?

For the different options, there are more or less different ways to proceed:

That’s it, I don’t have answers, I don’t have a completely clear solution yet… I’m just gauging interest to see if there’s any.

Hi @Sescandell!
I’m sure this would interest people, and I think it’s an excellent candidate for future external integrations in Gladys :blush:

Hello,

I’m really excited about trying this approach, even just for a simple POC initially.

I have a device available… I’ll start exploring what’s feasible outside of any Gladys context first to get familiar with everything.

If anyone has knowledge of this protocol, speak up, I’m all ears :wink:

That’s a valuable idea. Rather than fully integrating it into Gladys right away, connecting via OCPP-to-MQTT might be more practical; it facilitates testing and allows for the initial implementation of features like charging status, power output, energy levels, and start/stop control.

Deeper native integration can be considered once the specific usage requirements become clear.

Quick progress report (I’m still being careful with this, I don’t really want to fry the charger… or the car… or the house :smiley: )

At this stage, I have a relatively simple loop, outside of Gladys for the moment. But thanks to the project GitHub - gyzod/ocpp2mqtt: OCPP <==> MQTT Gateway · GitHub & GitHub - ocpp-balanz/ocpp-2w-proxy: A 2 way OCPP proxy · GitHub I was able to set up a web interface that can show me the real-time status of my charger.
The idea behind the ocpp-2w-proxy project, as its name suggests, is to act as a proxy (a kind of Man In The Middle) between the charger and the native Cloud server.

This allows me to keep the control via the native app. In my case, I notice that my Autel Charger app remains 100% functional (from what I’ve been able to test so far…). My MQTT also receives all messages. This allows me to update a fake web interface for the moment.

My problem is with starting a charge. Changing the configuration, stopping, refreshing: everything works. But starting a recharge, for the moment I’m stuck.

Perhaps the first step will be to make sure I can see the status of the charger. We can then dig into what’s not working.

Where I also have some reservations is that I had to modify the source code of the referenced projects for it to work. I’ll double-check this point, it might be a mistake on my part.

Nothing related to Gladys for the moment, but once I’m sure it’s OK: go for the plugin :wink:

If I were you, I’d run Claude on the topic with all the information from this post :slight_smile:

In 30 minutes, you have an external integration with Gladys that has a good chance of working right away on the first try, testable with one click in Gladys, and you can iterate from there.

End to end, I think that today an external integration takes less than an hour of work, without needing to dive into the API or the code.

On my end, the results I get are really clean, often better than what a human developer would do because all edge cases are well managed, it’s worth a try :smiley:

It’s not so much a matter of speed or ease of use with the APIs: that step isn’t a concern yet (this point won’t worry me, it will be quickly handled by AI).

The issue here is to really understand the tool we’re putting around and to really understand/master what’s being done: the capabilities and the organization between occp-2w-prxy and ocpp-2mqtt (and whether these are the right tools after all. Specifically, the start limitation, I wonder if the native HA plugin has the same issue or not: lbbrhzn/ocpp: Home Assistant integration for electric vehicle chargers that support the Open Charge Point Protocol (OCPP).)

That’s the priority for now. Once all this is squared away, I’ll create the Gladys plugin, I think it will go quickly :wink:

@Sescandell Why use ocpp-2w-prxy and ocpp-2mqtt?

Couldn’t the AI just rewrite the entire stack directly into the Gladys integration? :slightly_smiling_face: This way, we’d avoid being dependent on these external projects while keeping control over the implementation, updates, and bug fixes. It would also offer more flexibility in the long run, right?

This was one of the possible tracks also mentioned in the initial post. Are we in agreement that when you say « all this stack in Gladys » we’re still on the idea of an « external plugin via the API »?

The question boils down to why look to redevelop something that already exists. I’m exaggerating, but basically you’re asking: why choose zigbee2mqtt instead of redeveloping it? If solutions on the market do the job, why want to re-implement them. Am I misinterpreting the question?

If these two projects do the job, we might as well not do without them. If they are limiting, I’ll look into a home implementation. In the absolute, I almost want to say, as an « external plugin » it’s an « implementation detail »?

I don’t have a definitive opinion on the question yet. As far as I’m concerned, I’m still in the exploration phase of the possible. I think this weekend I’ll work on this point to push an integration.

Yes, that is indeed the question. :slightly_smiling_face: But when I look at ocpp-2mqtt, I see a relatively simple project that evolves very little.

The repository has only 90 commits, and a large part of them are related to documentation or CI updates. In the end, there isn’t much business logic.

ocpp-2w-proxy is even worse, with only 19 commits and the last update being 5 months ago.

This is exactly the kind of project that an AI can rewrite in about thirty minutes today. This way, we avoid dependency on a third-party project that evolves little, while keeping control of the code and the ability to evolve it or quickly fix bugs.

On the other hand, Zigbee2MQTT is on a completely different scale: over 6,400 commits, hundreds of contributors, and very active development. Here, there is real accumulated expertise and a huge compatibility base with Zigbee devices. In this case, it is much more relevant to rely on their work rather than trying to reimplement everything.

I haven’t ruled out this possibility. I was thinking in terms of a fork, but we could also imagine starting « from scratch ».
I’m testing the HA solution to see if the issue I’m identifying is specific to my device or the library, and here’s what I found: +1: +1: +1:

The system is taking shape

But I need to review the operation and configuration management.
I’ll work on this over the weekend :wink:

So you went with an additional container mode? Honestly, that’s clearly overkill in my opinion, you could have done it natively, a shame to have a dependency :slight_smile:

So either I’m missing something in the documentation, or you’re not fully aware of how OCPP works.
For the OCPP integration, we need a new WebSocket to be available. The charging stations connect to this WebSocket. The idea behind the integration is to act as a Man In The Middle to see what’s happening. Otherwise: it’s total confusion.

From the documentation, I read three things:

  • the manifest fields: which don’t mention port exposure capabilities
  • the security model which explains that the Docker lives in an isolated Network
  • companion containers: which are there to (among other things) create protocol bridges… for me, this is exactly the case here

So either I’m missing information, the main container can perfectly open ports and make them available on the network, and in that case, I agree with you, tell me how to do it and I’ll modify it without any issues. Or it’s not possible and therefore it’s not overkill :wink:

You’re right, I spoke nonsense :smiley:

I double-checked the spec: the main container of an external integration intentionally has no published ports, its only incoming channel is the outgoing WebSocket to Gladys. Published ports only exist on the sub-containers declared in containers[]. So for OCPP, where it’s the charging station that connects as a WS client, the companion container is indeed the only option today. Not overkill at all :smiley:

After that, nothing forces you to use a third-party image in the sub-container, containers[].docker_image also accepts yours. You can therefore declare a sub-container that runs your own OCPP server if you ever want to limit external dependencies!

That’s exactly what I implemented: GitHub - sescandell/gladys-ocpp-integration: Gladys OCPP EV Charger External integration. · GitHub

Perfect! :raising_hands:

If you ever notice any limitations in the current system, don’t hesitate, everything can evolve :slight_smile:

Hello @pierre-gilles, I have a question/comment,

I am « limited » in the ability to integrate the feature via the SDK. And the user experience may not be completely satisfactory.

My problem lies in the « Discovery » / « Configuration » / « Actions » part.

A quick explanation of how OCPP works: users must actually modify the URL to which their charging station connects (from their native Cloud application of the platform). By doing this, the charging station naturally expects a response from the endpoint it connects to. The goal of the targeted integration is that the user’s native app remains usable. The integration positions itself as a RELAY. It looks at what is happening, transmits the useful information to Gladys, and relays the message to the original cloud server.

And that’s the issue. I need a way to, from the integration interface, be able to say « Such a charging station must point to such a Cloud service ». What I do today is that the integration presents the URL to be entered in the app so that the charging station targets Gladys. It responds with « fake » data to ensure that the charging station remains connected. Thus, the integration « knows the identity of the charging station ».

The idea would then be to be able to configure this connected charging station (define the Cloud URL from Gladys). Except that - unless I’m missing the info in the doc’ it’s possible - I have no way to « present the discovered charging stations and act on configurations ».

Today the only thing I can do is an Action that requests the charging station ID + Cloud URL. But not linked to the « identified charging stations ». It’s static. It works… but it’s not great. The ID must be searched in the logs of the companion container. It’s not the easiest (while I have the info of the available IDs in memory). Do you understand what I’m trying to explain?

Do you think we can work on this point (I can imagine releasing a V1 of the plugin - subject to merging the dedicated PR on the Core side on the new CATEGORY and TYPES) with a « configuration by actions » mode, but there’s clearly something to be done I would say.

Your opinion on the matter?

If I’m not clear, let me know, I’ll rephrase with diagrams :slight_smile:

Thanks!

Hi @Sescandell,

Thanks for this detailed feedback, it’s exactly the kind of concrete case that helps us evolve the SDK.

However, I think there’s a misunderstanding about what the SDK already allows, because your case is normally covered without having to read the container logs:

1. When a charging station connects to your OCPP server, you know its ID (it’s in the connection URL). At this moment, you can publish it via POST /discovered_device. It then appears in the “Discovery” tab of your integration, with its name, and the user only has to click on “Create”. It’s the same pattern as internal integrations (Zigbee2MQTT for example), and your case is even simpler than a network scan since the discovery is incoming: the charging station comes to you.

2. For per-charging station configuration (the manufacturer’s cloud URL to relay), you can declare an action in your manifest with a select field that has « source »: « devices ». The form is then automatically populated with the devices from your integration (label = charging station name), and you receive the chosen external_id as any other field value. The user selects their charging station from a list, they never copy an identifier.

3. And when you republish an already created charging station with updated params (relayed URL, detected protocol version, etc.), they are silently updated in the database without touching the name or the features.

So the complete V1 journey is: the charging station connects to your relay, you publish it in discovery, the user creates it from the UI, then they configure the cloud URL via your action with the select. Zero log consultation.

One point of attention given your architecture: if your OCPP server runs in the sub-container, it’s the main integration container that holds the token to call the host API. Therefore, your sub-container needs to report the connection info to it (via your private network) so that it can publish the discovery.

Where you’re right is that today there’s a lack of persistent and device-visible configuration: an action is write-only, the user doesn’t see which URL is configured for each charging station. This is a need we had already identified for phase 2 (actions at the device level, directly on its card), and your feedback confirms that the need is real. But I prefer that we move forward on this once your V1 is out with the current building blocks, to design it with hindsight and other use cases than yours.

If one of the three points above doesn’t work in your case, let me know, it would mean there’s either a bug or a gap in the SDK documentation.

I had overlooked the concept of source devices. This will improve the experience a bit, but I think there will still be something to improve. I’ll do that and let you know what it gives. The rest already matches what’s in place.

Thanks, I’ll keep you informed.