Chrissys random stuff.

// 2025-10-30 // by chrissy

A small Python-Script for getting information for your Trip with Deutsche Bahn in your i3blocks Status-Bar

When taking a journey with Deutsche Bahn’s Intercity or Intercity-Express trains one may notice the travel information provided in the ICE Portal. This Website only works as intended when one’s device is connected to the WifionICE Wifi in the train and just then provides several information for your current trip, for instance the next station or the current speed.

As an average i3 user, I wanted to have this information available in my status bar. At least since I saw a similiar feature on someone elses status bar. Luckily I am using i3blocks as a status command for my i3bar. So I only need a small script providing my desired information via standard output. So I wrote a small Python script to obtain the wanted data from ICE-Portal’s API and merging it to a string depictable by i3blocks.

This script is avilable from my Gitea: https://gitea.dm2lct.radio/chrissy/i3blocks_TravelInfo and may be used for whatever you would like to use it. Since I lack in programming there will surely be some bugs.

Which stuff from where? | Short look at the data obtained via the ICE-Portal API.

This section should give a brief overview over the data one can obtain via the given API used in the ICE-Portal. The Webside uses a REST-API and provides us, when asked a JSON-formatted String with the precious information we crave for. When investigating the ICE-Portal one may find at least two URLs of interest. The first one contains in addition to the current geoposition the current speed and some other stuff, that may be interesting. I just use it for getting the current speed. A quick request via curl will get an unformatted output, so we pipe it into jq to accomplish a more friendly to read readout:

curl -s https://iceportal.de/api1/rs/status | jq

{   
  "connection": true,
  "serviceLevel": "AVAILABLE_SERVICE",
  "gpsStatus": "VALID",
  "internet": "HIGH",
  "latitude": 51.6378456667,
  "longitude": 13.5727226667,
  "tileY": 181,
  "tileX": 283,
  "series": "401",
  "serverTime": 1761832820434,
  "speed": 141.5,
  "trainType": "ICE",
  "tzn": "ICE0159",
  "wagonClass": "SECOND",
  "connectivity": {
    "currentState": "HIGH",
    "nextState": "MIDDLE",
    "remainingTimeSeconds": 600
  },
  "bapInstalled": true
}

The other one containes the train route data, all planned stations, planned and actual arrival times, delays and slightly more data which could be from interest. It should be noted, that the next station is displayed with its “Interne Bahnhofsnummer. With this number one can now pick out the correct “station” block, where the evaNr within that particular block matches the evaNr stored in the variable “actualNext”.

Once figured out which block to use, one can now draw all desired data for the next station(s). One thing remains to be mentioned: The given timestamps are specified as Unix time but in this particular case they are given in milliseconds for whatever purpose:

curl -s https://iceportal.de/api1/rs/tripInfo/trip | jq

