Category Archives: Uncategorized

Cutie Pi

My friend was recently feeling down so I came up with a simple project to inject some ‘aww’ into their day to cheer them up. This project also provides a good example of how to automate basic scripts on the Raspberry Pi and as my Pi was already on 24/7 hosting this site reliably I thought it would be easy to set up an automated emailing task. I wanted to set up a script that would run every day downloading and emailing cute pictures of puppies and bunnies etc, that would make my friend go ‘aww’ and would hopefully fill their day with delight.  The following tutorial is targeted at people wanting to get started writing and running Python scripts in Raspbian and who have already learnt the fundamentals of Python although this isn’t necessary as all the code is here. The following resources and techniques would allow me to do what I wanted:

  • The image hosting server Imgur would be used to provide the daily cute picture.
  • Google’s SMTP server would be used to send the email.
  • Cron would be used to automate the running of the script every day.
  • The modules provided in the Standard Library would be used to make the script pause for a random time so that the email would be sent at an unexpected time.

The environment

You should already have Raspbian loaded onto your Pi and you should be able to boot up into the terminal. If you haven’t then you should follow the first three steps of my tutorial that explains how to set up your Raspberry Pi. Once you’ve loaded up the terminal type the following command to load up the Graphical User Interface:

startx

Once the desktop has loaded you will see icons on the desktop for some of the pre-installed software that comes  bundled with Raspbian. The programs that are relevant to this tutorial is IDLE, specifically IDLE 3. IDLE  or “Integrated DeveLopment Environment” is completely written in Python and is a good basic environment to start writing Python applications with. IDLE is used for writing applications in Python 2.x and IDLE 3 is used for writing application in Python 3.x which is the most up to date version of Python and the version we will be using. Initially when Python 3.x was released Pyton 2.x libraries had to be rewritten which caused a slow adoption of Python 3, this was due to Python 3 having breaking changes and not being backwards compatible. If you load IDLE 3 you will be presented with the Python Shell. The Python Shells allows you to enter Python statements and expressions which will be immediately executed. For example below I have assigned 5 to the name x in one line and have printed the value of x + 3 on the next line. The Python Shell is great for trying out code and executing individual statements but we want to have the ability to build and automate scripts. pythonshell To create a new script select File, New Window or press Ctrl + N. When a script is saved a module is created, the name of the module is the file name without the extension and it can contain variables, functions, and classes. A module’s functionality can be imported into other modules which makes reusing functionality very easy. This will be explained in the following sections.

Organisation

Creating a number of modules will be useful when writing the code as it allows for the responsibilities of the Python program to be divided up for easy reuse later. We could have all the logic in one script but if we wanted to write another script for a different purpose that involved sending images as attachments, perhaps for emailing images in a home security application, this would be hard. Spend more time thinking about how to structure modules first to save time later! The modules that I created were:

  • imgur.py – responsible for interacting with Imgur and getting an image.
  • advancedemail.py – responsible for sending emails already preconfigured with the gmail SMTP server settings.
  • sleep.py – responsible for pausing the application to alter the time the email gets sent.
  • awwemailer.py – responsible for tying the above together. This will be the script that will be executed by Cron.

Getting the image

The first step if to create the module that will retrieve images from Imgur, a popular image hosting service. Imgur has many image categories including aww which is a category for pictures that… make you go ‘Aww!’. The popular images of the day for the ‘aww’ category can be loaded using the URL below:

https://imgur.com/r/aww/top/day

Browsing to the URL brings back the top images in your browser however this isn’t ideal for being consumed by an application. Thankfully Imgur provides a Restful API for retrieving the same information but as JSON instead of HTML. JSON is a lightweight structured format which allows our application to consume the data easily and isn’t at risk of breaking which would be the case if HTML was scraped and parsed – the HTML structure on sites often changes! The JSON version can be retrieved by appending .json to the URL.

https://imgur.com/r/aww/top/day.json

If a request to the above URL is made in a browser like Chrome the JSON will be nicely formatted and you will be able to see the meta data associated with each image, the format is designed to be human readable. To get started creating the Imgur module, double click on IDLE 3 on the desktop and then go to  File, New Window or press Ctrl + N. This will open a new Python file which you should save as Imgur.py, I saved the modules in the location /home/pi/AwwEmail. First we want to import some required modules that come packaged with Python in order to use their functionality. A third party module is also required called requests which will need to be installed. The documentation states that python requests can be installed by running the following command in LXTerminal which can be opened from the desktop along side IDLE 3:

