How to Flash an LED on Your Raspberry Pi When You Get New Email

Using LED to Indicate New Email

This week we’ll create an email notification system using the Raspberry Pi. The idea is to check for new email, and flash an LED when we get one.

Contents

Connecting to Gmail

The circuit will be extremely straight-forward, so let’s focus on the more difficult part first – connecting to an email service.

We need to create a secure connection to our email provider, so we can find out when new mail arrives. Do a quick search, and you’ll likely find scripts like this one where you just connect with your username, password and a few other pieces of info depending on who the provider is. But what you can do will be extremely limited, and the code will be fragile.

Find the Official API

The preferred option is to check whether your email provider has already provided an “official” way to connect to their system and retrieve data from it. These are commonly referred to as APIs, or application programming interfaces.

When a company provides an API, that means they’ve put real time and effort into exposing certain areas of their system to you (although you’ll still have to do some work of your own, as we’ll see), and you can access those systems in relative confidence that how you’re connecting won’t just change or break.

Most of the major email services provide an API, including MS Office, Yahoo Mail, and Gmail.

(Note: At the beginning of the week, I thought I’d implement all three of those, to demonstrate how each works. But understanding an API enough to properly implement it takes time, and I just didn’t have enough of it. Gmail is the only service I use, so that’s the one I focused on.)

Authenticating

With Gmail, the API was my only option. Every time I tried a Python script I found, I’d get the following error:

imaplib.error: [ALERT] Application-specific password required: https://support.google.com/accounts/answer/185833 (Failure)

The problem is that I have 2-Step Verification enabled, and the script can’t get past that. That’s okay… 2FA is a good thing, and we don’t want to disable that. There’s another, more secure and stable, way to access a Gmail account.

Google provides an API for connecting to most of their systems (including Gmail), along with tutorials to implement it in multiple languages (including Python). We can use the API to access messages or pretty much any other aspect of our email account.

This process involves entering a few details on their side, and then they assign you some special numbers (a “client id” and a “client secret”). You download those numbers in a special file called “client_secret.json” and include it with your script, which in turn helps prove that the script is authorized to access your account.

Follow their Python Quickstart. Note the other languages on the left too, if you’d rather try one of those. After you authenticate, their sample script prints a list of your email tags:

gmail api setup 4

Getting the Unread Mail Count

Once authenticated, their API gives you access to all kinds of useful info about your account. We’ll just modify the sample script they provided.

Most of their script is just about authenticating to Gmail, so don’t touch any of that. The following two lines got the list of labels above, and those are the ones we’ll change.

results = service.users().labels().list(userId='me').execute()  
labels = results.get('labels', [])  

But what do we change them to? To find out which API calls we need to make, we’ll delve into the APIs Explorer for the Gmail API. It’s a nice tool, where you can browse through all the available API calls, and even try them out, all from within your browser.

The one we need is gmail.users.messages.list, so we can get a list of messages (and then count them). But we’re not interested in *all *email, just a subset. How do we do that?

Check out that screen capture above again. There’s a hidden label called “UNREAD” that’ll work nicely. The API allows you to place filters (such as userId='me') inside the call to list(), and one of them is a query string (denoted as q=... below).

messages = service.users().messages().list(userId='me', q='is:inbox + is:unread').execute()  
unread_count = messages['resultSizeEstimate']  

By using is:inbox + ``is:unread we can get emails with both of those labels. The json response we get back includes two keys – one is a list of messages, while the other is the number of messages. We can use the latter to get our message count.

{
 "messages": [
  {
   "id": "xxxxxxxxxxxxxxxx",
   "threadId": "xxxxxxxxxxxxxxxx"
  }
 ],
 "resultSizeEstimate": 1
}

Here’s the main portion of the script. I split most of the Gmail stuff into a separate file when I intended to implement several different email providers. I ended up not having enough time, but it’s still nice to have the code somewhat organized. The code that does the actual authorization to Gmail is in a third file, which you can find along with the entire project on Github.

Gmail.py:

from apiclient import errors  
import threading  
import time  
import RPi.GPIO as GPIO  
import GmailAuthorization

PIN = 35  
CHECK_INTERVAL = 30

service = None  
unread_count = 0


def refresh():  
    global unread_count
    try:
        messages = service.users().messages().list(userId='me', q='is:inbox + is:unread').execute()
        unread_count = messages['resultSizeEstimate']
    except errors.HttpError as error:
        print('An error occurred: {0}'.format(error))


def indicator():  
    while True:
        if unread_count > 0:
            GPIO.output(PIN, not GPIO.input(PIN))
        else:
            GPIO.output(PIN, GPIO.LOW)
        time.sleep(0.5)


def monitor():  
    while True:
        refresh()
        time.sleep(CHECK_INTERVAL)


def start_indicator():  
    t = threading.Thread(target=indicator)
    t.daemon = True
    t.start()


def start_monitor():  
    t = threading.Thread(target=monitor)
    t.daemon = True
    t.start()


def load_service():  
    global service
    service = GmailAuthorization.get_service()


def start():  
    load_service()
    start_indicator()
    start_monitor()

NewEmailIndicator.py:

import RPi.GPIO as GPIO  
import Gmail

CHECK_NOW_PIN = 12


def initialize_gpio():  
    GPIO.setmode(GPIO.BOARD)
    GPIO.setup(Gmail.PIN, GPIO.OUT)
    GPIO.setup(CHECK_NOW_PIN, GPIO.IN, pull_up_down=GPIO.PUD_DOWN)
    GPIO.add_event_detect(CHECK_NOW_PIN, GPIO.RISING, callback=check_mail_now, bouncetime=1000)


def check_mail_now(_):  
    Gmail.refresh()


def main():  
    try:
        initialize_gpio()
        Gmail.start()
        raw_input("\nPress any key to exit.\n")
    finally:
        GPIO.cleanup()


if __name__ == '__main__':  
    main()

Designing the Circuit

Once we’ve got a script that’ll connect to an email account, retrieve the data we’re interested in, and turn a GPIO pin on or off based on what we find, the next step is to create the circuit.

It’s a fairly simple one, limited to just an LED and a resistor, connecting board pin 35 to ground. I’ve also added a button (and second resistor) to the circuit, connected to board pin 12, that allows us to immediately check for new email without waiting for the interval. That way, we can change CHECK_INTERVAL to some less-frequent number like 60 (a minute), but then press the button if we don’t feel like waiting. That’s what the add_event_detect line is for in the above code.

New Mail Indicator

Here are some photos of the actual circuit.

IMG_2022

IMG_2023

Questions? Comments? Hit me up in the comments section below, or on Twitter.

More Reading

Some extra reading if you’re interested. Just some stuff I came across while doing research this week.

Gmail

Raspberry Pi

Python

Subscribe to Weekly Updates!

Get an email with the latest posts, once per week...
* indicates required