{   
  "trip": {
    "tripDate": "2025-10-30",
    "trainType": "ICE",
    "vzn": "2544",
    "actualPosition": 2882,
    "distanceFromLastStop": 83069,
    "totalDistance": 169650,
    "stopInfo": {
      "scheduledNext": "8010255",
      "actualNext": "8010255",
      "actualLast": "8010089",
      "actualLastStarted": "8010255",
      "finalStationName": "Berlin Hbf",
      "finalStationEvaNr": "8011160"
    },
    "stops": [
      {
        "station": {
          "evaNr": "8010085",
          "name": "Dresden Hbf",
          "code": null,
          "geocoordinates": {
            "latitude": 51.040563,
            "longitude": 13.732035
          }
        },
        "timetable": {
          "scheduledArrivalTime": null,
          "actualArrivalTime": null,
          "showActualArrivalTime": null,
          "arrivalDelay": "",
          "scheduledDepartureTime": 1761829020000,
          "actualDepartureTime": 1761829414000,
          "showActualDepartureTime": true,
          "departureDelay": "+6"
        },
        "track": {
          "scheduled": "12",
          "actual": "1"
        },
        "info": {
          "status": 0,
          "passed": true,
          "positionStatus": "passed",
          "distance": 0,
          "distanceFromStart": 0
        },
        "delayReasons": null
      },
      {
        "station": {
          "evaNr": "8010089",
          "name": "Dresden-Neustadt",
          "code": null,
          "geocoordinates": {
            "latitude": 51.065899,
            "longitude": 13.740701
          }
        },
        "timetable": {
          "scheduledArrivalTime": 1761829260000,
          "actualArrivalTime": 1761829728000,
          "showActualArrivalTime": true,
          "arrivalDelay": "+7",
          "scheduledDepartureTime": 1761829380000,
          "actualDepartureTime": 1761829888000,
          "showActualDepartureTime": true,
          "departureDelay": "+8"
        },
        "track": {
          "scheduled": "6",
          "actual": "6"
        },
        "info": {
          "status": 0,
          "passed": true,
          "positionStatus": "departed",
          "distance": 2882,
          "distanceFromStart": 2882
        },
        "delayReasons": null
      },
      {
        "station": {
          "evaNr": "8010255",
          "name": "Berlin Ostbahnhof",
          "code": null,
          "geocoordinates": {
            "latitude": 52.5104881,
            "longitude": 13.4346807
          }
        },
        "timetable": {
          "scheduledArrivalTime": 1761838320000,
          "actualArrivalTime": 1761838320000,
          "showActualArrivalTime": true,
          "arrivalDelay": "",
          "scheduledDepartureTime": 1761838740000,
          "actualDepartureTime": 1761838740000,
          "showActualDepartureTime": true,
          "departureDelay": ""
        },
        "track": {
          "scheduled": "3",
          "actual": "3"
        },
        "info": {
          "status": 0,
          "passed": false,
          "positionStatus": "future",
          "distance": 162050,
          "distanceFromStart": 164932
        },
        "delayReasons": null
      },
      {
        "station": {
          "evaNr": "8011160",
          "name": "Berlin Hbf",
          "code": null,
          "geocoordinates": {
            "latitude": 52.525592,
            "longitude": 13.369545
          }
        },
        "timetable": {
          "scheduledArrivalTime": 1761839160000,
          "actualArrivalTime": 1761839160000,
          "showActualArrivalTime": true,
          "arrivalDelay": "",
          "scheduledDepartureTime": null,
          "actualDepartureTime": null,
          "showActualDepartureTime": null,
          "departureDelay": ""
        },
        "track": {
          "scheduled": "13",
          "actual": "13"
        },
        "info": {
          "status": 0,
          "passed": false,
          "positionStatus": "future",
          "distance": 4718,
          "distanceFromStart": 169650
        },
        "delayReasons": null
      }
    ]
  },
  "connection": {
    "trainType": null,
    "vzn": null,
    "trainNumber": null,
    "station": null,
    "timetable": null,
    "track": null,
    "info": null,
    "stops": null,
    "conflict": "NO_CONFLICT"
  },
  "active": null
}

Patch it up to a small script

Now it is easy to write a small Python script to generate a string, we one can display in i3blocks. As I am writing this documentation, it consists of no more than 69 lines of Python code.

Check SSID

This is a function to get the current SSID to get the SSID of the current WIFI. The purpose of this function is to figure out if the connected SSID matches “WIFIonICE”. And just then the GET Request is executed otherwise the script will do nothing and exit.

get API DB

This function obtaines the raw data via the ICE-Portal API and stores it in two global available variables: statusData for the Status request and tripData for the tripInfo request. These variables now can be handled as a Python Dictionary.

get speed DB

Returns the current speed from the variable statusData. So one just has to look in the dictionary “statusData” for the entry “speed” and gets the associated value.

get next DB

This function takes the evaNr for the next station and looks out for the station in the list of stops where the evaNr matches the one obtained above. If thats the case, it gets the Name, the arrival delay, the track number and the actual arrival time. Since the arrival time is encoded as Unix time in milliseconds it is converted with the help of datetime to a readable local time string.

get trainInfo DB

This just extracts the Train type, number and the final station from the tripData variable. It may be helpful if one is in doubt if one is sitting in die correct train.

Concluding remarks

This took way too much time creating a script, that would become eventually useless if there is a major change in the used datasources. But apart from that it is a good small exercise for me. Maybe my programming ability gets a little bit less worse if I do some small scripting and write about it. And while I am at it, I use the documentation of this stuff to make my english less worse. And to do this I write this documentation in englisch even if it is completely unnecessary because this API just works in trains from DB. And because I just mentioned that: The České dráhy also has a quite similiar API on their trains. That is also the reason, that the DB specific functions contain a DB in their function name. Next time I travel with an Czech train, I will hopefully implement their API too.

So if you think this script is helpful, just fork and adapt it to your needs. And if you have any remarks just let me know…