GoveeBTTempLogger as a Debian Package

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 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.

│       control
│       postinst
│       postrm
│       prerm
│   └───systemd
│       └───system
│               goveebttemplogger.service
│   └───local
│       └───bin
│               goveebttemplogger

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
Description=GoveeBTTempLogger service dbus-org.bluez.service

ExecStart=/usr/local/bin/goveebttemplogger -v 0 -l /var/log/goveebttemplogger/


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 article which helped understanding the control file.

Package: GoveeBTTempLogger
Version: 1.20200725-1
Section: custom
Priority: optional
Architecture: armhf
Essential: no
Installed-Size: 95
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.

# 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.

# 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.

# 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 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.

2 thoughts on “GoveeBTTempLogger as a Debian Package

  1. Thanks for the great contribution! I am getting great use out of it. Looking forward to log dwnld.

    Here is a bit simpler way to get time(formatted):

    using namespace std;
    char buffer [80];

    int main()
    time_t now = time(0);
    strftime (buffer,80,”GMT Time %F %T”, gmtime(&now)); cout << buffer << endl;
    strftime (buffer,80,"Loc Time %F %T",localtime(&now)); cout << buffer << endl;


    • I’m still trying to understand how to download the logged data. any pointers on how to connect using GATT (I think that’s what’s required) are appreciated.

      My date routines are explicitly trying to avoid using sprintf formatting. If there’s a std::time or std::string function that I’m not aware of, I’d love to hear about it.

Leave a Reply

Fill in your details below or click an icon to log in: Logo

You are commenting using your account. Log Out /  Change )

Google photo

You are commenting using your Google account. Log Out /  Change )

Twitter picture

You are commenting using your Twitter account. Log Out /  Change )

Facebook photo

You are commenting using your Facebook account. Log Out /  Change )

Connecting to %s