I found this blog post, which does a pretty good job of contrasting how this works on SysV and systemd, and explaining how to deal with issues like the filesystem becoming read-only and the command you want to run possibly taking a long time. The following scripts are basically the ones he gives for systemd, with minor modifications.
brownie_shutdown.sh:
#!/bin/bash
# see https://fitzcarraldoblog.wordpress.com/2018/01/13/running-a-shell-script-at-shutdown-only-not-at-reboot-a-comparison-between-openrc-and-systemd/
REBOOT=$( systemctl list-jobs | egrep -q 'reboot.target.*start' && echo "rebooting" || echo "not_rebooting" )
if [ $REBOOT = "not_rebooting" ]; then
/usr/bin/python /home/pi/Documents/programming/brownie/brownie_blank.py
fi
brownie_shutdown.service:
# see https://fitzcarraldoblog.wordpress.com/2018/01/13/running-a-shell-script-at-shutdown-only-not-at-reboot-a-comparison-between-openrc-and-systemd/
[Unit]
Description=Blank out the eInk screen before shutting down
DefaultDependencies=no
Before=shutdown.target halt.target
# The following is required because my scripts are in /home:
RequiresMountsFor=/home
[Service]
Type=oneshot
ExecStart=/usr/local/sbin/brownie_shutdown.sh
[Install]
WantedBy=halt.target shutdown.target
Makefile:
configure_shutdown:
cp brownie_shutdown.sh /usr/local/sbin/brownie_shutdown.sh
chmod +x /usr/local/sbin/brownie_shutdown.sh
cp brownie_shutdown.service /etc/systemd/system/brownie_shutdown.service
systemctl enable brownie_shutdown.service
In the bash script, the code at the top is to distinguish between booting and shutting down. In the .service file, the line RequiresMountsFor=/home is there because my scripts are in /home. The ExecStart line points to the bash script. The makefile should be executed as root.
The blog post says you have to reboot for these changes to take effect, but I actually found that they worked immediately when I shut my system down. He talks about the issue of the action possibly taking a long time, which applies to me, and says that other solutions he found online didn't allow his task to run to completion. However, he doesn't explain what feature of his code makes that work correctly.