Reading Gmail with Raspberry Pi and IMAPClient
In part one we covered the basics of what we’re trying to build, and what you’ll need to get started. Here in part two we’ll be playing with some python code to interact with our Gmail account using IMAPClient. Later on, part three is focused on the servo motor, while part four connects it all to the food dispenser.
The goal of this part?
- Setup your Gmail Account with 2-step authentication for a secure way to interact with it through code.
- Write some code to interrogate your inbox for an email and mark it as read when found using the IMAPClient library.
Let’s get to it!
Preparing Your Gmail Account with 2-step Authentication
This is not strictly necessary, but a good security practice. If you don’t mind your credentials being in plain text in your code, feel free to skip ahead.
Since we’re going to be using email as the trigger, we’ll need a way to securely log into our Gmail account from code. You could store your credentials in plain text within the code we write, but I strongly discourage that. Instead, make use of Gmail’s two-step authentication and app passwords.
I created a new Gmail account for this purpose.
- Log into your gmail account
- Navigate to the Sign-in and security page
- Under the Signing in to Google section, click the 2-Step Verification menu, then follow the instructions to enable 2-Step Verification
- Back in the Sign-in and security page right under the 2-Step Verification button you’ll see App passwords.
- Generate a password
- Note: this password will be 16 digits with spaces separating every 4 digits (e.g. xxxx xxxx xxxx xxxx). Please be sure to include the spaces!
The password you generated can be used to log into your Gmail account. We don’t need it right this second, but we’ll be using it to scan for an email that demands we feed the cats!
Writing Code to Read Your Gmail
Now that we have a Gmail account ready to rock, let’s write some code to interrogate it. We’re going to be using the IMAPClient python library for this purpose, and we’ll wrap the calls to this client in our own Python class.
Install IMAPClient now from the terminal: sudo pip install imapclient
Create the GmailWrapper.py Script
Now let’s create our Gmail wrapper class: nano GmailWrapper.py
. Within the GmailWrapper.py script, add the following code:
#!/usr/bin/env python from imapclient import IMAPClient, SEEN SEEN_FLAG = 'SEEN' UNSEEN_FLAG = 'UNSEEN' class GmailWrapper: def __init__(self, host, userName, password): # force the user to pass along username and password to log in as self.host = host self.userName = userName self.password = password self.login() def login(self): print('Logging in as ' + self.userName) server = IMAPClient(self.host, use_uid=True, ssl=True) server.login(self.userName, self.password) self.server = server # The IMAPClient search returns a list of Id's that match the given criteria. # An Id in this case identifies a specific email def getIdsBySubject(self, subject, unreadOnly=True, folder='INBOX'): # search within the specified folder, e.g. Inbox self.setFolder(folder) # build the search criteria (e.g. unread emails with the given subject) self.searchCriteria = [UNSEEN_FLAG, 'SUBJECT', subject] if(unreadOnly == False): # force the search to include "read" emails too self.searchCriteria.append(SEEN_FLAG) # conduct the search and return the resulting Ids return self.server.search(self.searchCriteria) def markAsRead(self, mailIds, folder='INBOX'): self.setFolder(folder) self.server.set_flags(mailIds, [SEEN]) def setFolder(self, folder): self.server.select_folder(folder)
Save and exit the nano editor: CTRL+X
, then Y
, then Enter
.
Verifying the GmailWrapper.py Class Works
Let’s do a test: our script is going to log into the Gmail account, search for email with a specific subject, then mark it as read. Before running the code, send yourself an email with the subject feed cats (the search is case-insensitive, by the way).
We’re going to use the Python interpreter to run our tests. In your terminal make sure you’re in the same directory as the GmailWrapper.py class we just created, then:
# press enter after each line for the interpreter to engage # invoke the interpreter python # import our wrapper class for reference from GmailWrapper import GmailWrapper # create an instance of the class, which will also log us in # the <password> should be the 2-step auth App Password, or your regular password gmailWrapper = GmailWrapper('imap.gmail.com', '<your gmail username>', '<password>') # search for any unread emails with the subject 'feed cats', and return their Ids ids = gmailWrapper.getIdsBySubject('feed cats') # have the interpreter print the ids variable so you know you've got something ids # now lets mark the email as read gmailWrapper.markAsRead(ids) # exit the interpreter quit()
If everything went as planned your email should now be marked as read. Pretty neat!
Conclusion and Next Steps
Part one set the goals and outline. Now with part two wrapped up we have the ability to discern whether or not an email exists. With this power we can trigger certain events, such as spinning a servo motor. That’s exactly what we’re going to do in part three, I’ll meet you there.
Later on in part four we’ll be attaching the Pi and servo motor to the dispenser, but before we make anything permanent let’s get that servo spinning.
Hi, I’m Sam.
I’m a programmer and a DIYer. When I’m not finding things to build I enjoy cooking, hiking, camping and traveling the world with my best friend. Say Hello!
6 Replies to “Reading Gmail with Raspberry Pi and IMAPClient”
Hi Sam,
Thank you for this great post.
I get stuck with the gmailwapper
there is no more 2 factor authentication, or its called difrently nowadays
I added logging in bij Phone after logging is what should be 2 factor authentication ass well
But after that it is not possible to get to app passwords.
so i skipped that part.
After working trough the codes i check if the phyton can log me in but i get the error
NameError: name ‘GmailWrapper’ is not defined
What dit i do wrong?
Hey Sam, Fantastic project! I have a question about the gmailwrapper py script. Using the python interpreter for testing, I am able to get connected to my gmail account, however, when I try and search for any type of string in the subject e.g feed cats, nothing gets returned. ids returns nothing, print(ids) returns ‘none’. Any thoughts?
Hey Skyler – the emails are marked UNREAD, correct? If you send me the scripts you wrote I can take a peek to see if anything sticks out.
Hey Sam, Thank you for the awesome tutorial and inspiration. This is my first attempt at a Python project thanks to a Raspberry Pi Christmas gift. I’ve followed every step thus far with the exception of having two step verification for the cats gmail account. I receive this error message when attempting to Python Interpreter:
Logging in as
Traceback (most recent call last):
File “C:\Users\Jasen\AppData\Local\Packages\PythonSoftwareFoundation.Python.3.7_qbz5n2kfra8p0\LocalCache\local-packages\Python37\site-packages\imapclient\imapclient.py”, line 342, in login
unpack=True,
File “C:\Users\Jasen\AppData\Local\Packages\PythonSoftwareFoundation.Python.3.7_qbz5n2kfra8p0\LocalCache\local-packages\Python37\site-packages\imapclient\imapclient.py”, line 1538, in _command_and_check
typ, data = meth(*args)
File “C:\Program Files\WindowsApps\PythonSoftwareFoundation.Python.3.7_3.7.1776.0_x64__qbz5n2kfra8p0\lib\imaplib.py”, line 598, in login
raise self.error(dat[-1])
imaplib.error: b'[AUTHENTICATIONFAILED] Invalid credentials (Failure)’
During handling of the above exception, another exception occurred:
Traceback (most recent call last):
File “”, line 1, in
File “C:\Users\Jasen\Desktop\My Stuff\Programming\Thonny\GmailWrapper.py”, line 14, in __init__
self.login()
File “C:\Users\Jasen\Desktop\My Stuff\Programming\Thonny\GmailWrapper.py”, line 19, in login
server.login(self.userName, self.password)
File “C:\Users\Jasen\AppData\Local\Packages\PythonSoftwareFoundation.Python.3.7_qbz5n2kfra8p0\LocalCache\local-packages\Python37\site-packages\imapclient\imapclient.py”, line 345, in login
raise exceptions.LoginError(str(e))
imapclient.exceptions.LoginError: b'[AUTHENTICATIONFAILED] Invalid credentials (Failure)’
Any ideas?
I’m having the same problem with Authentication Failed. I enabled ‘Less Secure Apps’ in gmail, but I still get the same error
Found the problem: 1.thought I had enabled ‘less secure apps’ but it didn’t take. fixed that. 2.Also needed to enable IMAP forwarding in my google account. Works now!