Push API & Websockets

Hi,

I'm back with my silly project on displaying statuses via Python websocket API so I can show something going on in my big screen attached totally unnecessarily to my OctoPi setup next to the printer.

However since I last made a progress on this API it did work and I managed to receive all GCODE commands via the push, but now I can't get it to work at all.

I saw @Fabian 's post on the authentication needed to be double-wrapped in json but even that does not work. I am also confused whether an additional header is needed for web-socket in top of the authentication message?

There was a reference to documentation on messaging @foosel directly in case someone is planning on using this API and get the latest plans. Well, here we go I guess :slight_smile:

So the question is: how to do the authentication with Python3 to the PUSH API via websocket using the websocket-client library and get the GCODE notifications (or anything for that matter) after socket has been initiated?

Cheers :slight_smile:

I can't help you with your question - I just want to inform you that @foosel is on vacation right now. She will be back approximately around the 10th of January. :slight_smile:

1 Like

I posted in the other thread, but I think you might just be missing the API auth process that needs to happen ahead of the websocket auth, in order to get a session key.

The code I used is public, you can see it at the following address:

Line 45 is the most important one where the authentication is done.

It's Javascript, and I haven't done anything there in a while, so I can't guarantee that this code works in recent Octoprint versions.

My experience is with a browser client, which reacts very sensitively to stuff like CORS or mixed content (using unencrypted websockets from an encrypted page). This should be less problematic in a Python client, but I'd still try both the ws: and the wss: version, in case one version is not allowed. You might also run into issues with self-signed certificates, in which case you either need to disable certificate validation in Python or use a certifate that your computer will regard as valid.

1 Like

Thanks for the heads up :slight_smile: Likewise I am on holiday until January, but in my case this is the time I have finally time for my hobby projects from work :smiley:

2 Likes

Wait what.....Foosel is a lady?
I totally did not know that!

lol, you've never watched an octoprint video...

3 Likes

1 Like

Here I am, back from my own holidays eager to see how my 3 new replies to the thread help me onwards with my problem, but all I see was a discovery of Foosel's gender :joy:

Anyway, couldn't get Fabian's code to work with my python version, so still something missing. Will give it another look next week.

Well, I put some effort to this now after sauna (which always helps), and managed to get this working!

Essentially I guess I was getting mixed with different tokens and in what form to send them, of which finally the following snippet worked.

Especially the format of auth_message was the key: use username + session token. Nothing else worked. I'm sure there is a prettier way but if someone else is stuck with this, here's one way of getting it work.

Cheers.

import websocket, json, requests

try:
    import thread
except ImportError:
    import _thread as thread
import time

HOST = "ws://octopi/sockjs/websocket"

def on_open(ws):
    auth_message = "{ \"auth\": \"xxx:"+session+"\" }"
    ws.send(auth_message)

def on_message(ws, message):
    print("Incoming message: ")
    print(message)

def on_error(ws, error):
    print(error)

def on_close(ws):
    print("Closing Socket...")

if __name__ == "__main__":
    logindata = { 'user': 'xxx', 'pass': 'yyy' }
    r = requests.post('http://octopi:80/api/login', json=logindata, verify=False)
    data = r.json()
    session = data['session']
    websocket.enableTrace(True)
    ws = websocket.WebSocketApp(HOST,
        on_open = on_open,
        on_message = on_message,
        on_error = on_error,
        on_close = on_close)
    ws.run_forever()```
2 Likes

I wanted to do something similar and spent a little bit of time looking into it, but decide to search if someone else had already done it and was very pleased to see this :grinning:. turns out I forgot the need to authenticate lol. so thanks!

Here is the prettier way how to send the auth message (works for me using this package https://github.com/websocket-client/websocket-client)

auth_message = json.dumps({"auth": f"{user}:{session}"})

I suggest setting the user name variable, so that you can use it here and later on when you post the logindata.

Did you have plans to send messages to the websocket? That's what I want to do next if I get time to look into.