apt-get install python-requests

However, this will only install requests for Python 2.x and as we are using IDLE 3 we require the version for Python 3.x. This can be achieved by installing the Python package manager and then installing requests:

sudo apt-get install python3-pip
sudo pip-3.2 install requests

The modules can now be imported by putting the following at the top of imgur.py:

# Requests needs downloading.
import requests

# Dealing with JSON data.
import json

# Time access and conversions.
import time

# Miscellaneous operating system interfaces.
import os

# Used for creating Universally Unique Identifiers
import uuid

Define a function that takes an Imgur category and directory as parameters:

def GetDailyTopImageForImgurCatory(imgurCategory, destinationPath):

Get the top images and generate a URL of the most popular JPEG of the day, I appended a Unique Identifier to the URL as the standard URL was being cached for some reason, not cute. The unique name and title were extracted from the JSON, the title is a caption and will accompany the image in the email to provide some context from the author:

# Get the JSON data for the top items of the day in the given category, a UUID is appended to
# the URL as the request is being cached for some reason.
r = requests.get('http://imgur.com/r/aww/top/day.json?var=' + str(uuid.uuid4()))

# Parses the JSON and creates a dictionary.
j = json.loads(r.text)

# Gets the list of images from 'data' field.
imageList = j['data']

# Extract the first JPEG from the list of images.
imageName = None
title = None

for image in imageList:
	if image['ext'] == '.jpg':
		imageName = image['hash']
		title = image['title']
		break

# Create a suitable name for the file based on the current time.
filename = time.strftime("%Y%m%d-%H%M%S")

# Generate the image URL.
url = r'http://imgur.com/{name}{ext}'.format(name=imageName, ext='.jpg')

Download the image to the specified location and return the path and title of the image, notice the second parameter passed to the open function this opens the file for writing in binary format.

# Create a suitable name for the file based on the current time.
filename = time.strftime("%Y%m%d-%H%M%S")

# Generate the image URL.
url = r'http://imgur.com/{name}{ext}'.format(name=imageName, ext='.jpg')

# Download the image data.
response = requests.get(url)

# Create the destination path and save the image in that location.
path = os.path.join(destinationPath, '{name}{ext}'.format(name=filename, ext='.jpg'))
fp = open(path, 'wb')
fp.write(response.content)
fp.close()

# Return the path and title of the image.
return (path, title)

The new module can be tested in the Python Shell as shown below, if the module works the specified directory will contain a funny image:

imagedownload

Emailing the image

A separate module for emailing will provide good flexibility without adding much complexity. The advancedemail module contains a function for sending an image with some text as an email to one or more recipients via email when provided with credentials for a Gmail account.  The module is bound to using  Google’s SMTP server however this allows the interface to be easier to use, the consumer doesn’t need to provide a host or port.

A new Python file should be created in the same directory as the previous module created. At the top of the file write the required import statements:

# Import the email modules we'll need.
import smtplib
# Email related modules.
import email.mime.application
from email.mime.multipart import MIMEMultipart
from email.mime.text import MIMEText

Notice the from … import construct. This allows for access to the MIMEMultipart and MIMEText without having to fully qualify the calls by specifying the module each time the functions are used.

Define a function that takes a message, image path, a list of recipients and valid Gmail credentials which will provide access to Gmail’s SMTP servers for sending the email.
Also create an email and attach the message text.

def SendGmailEmail(message, jpegPath, recipients, gmailAddress, gmailPassword):

    # Compose the message.
    msg = MIMEMultipart()
    msg['Subject'] = 'Cutie Pi'
    msg['From'] = gmailAddress
    msg['To'] = "; ".join(recipients)

    msgtext = message

    body = MIMEMultipart('alternative')
    body.attach(MIMEText(msgtext))

Open the file, specifying ‘rb’ as the second parameter to open, opens a file for reading in binary format. Create an email attachment and set ‘Content-Disposition’ as ‘attachment’ as opposed to ‘inline’ so that the image is treated as an attachment by the recipient’s email client. Attach both the body and attachment to the email.

