My Profile Photo

AndrewCz


Using liberty-minded opensource tools, and using them well


Systemd for an Alarm Clock




I prefer full albums over singles or sound bites without exception. So setting up an alarm that was a full album in the morning not only satisfies me, but over time I figure out how late I am going to be based on what song is playing when I go to leave the house for work.

As a general overview, my external hard drive is hooked up to my laptop, which contains all of my music, is currently connected to my laptop. In the past, I had employed a separate server and worked with a crontab, but for reasons detailed below, that needed to change. So after a bit of fussing and a lot of cussing (out Poettering), I got everything set up in quite the satisfying way.

My JBLs

The picture for my previous post was of my JBL servers. Those I have sitting up on my desk. Since my one wall is solid concrete, and the other borders on an apartment that is being completely gutted by the management, I have no one to complain about my noise level. Therefore, the best chance of me not sleeping through my alarm is to play it through my speakers.

Now, given that those speakers are useful for other purposes (streaming music, playing videos on one of my three monitors, etc.) I’d prefer that I don’t have to disconnect them from my primary purposes just in order to have a working alarm clock. That rules out any other servers. Additionally, I can’t control the volume on the speakers via the default audio driver (ALSA), yet I can with pulseaudio (via pavucontrol). And now that I’ve got my mpd server streaming to my laptop, I have my music stream, videos, and alarm all in one place.

Systemd Timers

So given that I have the inclination to use my laptop to drive the speakers for my alarm, I now need to make the damn thing work. Previously, I was using a cronjob to set the alarm for whenever I needed it to run, however, I didn’t want to install that on my arch laptop, when I had “the replacement for cronjobs” already installed.

Now, I went through the actual setup of the timer a while ago in preparation for this, but will do my best to summarize the setup. In essence, a timer only calls a .service file every…whenever it’s scheduled. It is typical to name the .timer file the same as the .service file - in fact it’s expected by systemd that it’s named the same.

So, typically any .service or .timer files would be put in the /etc/systemd/system/ directory. However, we have to back up a second to investigate the actual execution of the alarm media file. So, we’re calling this album with mpv - an incredibly versatile media player. This is defined in the .service file. It’s a oneshot service that calls a script to call the specific album.

[Unit]
Description=Alarm Clock to wake up in the morning
# First I question the wisdom of Poettering, then I laud it
# https://superuser.com/questions/756334/pulseaudio-no-sound-after-su-to-root-no-default-audio-device-configured

[Timer]
# 8 @ 11:30
# 6.5 @ 1:00
OnCalendar=*-*-* 07:30:00

# 8 @ 12:30
# 6.5 @ 02:00
#OnCalendar=*-*-* 08:30:00

# 8 @ 1:30
# 6.5 @ 03:00
#OnCalendar=*-*-* 09:30:00

# 8 @ 2:30
# 6.5 @ 04:00
#OnCalendar=*-*-* 10:30:00

Unit=alarmclock.service

[Install]
WantedBy=timers.target

The mpv script

From what I remember of setting up the service file (awhile ago), systemd service files are very poor at executing commands, uh, how do I say this… it’s very poor at executing them correctly. Anyways, the easiest way is to call a script from a service file. At the moment, I have all of the variables (album name and path) defined in that file, so anything in regards to what is played is defined in the script. Ultimately, it’s just one file issuing one command:

musicdir='<...>'
genre='SundayAfternoon'
artist='Dirty_Heads'
album='Cabin By the Sea (Deluxe Version)'

mpv --no-audio-display --no-video --ao=pulse "${musicdir}/${genre}/${artist}/${album}/"

Which may explain the title image for this post. And as you can see, I prevent any visual aspect of the program from manifesting itself, and limit the program to playing the music.

Pulseaudio vs the root user

Long story short - as you can see from the link (in the systemd timer file above):

Pulseaudio in Ubuntu is configured to use per-user sessions. That means that while the normal user account can happily access it, as you’ve experienced, it is not configured to be used by another user at the same time (such as root).

AKA - if you have come here after spending 3-4 hours trying to get systemd to run media system-wide (with default .timer/.service files) and the default pulseaudio, I’ll tell you straight out that it’s not going to work unless you have pulseaudio configured system-wide, which is not recommended.

Systemd and user units

Systemd had the option to run daemons/services under a specific user instead of system-wide. Much like user-specific cron jobs. This is frequently used for mpd which I may be exploring in the future, but for the time being it’s going to come in handy for this alarm clock. From the Arch Wiki:

As per default configuration in /etc/pam.d/system-login, the pam_systemd module automatically launches a systemd –user instance when the user logs in for the first time. This process will survive as long as there is some session for that user, and will be killed as soon as the last session for the user is closed. […] Similarly to system units, user units are located in the following directories (ordered by ascending precedence):

  • […]
  • ~/.config/systemd/user/ where the user puts its own units.

This mirrors the setup of the default /etc/systemd/system unit files. Namely that the .timer and the .service files are both put in that directory, with the same base name.

Running the Unit

Once the .timer and .service files are set up in that directory, all subsequent systemd commands must be run with the switch --user.

$ systemctl --user enable alarmclock.timer
$ systemctl --user start alarmclock.timer

The latter is used to test the setup. Or if the service file is in question, you can start the .service file. For reference, this is a known-good config:

[Unit]
Description=Sound the alarm to get up

[Service]
Type=oneshot
ExecStart=/usr/local/bin/alarmclock.sh

Editing the Unit

Keep in mind that once the unit is edited a systemctl --user daemon-reload is required. Otherwise, systemd won’t read the changes on the disk, AKA the alarm won’t go off in the morning, which is bad news for getting up in time for work!

Pulseaudio settings

To be honest, the pulseaudio setup just kinda worked. This is most likely because I used pavucontrol to configure the audio settings when the test was running, otherwise it might have defaulted to the internal speakers. But after switching it to the correct output, it tested successfully, and performed admirably the next morning.

Further adaptation

Now that the systemd setup is doing fine, I’d most likely have the script deal with any NFS mounts where I would store my music. Even better, I’d love to have a setup that leveraged my mpd setup to run the alarm clock. But for the time being, I can have my laptop do the bulk of the lifting. This because I mainly use it with the power supply connected and the battery disconnected and…(/me counts the connected peripherals) 7 inputs (USB, mini-DisplayPort, VGA, Power) connected at any given time. This being my arch (and primary) setup, it’s nice to control everything all in one place.


References: