Click to watch demonstration video.
Contents
I once created a desktop application called Tasky which would allow
me to create tasks in sticky-note-like windows. I could set
notifications for each task and have notifications sent to my phone.
The UI was nice and but that was about it; Tasky was clunky and buggy
and I couldn't conventiently schedule tasks from my smart phone.
MQTTTasky is much less terrible, despite its name. The basic idea is,
using GroupMe on my phone, I can schedule a task. A GroupMe bot
I have configured will interact with me and schedule the tasks. When
a task notification occurs, the Raspberry Pi running MQTTTasky will
display a notification window about the task and read the task aloud
using a separate GitHub project called simple_google_tts. Additionally,
when LEDs are connected to the Raspberry Pi's BCM pins 18, 23, and 24,
they serve as status lights. Of course, it wouldn't be MQTTTasky
without some MQTT action now would it? MQTTTasky connects to a Mosquito
broker and when a task notification occurs, the notification is also
published to pub/tasks. Payloads are encrypted with Fernet and a
base-64 32-byte key can be used by any MQTT client to decrypt the
message payloads using a Fernet decryption.
In case my artistic endeavors are lost on you, I will try to explain
my system design. We can think of it in terms of the HTTP(S) side of
the architecture, what happens locally on Pi (Pi side?), and the MQTT
side.
Sitting on the Raspberry Pi, we have the mqtttasky_groupme
package running. This package includes the GroupMe bot which communicates
with users over HTTP. The the bot code processes requests in the group
to act on the scheduling component of mqtttasky_groupme which schedules
tasks. So on the 'Pi side', the bot and the scheduler will blink the LEDs
to indicate the status of messages and task notifications. The scheduler
will display graphical task notifications at the notification time of
each task and it will also cause the task information to be read aloud
through a connected speaker using a system call to simple_google_tts.
Lastly, mqtttasky_groupme also includes an MQTT client. When it is time
for a notification to be sent, the scheduler also calls on the MQTT client
to publish the data to the topic, 'pub/tasks'. The client publishes
payloads which are encrypted using Fernet encryption for which the client
provides the symmetric key during initial configuration so that another
MQTT client (such as the one contained in this repo in
./node_mqtttasky_pi_client) can use the key to create Fernet tokens and
decrypt the payloads.
The Node.js client pictured in the above design is an example of another
client which subscribes to 'pub/tasks' on the same broker as MQTTasky is
publishing to. For instructions on setting up a broker for which to connect
your clients, see the section, 'Setting up a Mosquitto broker'.
For me, this design is nifty because it is fully functional with just one
Pi but the MQTT architecture allows me to implement multiple displays and/or
speakers for task notifications such as on my monitor, my Raspberry
Pi touch display, or even a television.
- A Raspberry Pi 3B/3B+ or 4B/4B+ running Raspberry Pi OS w/ desktop
- A display connected to the Raspberry Pi
- A speaker connected to the Raspberry Pi
- A stable Internet connection on the Pi
- Three LED lights (preferably a red, yellow, and green)
- Four female to male jumper wires
- One ~100-220 Ohm resistor
- One breadboard
Please refer to the following diagram (I tried):
1) Connect one jumper wire to physical pin 1 (3.3V); connect the other
end to the positive side of the breadboard to share its voltage.
2) Connect the resistor to the positive side of the breadboard after
the jumper wire.
3) Connect the longer end (anode) of the first (red) LED to the positive
side of the breadboard after the resistor.
4) Connect the next wire to physical pin 12 (BCM 18); connect the other
end to the shorter end (cathode) of the first (red) LED.
5) Connect the longer end (anode) of the second (yellow) LED to the
positive side of the breadboard after the first LED.
6) Connect the next wire to physical pin 16 (BCM 23); connect the other
end to the shorter end (cathode) of the second (yellow) LED.
7) Connect the longer end (anode) of the third (green) LED to the
positive side of the breadboard after the second LED.
8) Connect the next and final wire to physical pin 18 (BCM 24); connect
the other end to the shorter end (cathode) of the third (green) LED.
1) Download each of the following
files on your Raspberry Pi:
2) And following the same instructions, install each of the packages onto your Pi:
3) If git is not installed, you will need to install it:
sudo apt install git
4) As instrcuted in the simple_google_tts readme, run the following command:
sudo apt-get install xsel libnotify-bin libttspico0 libttspico-utils libttspico-data libwww-perl libwww-mechanize-perl libhtml-tree-perl sox libsox-fmt-mp3
5) Clone the repo into the home directory and then open ~/.bashrc:
cd /home/pi; git clone https://github.com/Glutanimate/simple-google-tts.git
nano .bashrc
6) On a new line, at the end of .bashrc, add the following:
export PATH="$PATH:`pwd`/simple-google-tts"
1) First, ensure python3-pip is installed on your Raspberry Pi:
sudo apt install python3-pip
2) Now install mqtttasky_groupme from the package index:
pip3 install mqtttasky-groupme==0.1.4
1) If you do not already, you will need to create a GroupMe account
since this is what MQTTTasky will use to allow you to schedule
tasks. You may sign up for groupme here.
2) Afer registering, login. You will need to create at least one group
to which you will add your GroupMe bot after having created it. For
help with creating a group, see this page.
1) First, register a developer account after logging in at
2) After your are logged in, click the 'Bots' link in the main site
navigation or visit (https://dev.groupme.com/bots).
3) Click "Create Bot", and select thr group to which it
will belong and name it. You will not need to enter
a callback URL and you may give the bot an avatar
by entering a public image URL.
4) After having created your bot, it will be visible on the page at
5) Copy your bot's ID, as well as the Group ID to which it belongs.
Finally, find the 'Access Token' link in the main site navigation
and copy it as well. You will need all of this information shortly.
MQTTTasky requires to be connected to an MQTT broker which uses
authentication. The broker can be hosted on another computer or
Raspberry Pi. It is not recommended to host the broker on the same Pi
as MQTTTasky will be running on. To set up a Mosquitto broker on a
Raspberry Pi, please refer to these instructions.
Please note while following along with the instructions above, that
there are a couple of descrepancies. In the first step, you will have
to change the line
sudo wget http://repo.mosquitto.org/debian/mosquitto-wheezy.list
to the following:
sudo wget http://repo.mosquitto.org/debian/mosquitto-buster.list
Furthermore, the line that says
sudo stop mosquitto
in step 2 should instead read:
sudo systemctl stop mosquitto
Finally, please record the username and password you used in this
configuration as you will need it when configuring MQTTTasky.
After having completed the above setup, you are ready to start the
mqtttasky_groupme program. This may be done simply by entering:
mqtttasky_groupme
On first launch, you will be prompted to enter your GroupMe information
as well as your MQTT broker and authentication information. You should
have already recorded these. If you are unsure of your broker's port
number, it is likely 1883 and if it is hosted on a Linux machine, the
IP address can be found using the 'ifconfig' command (or 'ipconfig' on
Windows). If the configuration is successful, you will see similar
output to that below:
[ MQTT Client ] Connected to broker.
#######################
# Bot: Tarnoff Bot
# Group: IoT Dev Server
# Status: Listening
#######################
Note that you can break out of the program using Ctrl-C. Any time you
need to re-run the configuration, you may enter the command:
mqtttasky_groupme_config
First you will notice that MQTTTasky's MQTT client has connected to
the broker. Once the broker is connected, MQTTTasky will start
listening for messages every 4 seconds.
Each time the program successfully retrieves the messages, the second
(yellow) LED will blink on and off.
Each time a message retrieval fails, the first (red) LED will blink on
and off.
Each time a new message is discovered, the third (green) LED will blink
on and off several times.
Each time a new response is sent back from the bot, the second (yellow)
LED will blink on and off several times.
Finally, when a notification is being shown and the MQTT client is
publishing data, all three lights will blink several times in a sequence.
Additionally, MQTTTasky will display the task notification in a graphical
window and read the task information aloud using simple_google_tts. Every
time a task notification is published, the payload is encrypted using
Fernet encryption and the key provided during the configuration of
mqtttasky_groupme. This key can be used to create a Fernet cipher in other
MQTT clients on the network so that they may decrypt the payload. Included
in the 'node_mqtttasky_pi_client' directory is an example of such a client
in Node.js. It's instructions can be found in a separate readme file within
that directory.
The MQTTTasky bot will make it fairly clear what you need to do to create
a task, however, other options, such as resetting task data or deleting
all scheduled tasks are available. The usage for communicating with the
bot is as follows:
MQTTasky Usage
===============================
- Initiate a new task:
'Schedule task'
'Schedule task <title> with description <description>'
- Date and time:
MQTTTasky will let you know how
to enter date or time when prompted.
If the year is not provided in a date
string, the current year is assumed.
Time strings ending in AM or PM are
automatically converted into 24-hr
format.
- Notifications:
Notifications are specified by a unit
of time before the time of the task.
You may use the following units of
time:
Minutes:
E.g. '15 minutes before'
Hours:
E.g. '15 hours before'
Days:
E.g. '15 days before'
Weeks:
E.g. '15 weeks before'
- Reset:
If you want to cancel or restart the
creation of a task, use the command:
'/reset'
- Delete:
To delete all active tasks:
'/deleteall'
- Bonus:
Ask bot to tell a joke or proverb.
The MQTT topic to which MQTTasky publishes its encrypted payloads
is pub/tasks. The first topic level denotes published data. The
second topic level denotes that task data is being published.
The task data published is a message designed to be read over a
speaker using TTS. Since a payload is published by the scheduler
when a notification is set to occur, this allows subscribed MQTT
clients to display and/or speak the notification when it is
received.
Since payloads are encrypted using Fernet encryption, the symmetric
key provided during the initial mqtttasky_groupme configuration can
be used by clients to implement Fernet decryption tokens to decrypt
the payloads which can then be decoded to UTF-8 strings.
MQTTTasky uses the Python Fernet class from cryptography.fernet.
This class is popular because of its ease of use, and the security
it offers for the lightweight encryption/decryption that is needed
in IoT.
The algorithm used in Fernet is AES using CBC mode with signing
using HMAC and SHA256. Thus Fernet not only encrypts/decrypts but
allows for the authentication of messages to ensure integrity
In cipher block chaining (CBC), plaintext blocks get XORed with
previous ciphertext blocks prior to the encryption process. That
is why it is referred to as block chaining, because each ciphertext
block depends on every processed plaintext block up to that point.
This preserves the integrity of the data
Hashing for Message authentication (HMAC) applies a hash function
over the data and the symmetric key. HMAC using SHA256 is the part
of Fernet that ensures the authenticity and integrity of the message
As Villanueva (2016) points out, one advantage of using HMAC for data
transfer is that hash functions create a fixed-length digest
regardless of the arbitrary length of the message hashed; this
results in mitigating the amount of bandwidth needed to transfer
data over a network.
Key generation using the cryptography.fernet library's Fernet class
renders a 32-byte key which is actually a composite of two 16-byte
keys. The first 16 bytes being a signing key used to sign the HMAC
and the second 16 bytes being a private key used by the encryption
The Fernet token used in the Fernet class consists of:
• Version, 1 byte - the only valid value currently is 128.
• Timestamp 8 bytes - a 64 bit, unsigned, big-endian integer
that indicates when the ciphertext was created. Time is
represented as the number of seconds since the start of Jan 1,
1970, UTC.
• IV 32 bytes - the 128 bit Initialization Vector used in AES
encryption and decryption.
• Ciphertext - the encrypted version of the plaintext message. This is
encrypted using AES, in CBC mode, using the encryption key section
of the Fernet key. The ciphertext is padded to be a multiple of 128
bits, which is the AES block size, using the PKCS7 padding algorithm.
This menas that the ciphertest will always be a multiple of 16 bytes
in length, but the padding will be automatically removed when the
data is decrypted.
• HMAC - a 256-bit HMAC of the concatenated Version, Timestamp, IV,
and Ciphertext fields. The HMAC is signed using the signing key
section [of the] Fernet key.
Once the HMAC has been calculated using the binary data from the Version,
Timestamp, IV, and Ciphertext, the entire token is encoded in Base64
encoding (McBride, 2020). According to Base64 (2020), this encoding
method is used to encode binary data which may need to be transferred to
and stored on media designed to handle ASCII. This is a convenient format
for the Web since it would allow binary data to be represented within a
URL as ASCII text. This encoding does come at a price -- increased size.
Every Base64 digit represents exactly 6 bits of data. Base64 can increase
the size of a string by as much as ~33 percent (Base64, 2020).
In the node_mqtttasky_pi_client,
directory of this repository is a Node.js client which can be configured
to use the mqtttasky_groupme client's Fernet key to decrypt the message
payloads published by MQTTasky and read them aloud using
simple_google_tts. See the separate README file on this page for details
about pacakges used, setup, and usage.