# Create the attachment and set the headers.
type='jpg'
fp=open(jpegPath,'rb')
att = email.mime.application.MIMEApplication(fp.read(),_subtype=type)
fp.close()
att.add_header('Content-Disposition','attachment',filename=os.path.basename(jpegPath))

# Attach the body and image to the email.
msg.attach(body)
sg.attach(att)

Create an SMTP instance to encapsulate the SMTP connection and use Google’s SMTP host details. Enable TLS (transport layer security) to provide extra security when connecting and finally send the complete email.

# Set the SMTP server address to use Gmail.
s = smtplib.SMTP('smtp.gmail.com:587')
# Enable Transport Layer Security.
s.starttls()
s.login(gmailAddress,gmailPassword)
s.sendmail(gmailAddress, recipients, msg.as_string())
s.quit()

Timing Is Everything

“The idea of waiting for something makes it more exciting” 
― Andy Warhol

The prospect of receiving an email of a cute kitten at an unknown time would make the experience a lot more fun for my friend, if they haven’t received the email yet then they will just have to wait… full of fuzzy anticipation, how exciting!

To allow us to achieve this the module below was created, this module contains the simplest function yet but allows for the email to be sent at a random time in a given window. The window is specified by providing the minimum and maximum number of hours to wait. The hours are then converted into seconds then the program generates a random number using the random module which is then passed to the sleep function of the time module. The program then pauses for the given number of seconds, this module was saved as sleep.py. Even though the message gets sent at random times the image is downloaded at midnight to ensure a new image is used each time.

import random
import time

def SleepForRandomIntervalBetweenHours(minHours, maxHours):

    minSeconds = minHours * 3600
    maxSeconds = maxHours * 3600

    randomWaitInSeconds = random.randint(minSeconds, maxSeconds)
    time.sleep(randomWaitInSeconds)

Putting it all together

Now that all the modules are complete we need a function that we can call at regular intervals that brings the other modules together. Create a new Python file, I called it awwemailer.py.First, our custom modules need to be imported so that we can use them.

import advancedemail
import imgur
import sleep

I wanted the email to be sent between 9:00 and 17:00, after all, who wants to be woken up by their phone to see a picture of a cat snuggling up to a dog? The hours specified only work out to be the hours in the day if the function is called at midnight every day. Set the category and set the directory to save the images, you could get images from a different category or build the functionality to include more than one category.

# Send the email during social hours.
minHours = 9
maxHours = 17
# Imgur category.
category = 'aww'
# Location to save images.
destinationImageDirectory = '/home/pi/AwwEmail/Awws'

Pass the required details to the functions from the modules created before and send the email. The recipients variable is a list of one or more email addresses and the email credentials should belong to an account that you have set up. This script will pause half way through for the random time interval, notice that there isn’t a function, we want python to execute the entire script:

# Recipient email addresses.
recipients = ['friend@gmail.com', 'anotherfriend@gmail.com']

# Gmail credentials for sending the email.
gmailAddress = 'cuteimageemailer@gmail.com'
gmailPassword = 'password'

# Download the daily top image and return the image path and title.
imagePath, title = imgur.GetDailyTopImageForImgurCatory(category, destinationImageDirectory)

# Wait a random amount of time between set hours in the day.
sleep.SleepForRandomIntervalBetweenHours(minHours, maxHours)

# The message for the email body.
message = "Here is your daily dose of 'aww'!"

# Send the email.
advancedemail.SendGmailEmail(message, imagePath, recipients, gmailAddress, gmailPassword)

At this point all the code is complete and all that is left to do is to set up the automation so that the email will send automatically, every day. For this we will use Cron, a Unix task-based job scheduling utility. Cron is driven by crontab (cron table) which can be edited and set up to run the awwemailer.py file at midnight. Open LXTerminal from the desktop icon and enter the following command to edit crontab.

crontab -e

You will see an explanation of the purpose of this utility and an example of how it can be used. Any line preceded with a hash is a comment, it can be useful to put hashes in front of entries that you want to temporarily disable without removing the entire line. Enter the following line at the end of the file to make Python run the script at the specified location at midnight:

00 00 * * * Python /home/pi/awwemail/awwemailer.pi

