Raspberry Pi Home Hub: Building and sending a daily email briefing

2019-03-09  Design,   Programming

The next feature for my Raspberry Pi Home Hub is a Python script that will automatically send an email briefing every morning. In this post I’ll be creating the template, populating it with up-to-date information, and sending it with Yagmail.

How the email briefing will work

The specifications for my Raspberry Pi Home Hub state: “The Home Hub will automatically send a briefing email at around 6:30am every day containing information that would be useful to review each morning.” The way I’m going to implement this can be broken down into four primary steps:

  1. Create an HTML email briefing template
  2. Populate the email template with up-to-date information
  3. Send the email using the Yagmail Python library
  4. Create a Linux cron job to automatically run the script each day

This post will cover the middle two steps, under the assumption that an HTML template has already been created containing variables looking something like this:

[html][head][meta http-equiv="Content-Type" content="text/html charset=UTF-8" /][/head]
[body style="max-width: 400px; margin: 0; padding: 0;"]
[p style="color: #fff; background: #333; margin: 0 0 20px; padding: 20px 5%; font-size: 32pt; font-family: Helvetica; font-weight: bold; width: 90%;"]Daily Briefing[/p]
[p style="color: #333; margin: 15px 5%; font-family: Georgia; font-size: 14pt; line-height: 18pt;"]{$v0} Today is [b]{$v1}[/b].[/p]
...

Note that this HTML is messier than the template for the news and weather screen. As this is an email template, all the formatting must be in-line, rather than in a separate CSS stylesheet.

As the news and weather screen is the only one we’ve created to date, we’ll focus on gathering up-to-date news headlines, travel information, and weather updates and packaging them in a nice daily email briefing for the user along with a random greeting and tips for the day.

Setting things up

As with any Home Hub module, the first step is to import some libraries and build some variables.

import yagmail, datetime, requests, feedparser, random, json, string

We saw most of these libraries the last time around – the important addition is Yagmail, which will enable the Python script to connect to a Gmail account I’ve created for the Home Hub and send the briefing to my address. All of these variables are defined in the config.ini file we created before.

weather_feed_url = "http://datapoint.metoffice.gov.uk/public/data/val/wxfcs/all/xml/" + weather_location_id + "?res=daily&key=" + weather_api_key
tube_feed_url = "https://api.tfl.gov.uk/Line/" + tube_line + "/Status"

Next we define weather_feed_url and tube_feed_url as we did in the last module, using the variables in config.ini to build out the addresses required to fetch the data.

Adding a greeting

To make waking up in the morning a bit more pleasant, let’s add a random greeting to kick the email off. This will be followed by the date in the format: “Today is Friday 8th March 2019.”

greetings = ["Good morning!", "Wakey wakey!", "Time to get up!", "Ohayo!", "Rise and shine!", "Top of the morning!", "Wake up and smell the coffee!"]
number = len(greetings) - 1
random_number = random.randint(0,number)
v0 = greetings[random_number]

I’ve added all the possible greetings to a Python list called greetings. The script then counts the number of entries in the list and generates a random number up to that count. The entry at that number is selected and assigned to the v0 variable ready to populate the email template.

today = datetime.date.today()
day = str(today.strftime('%d'))
if day[-1:] == 1:
    suffix = "st "
elif day[-1:] == 2:
    suffix[-1:] = "nd "
elif day[-1:] == 3:
    suffix = "rd "
elif day[-1:] >= 4:
    suffix = "th "
v1 = str(today.strftime('%A %d')) + suffix + str(today.strftime('%B %Y'))

Generating the date is simple enough using the today.strftime function, but we still have to do a little work to generate the correct suffix (“st”, “nd”, “rd”, or “th”). This is achieved by running an if loop on the day number, which is generated with today.strftime('%d') and assigned to day.

Adding weather information

The process for fetching the news, weather, and Tube status is the same as in the news screen script. For weather, we use requests to fetch the data from the Met Office.

