Timed OS light/dark theme switching

Dawid Laszuk published on
4 min, 772 words

tl;dr: A GitHub gist with commands walk-through is available here.

What

The ability to adjust themes, and in particular the dark mode, have been one of the most trendy tech features of 2019/2020. Many sites and apps now allow to to flip between the "normal" and the "dark mode".

Why

Although I don't belong to the die hard zealots one can find on the internet, I do appreciate this feature when in dark environment as I'm rather light sensitive and most devices have the lowest brightness on too-high for me. It is a nice surprise that Ubuntu 20.04 came with the global theme and a couple default ones. This let's me to decide when it's dark and then switch to the dark mode. Since many pages, e.g. stackoverflow.com or duckduckgo.com, now detect OS's theme mode they will also be in switch into the mode. Neat. So, when the light goes down, my dark mode goes on, and we're all happy.

But obviously the night comes everyday so why should I sent those 3 seconds of manual labour when I can make it automatic?

How

I won't into too much details but basically the proposed solution is using a service manager systemd and more specifically its systemctl command. There are two "services", one for each theme flip, and they are run daily at specific time.

For the servicd to automagically detect your services and timers they can be be placed in ~/.config/systemd/user. It's likely that there isn't such directory so create it. The code also expects that there is a directory ~/.scripts where some random utility scripts are placed.

The walkthrough code is below. Please note that none of the files are expected to be where they are so you have to create them and fill with the content that the cat command returned. Also, the script changes default terminal profile and it's expectating that there are two called "Dark" and "Light" for day and night, respectively.

mkdir -p ~/.config/systemd/user
mkdir -p ~/scripts

# Create light.service file
cat > ~/.config/systemd/user/light.service << EOF
[Unit]
Description=Automatically change the "Window Theme" to "light" in the morning.

[Service]
Environment=DBUS_SESSION_BUS_ADDRESS=unix:path=/run/user/1000/bus
ExecStart=~/scripts/profile_changer.sh light
EOF

# Create light.timer file
cat > ~/.config/systemd/user/light.timer << EOF
[Unit]
Description=Automatically change the "Window Theme" to "light" in the morning.

[Timer]
OnCalendar=*-*-* 06:00:00
Persistent=true

[Install]
WantedBy=default.target
EOF

# Create dark.service file
cat > ~/.config/systemd/user/dark.service << EOF 
[Unit]
Description=Automatically change the "Window Theme" to "dark" in the evening.

[Service]
Environment=DBUS_SESSION_BUS_ADDRESS=unix:path=/run/user/1000/bus
ExecStart=~/scripts/profile_changer.sh dark
EOF

# Create dark.timer file
cat > ~/.config/systemd/user/dark.timer << EOF
[Unit]
Description=Automatically change the "Window Theme" to "dark" in the evening.

[Timer]
OnCalendar=*-*-* 19:00:00
Persistent=true

[Install]
WantedBy=default.target
EOF

The last file is the script that will be run by the service. It's a simple bash script that will change the default theme and the default terminal profile. Create a new file ~/scripts/profile_changer.sh and paste the following content:

# Create script to change profiles
#!/bin/bash

get_uuid() {
  # Print the UUID linked to the profile name sent in parameter
  local profile_name=$1
  profiles=($(gsettings get org.gnome.Terminal.ProfilesList list | tr -d "[]\',"))
  for i in ${!profiles[*]}
    do
      local uuid="$(dconf read /org/gnome/terminal/legacy/profiles:/:${profiles[i]}/visible-name)"
      if [[ "${uuid,,}" = "'${profile_name,,}'" ]]
        then echo "${profiles[i]}"
        return 0
      fi
  done
  echo "$profile_name"
}

if [ $1 == "dark" ]; then
  THEME='Yaru-dark'
elif [ $1 == "light" ]; then
  THEME='Yaru-light'
fi
UUID=$(get_uuid $1)

/usr/bin/gsettings set org.gnome.desktop.interface gtk-theme $THEME
/usr/bin/gsettings set org.gnome.Terminal.ProfilesList default $UUID

After that, make the script executable and refresh the service daemon:

chmod a+x ~/scripts/profile_changer.sh  # Make script executable
systemctl --user daemon-reload
systemctl --user enable dark.timer light.timer
systemctl --user start dark.timer light.timer

The last three commands will refresh the service daemon to and make it look for any file changes, enable timer services to be run in the background on startup and start them now.

That's less work than expected initially. As most of the time, most of the work came from the StackExchange, from the AskUbuntu thread. Lucky that most of the time there's someone with a similar question and someone with good answer.