There is no need to restart anything, the changes will be made instantly! Everything is set up and your friend should start receiving images everyday that will make them go…

cat and dog
“…Aww!”

imgur.py module

# Requests needs downloading.
import requests
# Dealing with JSON data.
import json
# Time access and conversions.
import time

# Miscellaneous operating system interfaces.
import os

# Used for creating Universally Unique Identifiers
import uuid

def GetDailyTopImageForImgurCatory(imgurCategory, destinationPath):

# Get the JSON data for the top items of the day in the given category, a UUID is appended to
# the URL as the request is being cached for some reason.
r = requests.get('http://imgur.com/r/aww/top/day.json?var=' + str(uuid.uuid4()))

# Parses the JSON and creates a dictionary.
j = json.loads(r.text)

# Get the list of images from 'data' property.
imageList = j['data']

# Extract the first JPEG from the list of images.
imageName = None
title = None

for image in imageList:
	if image['ext'] == '.jpg':
		imageName = image['hash']
		title = image['title']
		break

# Create a suitable name for the file based on the current time.
filename = time.strftime("%Y%m%d-%H%M%S")

# Generate the image URL.
url = r'http://imgur.com/{name}{ext}'.format(name=imageName, ext='.jpg')

# Download the image data.
response = requests.get(url)

# Create the destination path and save the image in that location.
path = os.path.join(destinationPath, '{name}{ext}'.format(name=filename, ext='.jpg'))
fp = open(path, 'wb')
fp.write(response.content)
fp.close()

# Return the path and title of the image.
return (path, title)

advancedemail.py module

# Import the email modules we'll need.
import smtplib
# Email related modules.
import email.mime.application
from email.mime.multipart import MIMEMultipart
from email.mime.text import MIMEText

# Miscellaneous operating system interfaces.
import os

def SendGmailEmail(message, jpegPath, recipients, gmailAddress, gmailPassword):
    # Compose the message.
    msg = MIMEMultipart()
    msg['Subject'] = 'Cutie Pi'
    msg['From'] = gmailAddress
    msg['To'] = "; ".join(recipients)

    msgtext = message

    body = MIMEMultipart('alternative')
    body.attach(MIMEText(msgtext))

    # Create the attachment and set the headers.
    type='jpg'
    fp=open(jpegPath,'rb')
    att = email.mime.application.MIMEApplication(fp.read(),_subtype=type)
    fp.close()
    att.add_header('Content-Disposition','attachment',filename=os.path.basename(jpegPath))

    # Attach the body and image to the email.
    msg.attach(body)
    msg.attach(att)

    # Set the SMTP server address to use Gmail.
    s = smtplib.SMTP('smtp.gmail.com:587')

    # Enable Transport Layer Security.
    s.starttls()
    s.login(gmailAddress,gmailPassword)
    s.sendmail(gmailAddress, recipients, msg.as_string())
    s.quit()

sleep.py module

import random
import time
def SleepForRandomIntervalBetweenHours(minHours, maxHours):

    minSeconds = minHours * 3600
    maxSeconds = maxHours * 3600

    randomWaitInSeconds = random.randint(minSeconds, maxSeconds)
    time.sleep(randomWaitInSeconds)

awweamiler.py module

# Import the custom modules
import advancedemail
import imgur
import sleep

# Send the email during social hours.
minHours = 9
maxHours = 17

# Imgur category.
category = 'aww'

# Location to save images.
destinationImageDirectory = '/home/pi/AwwEmail/Awws'

# Recipient email addresses.
recipients = ['sadfriend@gmail.com']

# Gmail credentials for sending the email.
gmailAddress = 'cuteimageemailer@gmail.com'
gmailPassword = 'password'

# Download the daily top image and return the image path and title.
imagePath, title = imgur.GetDailyTopImageForImgurCatory(category, destinationImageDirectory)

# Wait a random amount of time between set hours in the day.
sleep.SleepForRandomIntervalBetweenHours(minHours, maxHours)

# The message for the email body.
message = "Here is your daily dose of 'aww'! Love from Calum and me, the Pi. :)\n\n\"" + title + "\""

# Send the email.
advancedemail.SendGmailEmail(message, imagePath, recipients, gmailAddress, gmailPassword)