Closed peteh closed 4 weeks ago
Edit: actually there are routes in the Zwift API, https://us-or-rly101.zwift.com/relay/dropin
returns DropInWorldList and https://us-or-rly101.zwift.com/relay/worlds/1/players/<player_id>
returns PlayerState.
Hi. There isn't an API but since it's open source you can implement something directly in zoffline. The length of the online dictionary tells you how many players are currently connected to the UDP server and online[player_id]
contains the last PlayerState received from that player. I suppose you can check speed to know if the player is moving or heartrate if you want to set the fan speed accordingly.
If you want to control the fan from another app, a possibility would be adding something like this in CDNHandler do_GET() to expose the values through HTTP
if self.path == '/players':
self.send_response(200)
self.send_header('Content-type', 'application/json')
self.end_headers()
output = json.dumps(list(online.keys()))
self.wfile.write(output.encode())
return
if self.path.startswith('/player/'):
try:
player_id = int(self.path.split('/')[-1])
if player_id in online:
self.send_response(200)
self.send_header('Content-type', 'application/json')
self.end_headers()
from google.protobuf.json_format import MessageToDict
output = json.dumps(MessageToDict(online[player_id]))
self.wfile.write(output.encode())
return
except:
pass
GET http://127.0.0.1/players
[1]
GET http://127.0.0.1/player/1
{"id": "1", "worldTime": "307330679646", "distance": 0, "roadTime": 991632, "laps": 0, "speed": 0, "roadPosition": 10484187, "cadenceUHz": 0, "psF10": 0, "heartrate": 0, "power": 0, "heading": "1384499", "lean": "1000000", "climbing": 0, "time": 0, "f19": 983060, "aux3": 33554447, "progress": 0, "justWatching": true, "calories": 0, "x": 4377.258, "yAltitude": 18546.525, "z": -9741.832, "watchingRiderId": "1", "groupId": "0", "sport": "CYCLING", "distLat": 129.20082, "world": 15, "psF36": 0, "psF37": 0, "canSteer": false, "route": -930393161}
Ah that would be perfect. I could even adjust light settings based on current power
Small update, for me the online
variable is always empty. I tried both in docker and standalone on a linux machine but it seems to never be update. Even when I ride.
Make sure server-ip.txt is correct.
You should see this in Zwift log.txt (Documents/Zwift/Logs) with the server IP
[INFO] TCP host 192.168.0.109:3025 (secure)
[INFO] Connecting to TCP server...
[INFO] Saying hello to TCP server (largest wa is 0)
[INFO] UDP host 192.168.0.109:3024 (secure)
[INFO] Connecting to UDP server with relay id 1...
The online dict is populated when the client connects to the UDP server.
Ah thank you very much, that works.
Does it make any difference if I add the api to the cdn or to the swift api server? The latter can specify routes easier within the flask app.
I would add something like /api_ext/players/[player_id]
Another question on the side: When switching from single to multiplayer, is there anything to be done with the profile? It looks like I cannot log into the single player account when switching to multiplayer via multiplayer.txt
Does it make any difference if I add the api to the cdn or to the swift api server? The latter can specify routes easier within the flask app.
Not much, but flask already has it (I edited my first reply), /relay/dropin
contains a list of online riders and /relay/worlds/1/players/<player_id>
contains the last player state.
When switching from single to multiplayer, is there anything to be done with the profile? It looks like I cannot log into the single player account when switching to multiplayer via multiplayer.txt
When multiplayer is enabled you need to sign up (/signup
). The first account will be profile 1 which is the default in single player mode. Also notice that if multiplayer is enabled you need to login (POST username and password to /login
) to have access to /relay/dropin
.
API Looks like the existing points are not json but Protobuf. So either I add a ?encode=json parameter or add new endpoints. But this seems doable.
Users I have a single player profile which I cannot log into when switching to multiplayer. Can I create a new profile and then upload the profile.bin to make it work?
Looks like the existing points are not json but Protobuf. So either I add a ?encode=json parameter or add new endpoints. But this seems doable.
Dropin already has a json response if "Accept" header is "application/json" (https://github.com/zoffline/zwift-offline/blob/master/zwift_offline.py#L2993). The other route only returns protobuf but you can decode in your script. If there isn't protobuf library for the language you are using, you can run command line protoc, for instance:
curl -k https://us-or-rly101.zwift.com/relay/worlds/1/players/1 -o 1.bin
protoc --decode PlayerState udp-node-msgs.proto < 1.bin
Can I create a new profile and then upload the profile.bin to make it work?
Yes, or you can rename your current profile folder in storage to 1.
Dropin example for multiplayer:
curl -k -c cookie.txt -F username=user@email.com -F password=xxx https://us-or-rly101.zwift.com/login/
curl -k -b cookie.txt https://us-or-rly101.zwift.com/relay/dropin -o dropin.bin
protoc --decode DropInWorldList world.proto < dropin.bin
or
curl -k -b cookie.txt https://us-or-rly101.zwift.com/relay/dropin | protoc --decode DropInWorldList world.proto
curl -k -b cookie.txt https://us-or-rly101.zwift.com/relay/worlds/1/players/1 | protoc --decode PlayerState udp-node-msgs.proto
Hi
I'm trying to use this server to see if Zwift is running and connected.
Use case: When I start the app, I want to start the fan too.
Idea: use one of the zoffline server's api endpoints to see if somebody is connected.
Does such an api endpoint exist that could be used?