mobilityhouse / ocpp

Python implementation of the Open Charge Point Protocol (OCPP).
MIT License
744 stars 291 forks source link

Rejected Trigger Messages #640

Closed leonard-voss closed 1 month ago

leonard-voss commented 2 months ago

Hello everyone, I'm currently working on a Python backend to read out a real wallbox from the management system's perspective. Since the wallbox only supports OCPP 1.6J, I rely on this version of the protocol. Receiving heartbeat, status and boot notifications works great. As far as I understand, I have to use the TriggerMessage feature to request the wallbox to send me the relevant data.

Unfortunately, my requests are always rejected.

I used the standard example for the management system and adapted it. I tested the TriggerMessage feature for several messages (BootNotification, StatusNotification, MeterValues) but they were all rejected. I also used prescribed spellings and checked them with or without connector_id.

Function within the ChargingStation:

async def send_trigger(self):
        print("\n--> SENDING TRIGGER NOTIFICATION\n")

        req = call.TriggerMessage(requested_message="StatusNotification", connector_id=1)

        '''
        Also tested:

        req = call.TriggerMessage(requested_message="StatusNotification")

        req = call.TriggerMessage(requested_message=Action.StatusNotification, connector_id=1)
        req = call.TriggerMessage(requested_message=Action.StatusNotification)

        req = call.TriggerMessage(requested_message= MessageTrigger.status_notification, connector_id=1)
        req = call.TriggerMessage(requested_message= MessageTrigger.status_notification)
        '''

        print("req", req)

        resp = await self.call(req)
        print("resp", resp)

        print("\nResponse status: " + resp.status + "\n")

        print("\n<-- ENDING TRIGGER NOTIFICATION\n")

Call within the on_connect function:

cp = ChargePoint(charge_point_id, websocket)

await asyncio.gather(cp.start(), cp.send_trigger())

The following output is generated (Loging: debugging)

Error-Message Rejected Trigger Message

Here you can find all the code used:

import asyncio
import logging
from datetime import datetime
import json
import ocpp.v16.call

try:
    import websockets
except ModuleNotFoundError:
    print("This example relies on the 'websockets' package.")
    print("Please install it by running: ")
    print()
    print(" $ pip install websockets")
    import sys

    sys.exit(1)

from ocpp.routing import on
from ocpp.v16 import ChargePoint as cp
from ocpp.v16 import call, enums
from ocpp.v16 import call_result
from ocpp.v16.enums import Action, RegistrationStatus, MessageTrigger

logging.basicConfig(level=logging.DEBUG)

class ChargePoint(cp):
    @on(Action.BootNotification)
    def on_boot_notification(
        self, charge_point_vendor: str, charge_point_model: str, **kwargs
    ):
        print("_________________________________________________\nboot notification\n_________________________________________________")
        return call_result.BootNotification(
            current_time=datetime.utcnow().isoformat(),
            interval=10,
            status=RegistrationStatus.accepted,
        )

    @on(Action.StatusNotification)
    def on_status_notification(self, connector_id: int, error_code: str, status:str, vendor_error_code: str, timestamp: str, **kwargs):
        print("_________________________________________________\nstatus notification\n_________________________________________________")
        return call_result.StatusNotification()

    @on(Action.Heartbeat)
    async def on_heartbeat(self):
        print("_________________________________________________\nheartbeat notification\n_________________________________________________")
        return ocpp.v16.call_result.Heartbeat(
            current_time=datetime.utcnow().strftime('%Y-%m-%dT%H:%M:%S') + "Z"
        )

    async def send_trigger(self):
        print("\n--> SENDING TRIGGER NOTIFICATION\n")

        req = call.TriggerMessage(requested_message="StatusNotification", connector_id=1)

        '''
        Also tested:

        req = call.TriggerMessage(requested_message="StatusNotification")

        req = call.TriggerMessage(requested_message=Action.StatusNotification, connector_id=1)
        req = call.TriggerMessage(requested_message=Action.StatusNotification)

        req = call.TriggerMessage(requested_message= MessageTrigger.status_notification, connector_id=1)
        req = call.TriggerMessage(requested_message= MessageTrigger.status_notification)
        '''

        print("req", req)

        resp = await self.call(req)
        print("resp", resp)

        print("\nResponse status: " + resp.status + "\n")

        print("\n<-- ENDING TRIGGER NOTIFICATION\n")

async def on_connect(websocket, path):
    """For every new charge point that connects, create a ChargePoint
    instance and start listening for messages.
    """
    try:
        requested_protocols = websocket.request_headers["Sec-WebSocket-Protocol"]
    except KeyError:
        logging.error("Client hasn't requested any Subprotocol. Closing Connection")
        return await websocket.close()
    if websocket.subprotocol:
        logging.info("Protocols Matched: %s", websocket.subprotocol)
    else:
        # In the websockets lib if no subprotocols are supported by the
        # client and the server, it proceeds without a subprotocol,
        # so we have to manually close the connection.
        logging.warning(
            "Protocols Mismatched | Expected Subprotocols: %s,"
            " but client supports  %s | Closing connection",
            websocket.available_subprotocols,
            requested_protocols,
        )
        return await websocket.close()

    charge_point_id = path.strip("/")

    cp = ChargePoint(charge_point_id, websocket)

    #await cp.start()
    await asyncio.gather(cp.start(), cp.send_trigger())

async def main():
    server = await websockets.serve(
        on_connect, "10.8.0.46", 9000, subprotocols=["ocpp1.6"]
    )

    logging.info("Server Started listening to new connections...")
    await server.wait_closed()

if __name__ == "__main__":
    # asyncio.run() is used when running this example with Python >= 3.7v
    asyncio.run(main())
OrangeTux commented 2 months ago

The code samples in examples/ don't contain a full fledged charger or CSMS. They only handle a single message.

Similar issues have been opened in the past, search for in the (closed) issues for more info.

leonard-voss commented 2 months ago

Hey, thanks for you quick response.

Sry, but I think I don't understand your answer correctly. Can you explain this a little bit further please?

For the CP, I'm using a real wallbox. So i think the CP-side implementation should be okay.

For the CS, I used the example in this library, but modified it with the implementation of the function, which you can see in my previous post. Is my function implementation setup correctly? Or do you have an working example for the TriggerMessage-call?

OrangeTux commented 2 months ago

I'm using a real wallbox.

Thanks for emphasizing that. I wrongly assumed you tested against your own CS.

In that case, the charger doesn't seem to support TriggerMessage, unfortunately.

leonard-voss commented 1 month ago

In that case, the charger doesn't seem to support TriggerMessage, unfortunately.

Update on the error:

I wrote to the manufacturer and they confirmed to me that the wallbox currently does not support Trigger Messages. Thanks for the hint! :)