After getting my program to listen and log Bluetooth Low Energy advertisements from Govee thermometers running reliably, I needed to figure out how to make the program automatically start when my Raspberry was rebooted. I was led down two paths to get things working, systemd unit files, and debian package files created with dpkg-deb.
The final file structure I came up with is visible in https://github.com/wcbonner/GoveeBTTempLogger but still can use some explanation as to what I did.
To create the debian package, I created a file structure under my source repository that mimicked what I wanted to put on the target system.
\GOVEEBTTEMPLOGGER\GOVEEBTTEMPLOGGER
├───DEBIAN
│ control
│ postinst
│ postrm
│ prerm
│
├───etc
│ └───systemd
│ └───system
│ goveebttemplogger.service
│
├───usr
│ └───local
│ └───bin
│ goveebttemplogger
│
└───var
└───log
└───goveebttemplogger
gvh507x.txt
I had decided I wanted my executable to be located in /usr/local/bin. It’s the file named goveebttemplogger. I wanted it to write log files into /var/log/goveebttemplogger/ and the easiest way to make sure that directory was created was to put a zero length file in that directory, gvh507x.txt.
The files in the DEBIAN directory are used by the dpkg-deb program when building the distributable package. More on those later.
To get the program configured to automatically run when the machine boots, and properly stop when it shuts down, I settled on the systemd unit files as the both the easiest and most reliable method. I’ve been around linux long enough to first think of /etc/rc.local manipulation, then script files for various runlevels in the /etc/init.d/ directories, and was amazed at both the power and ease of setting up to use the systemd unit files. The hardest part was figuring out what other services my program must have already started. I knew it was dependent on Bluetooth, but the specific services was a bit of a guess.
# Contents of /etc/systemd/system/goveebttemplogger.service
[Unit]
Description=GoveeBTTempLogger service
After=bluetooth.target dbus-org.bluez.service network.target
Requires=bluetooth.target
KillSignal=SIGINT
[Service]
Type=simple
Restart=always
ExecStart=/usr/local/bin/goveebttemplogger -v 0 -l /var/log/goveebttemplogger/
[Install]
WantedBy=multi-user.target
After creating that file in the specified location, I was able to issue the following commands to make systemd start the program.
sudo systemctl daemon-reload
sudo systemctl enable goveebttemplogger.service
sudo systemctl start goveebttemplogger.service
The most unique bit of my unit file is that I specifically want my program to be sent the SIGINT signal to kill it, since I will recognize that and flush the log files before exiting. The ExecStart line is the command line to run my program, which I’m also specifying the log directory as one of the parameters.
I had the systemd unit file and the initial DEBIAN/control file figured out pretty easily. I’d come across this https://linuxconfig.org/easy-way-to-create-a-debian-package-and-local-package-repository article which helped understanding the control file.
Package: GoveeBTTempLogger
Version: 1.20200725-1
Section: custom
Priority: optional
Architecture: armhf
Essential: no
Installed-Size: 95
Maintainer: wcbonner@users.noreply.github.com
Description: Listen and log Govee Thermometer Bluetooth Low Energy Advertisments
Depends: libbluetooth3
What took me a while to figure out was how to get the systemctl commands to be run after the files were put in place by the package manager. There are four script commands, which I’m using three. preinst, postinst, prerm, and postrm. Each of them is a simple script and needs to be marked executable in the file system. They are each run at various stages by the package manager, Pre-Installation, Post-Installation, Pre-Removal, and Post-Removal.
#!/bin/sh
# POSTINST script for goveebttemplogger
echo "\033[36m HI I'M A POSTINST SCRIPT `date +"%s"` \033[39m"
systemctl daemon-reload
systemctl enable goveebttemplogger.service
systemctl start goveebttemplogger.service
exit 0
After installation of my program and the systemd unit file, I reload the systemd database, enable my service, and start my service.
#!/bin/sh
# PRERM script for goveebttemplogger
echo "\033[36m HI I'M A PRERM SCRIPT `date +"%s"` \033[39m"
systemctl stop goveebttemplogger.service
systemctl disable goveebttemplogger.service
exit 0
Before removal of my program, I stop the service and disable the service.
#!/bin/sh
# POSTRM script for goveebttemplogger
echo "\033[36m HI I'M A POSTRM SCRIPT `date +"%s"` \033[39m"
systemctl daemon-reload
exit 0
After removal of my program, I reload the systemd database, to make sure it’s not got my unit file in its database any longer.
When I retrieve a copy of my code with the command git clone https://github.com/wcbonner/GoveeBTTempLogger I then have a subdirectory below the GoveeBTTempLogger that is also named GoveeBTTempLogger. That deeper directory is the structure that will be created into the package.
GoveeBTTempLogger/usr/local/bin/goveebttemplogger: goveebttemplogger.cpp
mkdir -p GoveeBTTempLogger/usr/local/bin
g++ -lbluetooth goveebttemplogger.cpp -o GoveeBTTempLogger/usr/local/bin/goveebttemplogger
deb: GoveeBTTempLogger/usr/local/bin/goveebttemplogger GoveeBTTempLogger/DEBIAN/control GoveeBTTempLogger/etc/systemd/system/goveebttemplogger.service
mkdir -p GoveeBTTempLogger/var/log/goveebttemplogger
touch GoveeBTTempLogger/var/log/goveebttemplogger/gvh507x.txt
chmod a+x GoveeBTTempLogger/DEBIAN/postinst GoveeBTTempLogger/DEBIAN/postrm GoveeBTTempLogger/DEBIAN/prerm
dpkg-deb --build GoveeBTTempLogger
I made the very simple makefile above to both compile the code and build the debian package with the simple command of make deb. It produces the package ‘goveebttemplogger’ in ‘GoveeBTTempLogger.deb’.
I can then install the package and start it running with the command sudo apt-get install ./GoveeBTTempLogger.deb
I can stop and either remove it or purge it with the command sudo apt-get remove goveebttemplogger or sudo apt-get purge goveebttemplogger.