ocpp
ocpp copied to clipboard
websocket for an ASGI server?
Hello, I'm started to work with starlette. And like they say:
Starlette is a lightweight ASGI framework/toolkit, which is ideal for building high performance asyncio services.
but with ocpp we need the standard websocket library, so I'm searching on what part of websocket
library are in use with ocpp and the only two functions that I'm found are recv
and send
so I did writing an interface to satisfy this with starlette-websocket
:
# websocketInterface.py file
from starlette.websockets import WebSocket
class WebSocketInterface():
def __init__(self, websocket: WebSocket):
self._websocket = websocket
async def recv(self):
receive_msg = await self._websocket.receive_text()
return receive_msg
async def send(self, text_message):
await self._websocket.send_text(text_message)
now this object can be use like an standard websocket
object, for example:
# simple starlette server.py file
import asyncio
from starlette.applications import Starlette
from websocketInterface import WebSocketInterface
from central_system import on_connect
app = Starlette()
@app.websocket_route('/{client_id:path}')
async def websocket_handler(websocket):
interface = WebSocketInterface(websocket)
await websocket.accept(subprotocol='ocpp1.6')
await on_connect(interface, websocket.path_params['client_id'])
await websocket.close()
this is with blob/master/examples/v16/central_system.py. Finally we can run this app with all python
ASGI server (for example uvicorn):
myuser@mymachine:~: uvicorn --port 9000 server:app
What do you think? with some like this you could user straightforward all ASGI frameworks?
Thanks for your effort @lsaavedr!
This library tries not to be coupled to a specific implementation of websockets. Exactly for this reason: that you choose whatever implementation you want.
You correctly figured out that a websocket implementation should implement following interface:
- ~
async def recv() -> bytes
~async def recv() -> str
- ~
async def send(msg: bytes)
~async def send(msg: str)
As you've shown it's easy to create a small wrapper around a websocket implementation which API is slightly different. In fact I've used this library successfully with Quart.
I think your code can be part of the documentation. What do you think?
mmm... the frame type are bytes or text? because if are bytes the interface must be:
# websocketInterface.py file
from starlette.websockets import WebSocket
class WebSocketInterface():
def __init__(self, websocket: WebSocket):
self._websocket = websocket
async def recv(self):
receive_msg = await self._websocket.receive_bytes()
return receive_msg
async def send(self, msg: bytes):
await self._websocket.send_bytes(msg)
are you sure?
I'm testing and with text frame type are ok, but with byte frame type fail on:
File ".../envSc/lib/python3.7/site-packages/ocpp/charge_point.py", line 122, in start
message = await self._connection.recv()
File "./websocketInterface.py", line 49, in recv
receive_msg = await self._websocket.receive_bytes()
File ".../envSc/lib/python3.7/site-packages/starlette/websockets.py", line 92, in receive_bytes
return message["bytes"]
KeyError: 'bytes'
You're right. The interface operates on str
instead of bytes
.
now I have a big doubt... the ocpp1.6 standard are websocket with text frames or binary frames :'(
now I have a big doubt... the ocpp1.6 standard are websocket with text frames or binary frames :'(
OCPP 1.6J defines the use of UTF-8 for character encoding, so in the end binary frames are used
yes, in the ocpp1.6-j specification (section 4.1.2) say that MUST be UTF-8 characters then the websocket frame type is text! thanks!!!
yes, in the ocpp1.6-j specification (section 4.1.2) say that MUST be UTF-8 characters then the websocket frame type is text! thanks!!!
Yes, sorry, I meant text :P
A more complete interface with close and exceptions:
# websocketInterface.py
from starlette.websockets import WebSocket, WebSocketDisconnect
from websockets.exceptions import ConnectionClosed
# transform starlette websocket to standard websocket
class WebSocketInterface():
def __init__(self, websocket: WebSocket):
self._websocket = websocket
async def recv(self) -> str:
try:
return await self._websocket.receive_text()
except WebSocketDisconnect as e:
raise ConnectionClosed(e.code, 'WebSocketInterface')
async def send(self, msg: str) -> None:
await self._websocket.send_text(msg)
async def close(self, code: int, reason: str) -> None:
await self._websocket.close(code)
regards!
I don't see this as part of the official documentation, but I would strongly support the initiative, I think more and more people are going to be interested in using a different websocket implementation. BTW, great work, so glad to have found it. thanks
I was also looking for this. I'm looking to integrate this into FastAPI since it's built on top of Starlette. Great work !