Grab Google Calendar Events From Thunderbird for DankMaterialShell drop-down calendar with Python

I was reading the DankMaterialShell docs and found a way to add events to their top bar drop down calendar. You can sync Google Calendar with Khal or manually add events directly with Khal. The thing is, I use Thunderbird, which grabs my Google calendar already so why not just grab them there?

This code will grab 7 days of events, or whatever period you need, from Thunderbird and add the records to Khal, which then get picked up by the drop-down calendar. I imagine this will work on other window managers with DankMaterialShell installed, but I am running Niri on CachyOS.

You can set the script to grab n days of events, one week seems good, so the code is set to run every Sunday. Errors display as desktop notifications so you know if your events don’t get added. Errors could be more granular, I suppose. It works for my purposes. This does not handle tasks, as far as I know.

Setup

In Thunderbird you need to set your calendars to local so they get added to the database as it syncs with Google. This should collect offline calendar events without making any changes. If you use Evolution, Kalendar, or something else, this code will not work without rewriting the database query.

Install Khal and configure.

Issues

The start date and end dates are odd, or maybe it’s just me… For example, holidays all start a day early and end on the actual holiday. I am not sure if Google does this or Thunderbird. In the Dank calendar you only get an event for the day before. Maybe that is by design?

In my case I decided to simply use the end date with no timestamp. I only have full day events so they don’t span days or weeks. I am mostly interested in holidays, paydays, birthdays, and things I need to do on a specific date.

To remedy this for others, I think adding a day to the start date and end date with time should do it. That could cause issues with some events, but I do not know since I have not tested it. Play around with it until you find a solution that works for your calendars.

Finally. If you do not see your events in Dank, log out and back in.

Code

#!/usr/bin/env python3
import sqlite3
import os
import datetime
import subprocess

'''
 Add Google calendar events from Thunderbird to Niri desktop calendar (top bar).
 Mohawke, Feb. 2026

 Since I already get my Google calendar in Thunderbird it seemed silly to setup
 another process to grab the same data.
 
 I am in the US, Eastern.
 
 Install khal (Arch): sudo pacman -S khal
 Run: khal configure
 I had to manually add the personal calendar in the khal config (~/.config/khal/config...). 
    see: https://khal.readthedocs.io/en/latest/configure.html

 Before running this script! 
    Set ADDEVENTS at line 34ish to False.
    Run this code in the terminal and verify your events, dates, and times.
    If everything looks good, set ADDEVENTS = True
     
 Things you can do to make this better.
    Add error handling and logging or tap into notifications on error so you are aware.
    In my case: notify-send "message" will send a notification on the desktop.
'''

'''
===============================================================================  
 User Configs   
===============================================================================
'''

# Set your Khal calendar. Private might work, but I have not tried it.
Calendar = "personal"

# The database lives in ~/.thunderbird under a sub-folder that is likely not the 
# same as mine. calendar-data/cache.sqlite should be the same.
# Swap USERNAME and profile, t0vttsrv.default-release, for yours. 
Database = "/home/USERNAME/.thunderbird/t0vttsrv.default-release/calendar-data/cache.sqlite"

# Add the events to Khal
ADDEVENTS = True

# If DayOnly is set to True, AdjustAddDay and UseTimeStamp get set to False 
# and events will be added by end date with no time.
AdjustAddDay = False # Add day to start and end event dates. Includes time.
UseTimeStamp = False # Do not add day to start and end event dates. Includes time.
DayOnly = True       # Adds event with end date only with no time.

# Limit in days. For 7 days you should run this script one day a week, etc...
DateLimit = 7

'''
===============================================================================
 Start Code Below
===============================================================================
'''

# datetime stuff...
now = datetime.datetime.now()
delta = now - datetime.timedelta(days=DateLimit)

cutoff_epoch_time = delta.timestamp()
cutoff_epoch_time = cutoff_epoch_time * 1000000
now = now.timestamp()  * 1000000

if DayOnly:
    AdjustAddDay = False
    UseTimeStamp = False

if AdjustAddDay:
    UseTimeStamp = False
    DayOnly = False