weather_data = requests.get(weather_feed_url)
weather_data = weather_data.content
weather_today_temp = weather_data.split('FDm=\"', 1)[1]
v2 = weather_today_temp.split('\"', 1)[0]
weather_today_type = weather_data.split('W=\"', 1)[1]
v3 = weather_today_type = int(weather_today_type.split('\"', 1)[0])

The data returned is then split in various ways to get today’s temperature and weather type and assign them to the v2 and v3 variables. We’ll need to do a bit more work with these later to convert the weather type number into descriptive text to use in the email template.

Adding transport updates

The current London Underground state is also retrieved using requests and split.

transport_data = requests.get(tube_feed_url)
northern_line_status = str(json.loads(transport_data.content))
northern_line_status = northern_line_status.split('u\'statusSeverityDescription',1)[1]
northern_line_status = northern_line_status[5:]
v4 = string.lower(northern_line_status.split('\'',1)[0])

Unlike the news and weather screen script, the email briefing script returns the description of the current Tube line status, rather than a number. This string is converted to lower case with string.lower so it’s suitable for use in its location in the email template.

Adding news headlines

The latest news headlines are returned using feedparser and the BBC News RSS feed URL.

news_data = feedparser.parse(news_feed_url)
v5 = news_data['entries'][0]['media_thumbnail'][0]['url']
v6 = news_data['entries'][0]['link']
v7 = news_data['entries'][0]['title']
v8 = news_data['entries'][0]['summary']
v9 = news_data['entries'][1]['title']
v10 = news_data['entries'][1]['link']
...

The top story will feature a thumbnail, headline, and short description, while the other stores will just be clickable headlines. The required information is returned by querying news_data['entries'] and assigned to a series of variables to be used in the briefing template.

Populating the email template

Now we have all the information, it’s time to populate the HTML email template.

template = open("html/templates/email.htm", "r")
contents = []

First we open the template file, which is stored in the templates folder in the app’s html directory. We create a blank variable called contents to store the completed email template for sending. Data is appended line by line, in the same way the news and weather screen was built.

for line in template:
    if "{$v0}" in line:
        line = string.replace(line, '{$v0}', v0)
    if "{$v1}" in line:
        line = string.replace(line, '{$v1}', v1)
    if "{$v2}" in line:
        line = string.replace(line, '{$v2}', v2)
    if "{$v3}" in line:
        if v3 == 0:
            line = string.replace(line, '{$v3}', "clear")
        elif v3 == 1:
            line = string.replace(line, '{$v3}', "sunny")
    ...

For most variables it’s just a case of iterating through the lines and switching out the placeholder for the contents of the correct variable. For others, like the type of weather, we need to select the correct replacement string based on the variable’s value with a nested if statement.

contents.append(line)

Once each line as been checked for all variable tags, it is added to the populated HTML file.

Sending the email

Once the email HTML has been fully built, it’s time to send it. To connect via SMTP, Yagmail requires the sender Gmail account’s address and password, which I have fetched from config.ini.

subject = "Daily Briefing: " + v1
yag = yagmail.SMTP(sender_address, sender_app_pw)
yag.send(destination_address, subject, contents)

Next we use yag.send along with the destination address (also from the configuration file), the email subject line, and the contents variable, which at this point contains the HTML for the entire email. If everything is configured correctly, we should now receive our briefing when we run the script!

The output

If we check our inbox, we’ll see a nicely formatted email briefing with a random greeting, the latest news headlines, weather (and advice on what to wear out), and travel updates.

Now the Raspberry Pi Home Hub is capable of displaying the latest news, weather, and travel information and sending a daily email briefing. Next month I’ll be building the energy screen, which will fetch information from Google Sheets and calculate my next energy bills. Look out for a blog post about that in the near future, and be sure to visit the project page if you didn’t catch the other posts.

Looking for the comments? My website doesn't have a comments section because it would take a fair amount of effort to maintain and wouldn't usually present much value to readers. However, if you have thoughts to share I'd love to hear from you - feel free to send me a tweet or an email.