Raspberry Pi Mail Checker

May 21 2013 Published by under Physical Computing, Uncategorized

Send to Kindle

WP_20130521_001

I was looking for some Raspberry Pi project to do that not only used external hardware (even if that means only an LED for now), but also reached out into the net to deal with some kind of real time data. I ran into this tutorial on adafruit about checking your gmail with a Pi and lighting up an LED based on whether or not you have new mail. This was along the lines of what I wanted to do, but had two drawbacks. One, I don’t use gmail anymore, and two, it uses a python library called feedparser, which is actually an RSS parser. It only works because apparently you can access your gmail as an RSS feed or something. I wanted to do the same thing, but with any email service that supports, say, IMAP4.

A bit of digging around and I found that Python has an imaplib library that can directly access any IMAP4 based 12mail account. This is included by default in the Python distro that comes with Raspbian. So no further setup there. It took a bit of fiddling around with the docs, both of the library itself and the IMAP4 specs, but I figured out the basic sequence.

The library docs are here: http://pymotw.com/2/imaplib/

And the IMAP4 spec is here: http://tools.ietf.org/html/rfc3501.html

First, you create an IMAP object with one of the following lines, depending whether you need SSL or not. My server does need it.

imap = imaplib.IMAP4(server, port)

or

imap = imaplib.IMAP4_SSL(server, port)

Then, you log in:

imap.login(user, password)

You then need to go into select mode to select IMAP folders and messages.

imap.select()

Then you can do a search. By default, you’ll be searching in your inbox, but there are methods to change folders as well. Search criteria are pretty powerful, but I just wanted to see how many unread messages I have. This does that:

type, data = imap.search(None, "UNSEEN")

The first parameter is the encoding. Using None will return messages with any encoding. Second param is the search criteria. See the IMAP4 spec linked above for a comprehensive list on what you can search for.

This will return a type, data tuple of strings. The type will be “OK” or “NO” depending on success of the call. Note, even if it returns no unread messages, you’ll still get “OK” here, with an empty string in the data.

The data will be an list of strings. Actually, it seems to generally be an list containing a single string. This string is a space delimited list of numbers. These numbers are ids of the messages returned by the search. It’ll be something like this: “1 2 3 4 5″.

You can split this into a list of individual ids like so:

messages = data[0].split()

And the length of this list tells you how many messages the search returned. Zero means no new mail. One or more and you have new mail! Fire up an LED!

Here’s the full program I wrote:

#! /usr/bin/python

import imaplib, time
import RPi.GPIO as GPIO

GREEN_PIN = 25
RED_PIN = 24

class MailChecker:
	def __init__(self, server, port):
		GPIO.setmode(GPIO.BCM)
		GPIO.setup(GREEN_PIN, GPIO.OUT)
		GPIO.setup(RED_PIN, GPIO.OUT)
		GPIO.setwarnings(False)

		try:
			self.m = imaplib.IMAP4_SSL(server, port)
		except:
			self.do_error("Unable to contact server")

	def do_error(self, error):
		# maybe flash another error LED
		print(error)
		exit()


	def log_in(self, user, password):
		try:
			self.m.login(user, password)
		except:
			self.do_error("Unable to log in")

	def check_mail(self):
		type, data = 0, 0
		try:
			self.m.select()
			type, data = self.m.search(None, "UNSEEN")
		except:
			self.do_error("Unable to check messages")

		if type == "NO":
			self.do_error("Problem checking messages")

		self.report(data)

	def start_checking(self, interval):
		while True:
			self.check_mail()
			time.sleep(interval)

	def report(self, data):
		message_count = len(data[0].split())
		if message_count > 0:
			print("You've got %i new messages" %
			message_count)
			GPIO.output(RED_PIN, GPIO.LOW)
			for i in range(1, 100):
        			GPIO.output(GREEN_PIN, GPIO.LOW)
        			time.sleep(.2)
        			GPIO.output(GREEN_PIN, GPIO.HIGH)
        			time.sleep(.2)
		else:
			print("No new mail right now")
			GPIO.output(GREEN_PIN, GPIO.LOW)
			GPIO.output(RED_PIN, GPIO.HIGH)

if __name__ == "__main__":
	user = "your_user_name"
	password = "your_password"
	server = "your_email_server_url"
	port = 993 
	# or whatever port you need to use

	checker = MailChecker(server, port)
	checker.log_in(user, password)
	checker.start_checking(60)

This creates the IMAP object and logs in. Then the start_checking method sets up an infinite loop that searches once every minute. If it finds there are unread messages, it blinks a green LED a bunch of times then leaves it on. No new mail, red LED. The rest is basic GPIO code.

You can change the behavior however you want, maybe remove all the non-error print lines. Then move it to your Pi, do a chmod +x on it to make it executable and run it as root:

sudo ./checkmail.py &

The trailing & character will fork the process and allow you to close the terminal session while keeping the program running – especially useful if you’re sshing into your Pi.

Send to Kindle

Comments are off for this post