if AdjustAddDay:
    UseTimeStamp = False
    DayOnly = False
    
if os.path.exists(Database):
    try:
         with sqlite3.connect(Database) as connection:
            cur = connection.cursor()
            cur.execute(f'SELECT title,event_start,event_end FROM cal_events WHERE event_end > {cutoff_epoch_time} AND event_end < {now} ORDER BY event_start;')
            rows = cur.fetchall()

            for row in rows:

                title = row[0]

                '''
                Most of my events are a single day with no need for times. I also found that single day events start a day ahead and 
                did not factor in the next day. As an example, Valentine's day started on 13th and ended on the 14th, but the calendar 
                only marked the 13th so all my events where a day ahead. If you do not have this issue you can add start and end with 
                time, but you may need to add a day to end_date for the calendar to register the last day. I am sticking with only using
                date from end_date. I will leave the code here using start and end with times.  
                '''

                # Adjusts events start and end dates a day and includes time for multiday events.
                if AdjustAddDay:
                    start_dt = datetime.datetime.fromtimestamp(row[1] / 1000000)
                    end_dt   = datetime.datetime.fromtimestamp(row[2] / 1000000)
                    delta_start = now - datetime.timedelta(days=1)
                    delta_end   = now - datetime.timedelta(days=1)
                    start_date  = delta_start.strftime("%m/%d/%Y %H:%M:%S")
                    end_date    = delta_end.strftime("%m/%d/%Y %H:%M:%S")
                    args = f"/usr/bin/khal new -a {Calendar} {start_date} {end_date} {title}"
                    
                if UseTimeStamp:
                    start_date = datetime.datetime.fromtimestamp(row[1] / 1000000).strftime("%m/%d/%Y %H:%M:%S")
                    end_date = datetime.datetime.fromtimestamp(row[2] / 1000000).strftime("%m/%d/%Y %H:%M:%S")
                    args = f"/usr/bin/khal new -a {Calendar} {start_date} {end_date} {title}"
                    
                if DayOnly:
                    end_date = datetime.datetime.fromtimestamp(row[2] / 1000000).strftime("%m/%d/%Y")
                    args = ["/usr/bin/khal", "new", "-a", Calendar, end_date, title]

                print(args) # To validate events.

                # Verify the above args before allowing this code to run.
                if ADDEVENTS:
                    stdoutdata, stderrdata = subprocess.Popen(args).communicate()
                    
        	stdoutdata, stderrdata = subprocess.Popen(["/usr/bin/notify-send","--app-name=Event Update", "Events updated."]).communicate()
                    
    except Exception as e:
        stdoutdata, stderrdata = subprocess.Popen(["/usr/bin/notify-send","--app-name=Event Update Failed!", e]).communicate()

Run once a week via Systemd

Note: You could use crontab if you prefer.

I saved my Python script as ~/Documents/CronScripts/thunderbird2khal.py
I want to run this script every Sunday at 10:00am.

Create two files in ~/.config/systemd/user

Filename: khalevents.timer

Adjust for your preferred run-time, description, and service name if you changed it from khalevents.service.

Contents

[Unit]
Description=Thunderbird events to khal synchronization timer
Requires=khalevents.service

[Timer]
OnCalendar=Sun *-*-* 10:00:00

[Install]
WantedBy=timers.target

Filename: khalevents.service

Adjust for your paths, filenames, and description. Be sure to swap USERNAME with your username.

Contents

[Unit]
description=Thunderbird events to khal synchronization timer

[Service]
Type=oneshot
WorkingDirectory=/home/USERNAME/Documents/CronScripts
ExecStart=/usr/bin/python /home/USERNAME/Documents/CronScripts/thunderbird2khal.py

[Install]
WantedBy=default.target

Refresh services, so the system is aware of the new files.

systemctl --user daemon-reload

Enable and start the services

systemctl --user enable khalevents.timer
systemctl --user enable khalevents.service

You can check the logs to see if they are running if you did not receive a desktop notification as expected.

journalctl --user | grep "khal"

Leave a Reply