The Dashboard Reaction Service (or DRS for short) is a small project gathering real time data from F1 Races to send to Home Assistant automations using MQTT. The project bases itself on the FastF1 Livetiming API to fetch the data real time.
- Sync smart lights with the F1 broadcast leader, matching their team colors.
- Automate dynamic lighting scenes for Safety Car, VSC, and Red Flag events.
- Dynamically calibrate the broadcast delay using Home Assistant to perfectly sync on-track events with your screen.
This is not a tool that will let you get a fancy map with all the car positions, or get you a full list of the current race standings. It will not give you nice graphs of car telemetry or advanced analytics.
To put it simply: it is a tool that I use to get an exact "here and now" picture of events that allows my smart devices around the TV to react to what is happening right now in the race. It is intended to enhance the F1/Sky-TV experience, not replace it.
- Python 3.9+
- A running Home Assistant instance.
- An MQTT Broker (the Mosquitto Broker add-on for Home Assistant is a great choice).
- Smart lights/devices configured in Home Assistant (e.g., Philips Hue to respond to the events).
Before running, you must create two configuration files:
mqtt_config.py: Holds your MQTT broker credentials. Create it in the root directory.
MQTT_BROKER_IP = "0.0.0.0"
MQTT_PORT = 1883 # For unsecure communication running over local network
MQTT_USERNAME = "service_user_name"
MQTT_PASSWORD = "your_strong_password" # MQTT password that's also present in your Home Assistantconfig.py: Contains key variables for the service. You can edit the existing file to set your preferred initial broadcast delay and cache filename.
Note
A Note on the PUBLISH_DELAY
You might wonder why there's a delay between the script receiving a message and publishing it to MQTT. This is the most important setting for syncing the service with what you see on screen.
The official F1 timing data, which this tool uses via the FastF1 API, often arrives many seconds (some report up to a minute!) before you see the corresponding action on your F1TV or television broadcast. This is due to natural broadcast and streaming delays.
The PUBLISH_DELAY variable lets you add a buffer to compensate for this. By setting a delay, you ensure that when your lights change color for a new leader or a safety car, it happens at the exact moment you see it on your screen, not seconds beforehand.
You'll need to fine-tune this value based on your specific broadcast:
- A good-guess starting point is often between 50 and 60 seconds. (based on personal experience)
- Remember, you can adjust this delay live using the Delay Calibration buttons in Home Assistant once the session has started.
git clone https://github.com/Moeren588/Dashboard-Reaction-Service
cd Dashboard-Reaction-Servicepython -m venv venv
source venv/bin/activate # On Windows, use `venv\Scripts\activate`pip install -r requirements.txtCreate the mqtt_config.py file as described in the Requirements section above and review config.py.
For easier deployment and management, you can use Docker to run the F1 Dashboard Reaction Service.
- Docker and Docker Compose installed
- A running Home Assistant instance with MQTT broker access
# Pull the latest image from Docker Hub
docker pull monxas/f1-dashboard-reaction-service:latest
# Run with environment variables
docker run -d \
--name f1-drs \
--restart unless-stopped \
-e MQTT_BROKER_IP=192.168.1.100 \
-e MQTT_USERNAME=f1_service \
-e MQTT_PASSWORD=your_password \
-e SESSION_TYPE=race \
-v f1_cache:/app/cache \
monxas/f1-dashboard-reaction-service:latestgit clone https://github.com/monxas/Dashboard-Reaction-Service
cd Dashboard-Reaction-ServiceCreate a .env file in the project root with your MQTT configuration:
# .env file
MQTT_BROKER_IP=192.168.1.100
MQTT_PORT=1883
MQTT_USERNAME=f1_service
MQTT_PASSWORD=your_strong_password
# Optional: Service configuration
PUBLISH_DELAY=30
SESSION_TYPE=race
# FORCE_LEAD=Ferrari# Build and start the service
docker-compose up -d
# View logs
docker-compose logs -f
# Stop the service
docker-compose down# Build the image
docker build -t f1-drs .
# Run the container
docker run -d \
--name f1-drs \
--restart unless-stopped \
-e MQTT_BROKER_IP=192.168.1.100 \
-e MQTT_USERNAME=f1_service \
-e MQTT_PASSWORD=your_password \
-e SESSION_TYPE=race \
-v f1_cache:/app/cache \
f1-drs| Variable | Default | Description |
|---|---|---|
MQTT_BROKER_IP |
- | Required. MQTT broker IP address |
MQTT_PORT |
1883 |
MQTT broker port |
MQTT_USERNAME |
f1_service |
MQTT username |
MQTT_PASSWORD |
- | Required. MQTT password |
PUBLISH_DELAY |
30 |
Delay in seconds before publishing events |
SESSION_TYPE |
race |
Session type: practice, qualifying, race |
FORCE_LEAD |
- | Force initial leader (e.g., Ferrari) |
The official Docker image is automatically built and published to Docker Hub:
- Repository:
monxas/f1-dashboard-reaction-service - Tags Available:
latest- Latest stable release from main branchv1.0.0,v1.1.0, etc. - Specific version releases- Multi-architecture support:
linux/amd64,linux/arm64
version: '3.8'
services:
f1-drs:
image: monxas/f1-dashboard-reaction-service:latest
container_name: f1-drs
restart: unless-stopped
environment:
MQTT_BROKER_IP: "192.168.1.100"
MQTT_USERNAME: "f1_service"
MQTT_PASSWORD: "your_password"
SESSION_TYPE: "race"
volumes:
- f1_cache:/app/cache
volumes:
f1_cache:The service requires two separate processes running in two separate terminals.
In your first terminal, activate the virtual environment and run the following command. This will connect to the F1 servers and start saving live data to the cache file.
python -m fastf1.livetiming save --append cache.txtNote
The livetiming starts broadcasting around 5 min before event start. The connection times out after 60s of no broadcasts. So be aware and not start the livetiming too early as it will cut your connection.
In a second terminal, activate the virtual environment and run the main.py script. The command requires a session type argument and accepts optional flags. When running, the service will start reading from cache.txt (or file defined in config.py) and publishing events to your MQTT broker.
Syntax:
python main.py <session_type> [options]Example:
python main.py qualifying --force-lead "Ferrari"- <session_type>(Required): Specifies the type of session to monitor. Valid options are:
practice(orp,fp)qualifying(orq,"sprint qualifying",sq)race(orp,"sprint race",sr)
--force-lead <TEAM_NAME>(Optional): Sets an initial leader state on startup. This is useful for testing automations without waiting for a leader to be established.
Once the DRS service is running, you need to configure Home Assistant to listen to the MQTT topics. Below you fill find examples for setups and automations.
The following is an example of you can write into your configuration.yaml file to create sensors for the flag status and current leader.
# In your configuration.yaml
mqtt:
sensor:
- name: "F1 MQTT Flag Status"
state_topic: "f1/race/flag_status"
value_template: "{{ value_json.flag }}"
json_attributes_topic: "f1/race/flag_status"
- name: "F1 MQTT Leader Team"
state_topic: "f1/race/leader"
value_template: "{{ value_json.team }}"
json_attributes_topic: "f1/race/leader"Remember to restart your Home Assistant whenever you make changes to the configuration.yaml file.
Using scripts to define your lighting effects keeps your automations clean. This makes everything more seperated, and it makes it easier to make changes to certain aspects and effects, instead of having to dig through the entire automation. You can call these scripts from the main automation. Here are some examples.
sequence:
- target:
entity_id:
- light.tv_left
- light.livingroom_spot
- light.livingroom_spot_3
data:
rgb_color:
- 0
- 91
- 169
brightness_pct: 100
action: light.turn_on
- target:
entity_id:
- light.tv_right
- light.livingroom_spot_1
- light.livingroom_spot_4
data:
rgb_color:
- 235
- 75
- 199
brightness_pct: 100
action: light.turn_on
alias: F1 Team Alpine
mode: single
description: ""sequence:
- target:
entity_id: light.group_lights_living_room_tv
data:
color_name: gold
brightness_pct: 100
action: light.turn_on
- target:
entity_id: light.group_lights_living_room_tv
data:
flash: long
action: light.turn_on
- target:
entity_id: light.living_room_ceiling_lights
data:
color_name: gold
brightness_pct: 40
action: light.turn_on
alias: F1 Safety Car
mode: single
icon: mdi:car-emergency
description: ""This automation listens to the MQTT topics and calls the appropriate script based on the message payload. The input_boolean.f1_mode is a toggle you can create in Home Assistant to easily enable or disable the lighting effects.
alias: F1 Race Lighting Control
description: Controls Hue lights based on F1 race status
triggers:
- topic: f1/race/flag_status
id: flag_update
trigger: mqtt
- topic: f1/race/leader
id: leader_update
trigger: mqtt
conditions:
- condition: state
entity_id: input_boolean.f1_mode
state: "on"
actions:
- choose:
- conditions:
- condition: template
value_template: >-
{{ trigger.id == 'flag_update' and trigger.payload_json.flag ==
'RED' }}
sequence:
- target:
entity_id: script.f1_red_flag
action: script.turn_on
data: {}
- conditions:
- condition: template
value_template: >-
{{ trigger.id == 'flag_update' and trigger.payload_json.flag in
['SAFETY CAR', 'VSC'] }}
sequence:
- target:
entity_id: script.f1_safety_car
action: script.turn_on
data: {}
- conditions:
- condition: template
value_template: >-
{{ trigger.id == 'flag_update' and trigger.payload_json.flag ==
'YELLOW', 'DOUBLE YELLOW' }}
sequence:
- target:
entity_id: script.f1_yellow_flag
action: script.turn_on
data: {}
default:
- choose:
- conditions:
- condition: template
value_template: "{{ trigger.payload_json.team == 'Ferrari' }}"
sequence:
- target:
entity_id: script.f1_ferrari
action: script.turn_on
data: {}
- conditions:
- condition: template
value_template: "{{ trigger.payload_json.team == 'Red Bull' }}"
sequence:
- target:
entity_id: script.f1_red_bull
action: script.turn_on
data: {}
- conditions:
- condition: template
value_template: "{{ trigger.payload_json.team == 'McLaren' }}"
sequence:
- target:
entity_id: script.f1_mclaren
action: script.turn_on
data: {}The time delay between when the python service receives messages, and when you see it on the broadcast
can be very hard to detect. That is why the service also listens in on channels that allows for adjustments
in the broadcasting delay. Currently it has two functions calibrate start and Adjust.
The service should detect in the messages it receives when it sees the session start message. With the automation setup below, you can have a button you press when you see the broadcast start. The service will then use this difference to set the MQTT publishing delay.
alias: F1 Calibrate Start
description: Send the session start time for calibrating the delay
sequence:
- data:
topic: f1/service/control
payload: CALIBRATE_START
action: mqtt.publishNote
For Practice and Qualifying it will be when there's a Green light at the Pit Exit (F1 TV usually has a countdown to this). For Qualifying it is only needed for the start of Q1.
For races this is when it's all five lights out and away they go! (not the start of the formation lap!)
Another way is to publish direct adjustment values that is added or subtracted to the current delay. The example below shows how the automation script and the button is set up, which would allow you to create some button variations based on one automation script
alias: F1 Adjust Delay
description: Adjusts the F1 MQTT delay by a specific amount.
fields:
adjustment_value:
description: The number of seconds to add or subtract from the delay
example: "1.0"
sequence:
- data:
topic: f1/service/control
payload: ADJUST:{{ adjustment_value }}
action: mqtt.publish
mode: singlebutton example
- type: button
name: Delay +1s
icon: mdi:plus-box
tap_action:
action: call-service
service: script.f1_adjust_delay
data:
adjustment_value: "1.0"Note
It is important that the payload starts with 'ADJUST:' followed by the value!
Screenshot of my delay adjustment buttons

