The data interface
This feature is part of the SmartRace Champions Club. This is a big package with cool extensions, which you can conveniently add in the form of a monthly or annual subscription in the app and of course cancel at any time. Learn more.
What is the data interface?
The data interface offers you the possibility to connect external services or scripts to SmartRace. For this purpose, SmartRace sends certain data to a configurable endpoint on request and you can process this data as you wish. The data is sent as a POST request in JSON format.
The data interface is endpoint-agnostic, i.e. it is not aimed at specific endpoints with a specific data format. What you do with the data is entirely up to you.
Activation and configuration
The data interface is not activated by default. To activate it, go to Settings in the main menu of SmartRace and open the Data Interface tab. Use the button at the top to enable the interface, but you must also enable the events that you want the data interface to consider. Which data are contained in the individual events, you will learn below.
The different events
You can select on which events the data interface should send data. Each event type triggers different actions in SmartRace. Which actions these are, you can see in the following:
- Lap: A car finishes a lap. The first crossing of start/finish does not trigger this event.
- Event: A new event (qualifying/race) is started or finished or the event status changes (e.g. suspended, jump start, etc.).
- Weather: Weather changes and change announcements (“it will rain soon”, etc.)
- VSC: Activating and deactivating the virtual safety car.
- Penalty: Getting and serving penalties
- Damage: Suffer and repair damage
- Fuel: Fuel state updates.
- Pitstops: Information about cars getting in and out of the pits.
- Miscellaneous: Different information (see below):
Sent data
The structure of the sent data is identical for each event and is built like this:
{
"time": 1684769957969, // Time of the event as Unix timestamp
"event_type": "ui.lap_update", // Event name
"event_data": {
// The data of the event, see below
}
}
ui.lap_update
{
"controller_id": "1",
"lap": 1,
"laptime": "0:13.861",
"laptime_raw": 13861,
"sector_1": "0:06.305",
"sector_1_pb": true,
"sector_2": "0:03.224",
"sector_2_pb": true,
"sector_3": "0:04.332",
"sector_3_pb": true,
"lap_pb": true,
"driver_data": {
"id": 2,
"name": "Marc",
"name_tts": "",
"active": "yes",
"start_no_text_style": "normal",
"team": "-",
"name_short": null,
"image": "",
"start_no": "",
"start_no_color_border": "rgb(68, 68, 68)",
"start_no_color_background": "rgb(68, 68, 68)",
"start_no_color_text": "rgb(68, 68, 68)"
},
"car_data": {
"color": "rgb(176, 243, 0)",
"brakes": null,
"active": "yes",
"tags": "[]",
"decoder_type": "Carrera (default)",
"image": "cdvfile:\/\/localhost\/persistent\/1684000048302.jpg",
"laps": 5,
"fuel": null,
"speed": null,
"tyres": "Ortmann",
"digital_analog": "digital",
"name": "Porsche 911 RSR Grello (911)",
"manufacturer": "Carrera",
"id": 40,
"interval_counter": 0,
"scale": "1:24",
"magnets": "yes",
"logo": "porsche.png",
"changed_on": null,
"interval": 0,
"sound": "-",
"comment": ""
},
"controller_data": {
"color_bg": "rgb(176, 243, 0)",
"color_text": "#000"
}
}
event.start
When starting a lap race over 50 laps:
{
"type": "race",
"laps": "50"
}
When starting a time race over 10 minutes (600 seconds):
{
"type": "race",
"duration": "600"
}
event.end
The data contains all participants, here exemplarily with three participants:
{
"type": "race",
"result": {
"1": {
"driver_id": 2,
"car_id": 39,
"controller_id": 1,
"laps": 33,
"best_laptime": 4915,
"pitstops": 0,
"gap": "",
"disqualified": false,
"retired": false
},
"2": {
"driver_id": 2,
"car_id": 3,
"controller_id": 3,
"laps": 29,
"best_laptime": 4914,
"pitstops": 1,
"gap": "+4 Lap(s)",
"disqualified": false,
"retired": false
},
"3": {
"driver_id": 14,
"car_id": 33,
"controller_id": 4,
"laps": 27,
"best_laptime": 4925,
"pitstops": 0,
"gap": "+6 Lap(s)",
"disqualified": false,
"retired": false
}
}
}
event.change_status
{
"old": "running",
"new": "ended"
}
Valid status names: prepare_for_start, starting, jumpstart, running, suspended, restarting, ended.
events.weather_change
Contains either dry or wet.
events.weather_update
Contains either about_to_rain or about_to_dry_up.
race.vsc_deployed
No further data.
race.vsc_retracted
No further data.
race.penalty_update
This event contains the controller_id and the penalty received in seconds. If the time is 0, the penalty has been served.
{
"controller_id": "2",
"penalty": 10
// Will be enriched with driver_data, car_data and controller_data, see ui.lap_update
}
race.damage_update
This event contains the controller_id and the type of damage. The type is one of the following: engine, battery, suspension or gearbox.
If the damage attribute has the value none, the damage has been repaired.
{
"controller_id": "2",
"damage": "engine"
// Will be enriched with driver_data, car_data and controller_data, see ui.lap_update
}
util.fuel_update
This event contains the controller_id and the tank content (-1 to 100). -1 means that the tank function is deactivated.
{
"controller_id": "2",
"fuel": "50"
// Will be enriched with driver_data, car_data and controller_data, see ui.lap_update
}
events.tire_change
Wird bei Reifenwechsel ausgelöst, enthält controller_id und die Reifenart, auf die gerade gewechselt wurde (dry oder wet). Jeder Fahrer startet immer auf „dry“.
{
"controller_id": "2",
"tire": "dry"
// Will be enriched with driver_data, car_data and controller_data, see ui.lap_update
}
event.pit.enter
A driver enters the pit. Contains only the controller ID.
event.pit.leave
A driver leaves the pit. Contains only the controller ID.
util.set_active_track
Wird ausgelöst, wenn die aktive Strecke gesetzt wird. Enthält die Daten der jetzt aktiven Strecke.
{
"length": "",
"reference_lap_time": null,
"track_svg": null,
"maximum_lap_time": "",
"id": 7,
"image": "cdvfile:\/\/localhost\/documents\/1570470682256.jpg",
"pitstop_delta": "25000",
"name_short": null,
"name": "Demo Circuit",
"minimum_lap_time": ""
}
ui.reset
Is triggered when the user interface is reset, e.g. when a new event is started. Contains no data.
ui.remove_car_from_session
Is triggered when a car is manually removed from the session (by swiping in the race screen). Contains only the controller ID.
cu.send_esc_command
Contains no data. Is triggered when the ESC command is sent to the control unit from SmartRace. The ESC command triggers or recalls the safety car (if one is configured). Attention: If the corresponding button is pressed directly on the CU, SmartRace is not aware of this and cannot trigger the event.
Processing the data
The receiving script can be of any type. Here as an example a PHP script:
<?php
header("Access-Control-Allow-Origin: *");
header("Access-Control-Allow-Headers: *");
if ($_SERVER['REQUEST_METHOD'] == 'POST'):
$json_data = json_decode(file_get_contents('php://input'));
print_r($json_data);
endif;
?>
CORS Issues and OPTIONS Request
SmartRace runs in a webview and logs on to the web server that receives the data from the data interface, probably with a host such as http://localhost. However, your web server may not allow data to be received from a local or different host due to its CORS policies. In this case, it helps to set appropriate headers via the web server configuration. For Apache, for example, these headers are as follows:
Header set Access-Control-Allow-Origin "*"
Header set Access-Control-Allow-Methods "GET, POST, PUT, DELETE, OPTIONS"
Header set Access-Control-Allow-Headers "x-test-header, Origin, X-Requested-With, Content-Type, Accept"
OPTIONS Request
In addition to the actual POST request, the data interface may first send an OPTIONS request, which you must handle accordingly. The code for this could look like this (example with Flutter/Dart):
server = await HttpServer.bind(InternetAddress(serverIp), 8080);
server?.listen((HttpRequest request) async {
// Allow all origins
request.response.headers.add('Access-Control-Allow-Origin', '*');
// Allow POST requests
request.response.headers.add('Access-Control-Allow-Methods', 'POST');
// Allow headers like Content-Type
request.response.headers.add('Access-Control-Allow-Headers', 'Content-Type');
if (request.method == 'OPTIONS') {
// Respond with status 200 for OPTIONS requests
request.response
..statusCode = HttpStatus.ok
..write('');
await request.response.close();
return;
}
}