The service communicates using the following MQTT topics.
These topics are broadcast by the service for Home Assistant to consume.
- Topic:
f1/service/running_status- Payload:
ONorOFF - Description: A retained message that shows whether the DRS script is currently running or has shut down. Ideal for a status light in your dashboard, letting you know if the service for some reason has shut down or crashed.
- Payload:
- Topic:
f1/service/publishing_delay- Payload: A number representing the delay in seconds (e.g.,
54.25) - Description: A retained message holding the current publishing delay.
- Payload: A number representing the delay in seconds (e.g.,
- Topic:
f1/race/flag_status- Payload: e.g,
{"flag": "YELLOW", "message": "DOUBLE YELLOW IN TRACK SECTOR 8"} - Description: A retained message that provides the current overall track status. (e.g,
GREEN,YELLOW,SAFETY CAR,RED)
- Payload: e.g,
- Topic:
f1/race/leader- Payload: e.g,
{"driver": "LEC", "team": "Ferrari"} - Description: An event message published when a new leader is set. The leader is determined by race lead in races, or fastest lap in pracitce and qualifying. Note that fastest lap is reset between Qualifying sessions (i.e. Q1, Q2 and Q3).
- Payload: e.g,
This topic is used to send commands from Home Assistant back to the service.
- Topic:
f1/service/control- Payload:
CALIBRATE_STARTorADJUST:<number> - Description: Used to send commands to adjust the publishing delay in real-time. (See HA adjustment section for more info)
- Payload:
You might want to test that your setup works, and since the main functionality of this tool relies on the lime data coming in during a broadcast; it can be tricky and frustrating. For this you will find a text file containing "debugging lines" in docs\debug_lines.txt.
- First ensure DRS is running, and HA is set to act on the MQTT topics.
- Copy a line from
debug_lines.txtthat you want to test in your HA setup (I have tried to organize them based on events) - Paste it into the cache file DRS is listening to.
- Save the cache file and see if DRS is logging a response, and then if your HA is doing what you expect.
- A good tip here is to temporarily set publishing delay very short in
config.py
- A good tip here is to temporarily set publishing delay very short in
- Functions and is roughly stable in all F1 session types.
- Very well tested for practice, not so well tested for races
- Works by itself in Qualifying (resetting between qualifying sessions)
- Handles events like Safety Cars and flags.
- Resets itself when yellow flags are cleared from track
- Resets itself when Safety car (or VSC) is deployed and ending
- Does NOT reset on red flags <- need more data for this one
- Adjustable publishing delay now works (though very untested)
- Most likely unstable and will fail when you need it the most, but I am working on making it better for every race 💖
- More testing, using it in as many sessions as I can:
- Specifically flags are uncertain, and delay adjustments.
- Make it more stable
- Implement tests to the code as well
- Replace the pandas library used (absolutely not needed after all)
- Move the drivers dict out of the f1_utils script
- General code improvements
This is a personal, non-commercial project created for fun and educational purposes. It is not affiliated with, authorized by, endorsed by, or in any way officially connected with Formula 1, the FIA, or any of their affiliates.
All official Formula 1 content, trademarks, and intellectual property are the property of their respective owners. The data used by this project is sourced from the public FastF1 API and is intended for personal use in conjunction with a valid F1 subscription. This tool is not a replacement for any official F1 products or services.
