By: Garden4Dinner

After I created a temperature logger for my garden room (which would work well for a greenhouse also), I usually checked the temperature on my phone before I got out of bed in the morning. I would also check it when I was getting in my daily exercise. I was checking the temperature twice a day and I found it was getting down to 53 degrees Fahrenheit at night and as high as 63 degrees during the day. It would get a bit higher if it was a sunny day.

I took out our oil filled radiator that was my husband’s in college and adjusted the settings over a couple of days to get it to about 10 degrees warmer. It is a huge improvement to the small room in my basement that has a moderately sized window facing south. The heating mat is consistently in the lower 70 degree range now because the room isn’t too cold. The heating mat will only increase the temperature 10 degrees typically. If it gets too hot, there is nothing in the room to cool down the heating mat and plants. So an ambient temperature range in the 60s is perfect. That is until I start tomatoes and want the warming mat warmer.

greenhouse temperature logger
Our electric oil filled radiator

After several days of checking the temperature and being pretty happy with the new ranges, I started to get lazy and rely on everything to just go on smoothly. A few days later, I checked my google spreadsheet and it had stopped logging. It immediately occurred to me, that I may have rebooted the machine and never set up a static IP address. So the IP address changed after reboot. It isn’t clear to me why it wasn’t logging with a different IP address, but I was unable to VNC into the machine. So I had to bring my raspberry pi and temperature sensor into my living room, hook it up to my TV and set a static IP address.

Since I am used to using a different distribution of Linux, I found instructions on how to set the static IP address that worked for me. After I set that, everything seemed to be running normally.

I decided that I needed to push the temperature logging data to me, instead of pulling it, so if it goes down for some reason, I will know. I want the history of the temperature in the room logged, even if I don’t check for a few days. My solution is to have my raspberry pi automatically send emails to me with the temperature data.  When I don’t receive the emails, I will know there is a problem right away.

My first step was to figure out how to get the raspberry pi to send me an email. I wanted to send an email using my Gmail account without compromising the security of my account. I discovered I could set up exim4 or SSMTP very easily, but I would have to downgrade the security on my Gmail account to allow these to work.

Then I found the Google Gmail API. Considering Google is such a large company, I expected this to be documented better, but I found their documentation to be difficult to search through. I eventually found it all there, I just found it hard to find where all of the different pieces were. I decided since the temperature logger code is python, it would be easiest to just stay in the same language.

I started out with running the Gmail API python quick start which makes sure that authentication is set up correctly to allow printing of all Gmail labels. After the quick start script worked properly, the environment was set up to allow my code to work.  I found the Google Oauth explained documentation to help explain authentication access.

The code to extract the google spreadsheet data for emailing uses gspread. The gspread documentation is pretty easy to follow. It didn’t have as much available functionality as I would have liked, but it was easy to use. I extracted the last 300 lines of temperature logging. The 300 in the code can easily be changed to a different value. Right now I am logging every 5 minutes, so the 300 lines provides a little over a day.

greenhouse temperature loggerTemperature update email that is sent daily

I will place the code below, make it available for download at the bottom of the page, and include the instructions on how to use the code.  To be honest, the code is functional, but I didn’t clean it up as I would have for my job. I plan to improve as needed and as I go along.

There are three files: collect_and_send_data.sh, send_email.py, and extract_spreadsheet.py

collect_and_send_data.sh

#!/bin/bash

#update the file path to be the location of the 3 scripts
directory='/home/pi/email'
#sudo copy or move this script to /etc/cron.daily for automatic daily emails

/usr/bin/python $directory/extract_spreadsheet_data.py
/usr/bin/python $directory/send_email.py

extract_spreadsheet_data.py

#!/usr/bin/python

#This file assumes the DHT22 sensor has already been set up with the google spreadsheet example script that comes from AdaFruit
#That procedure set up the oauth 2.0 procedure for authentication.
#If reuse is intended without setting up the DHT22, follow the Google Oauth 2.0
#procedure and share the spreadsheet with the email address that was generated 
#using that procedure. The email is the client_email inside of the .json file 
#that is generated.
#To share in the spreadsheet, open the spreadsheet and File->Share

import json
import sys
import csv
import time
import datetime
import os
import gspread

from oauth2client.service_account import ServiceAccountCredentials

#Update this to the location of the generated .json file. 
#I placed mine where I put my DHT22 example files,  
#Make sure this includes the absolute path
GDOCS_OAUTH_JSON       = '/home/pi/git_repository/Adafruit_Python_DHT/examples/DHT22.json'

# Google Docs spreadsheet name.
GDOCS_SPREADSHEET_NAME = 'DHT22'

def login_open_sheet(oauth_key_file, spreadsheet, tab):
  """Connect to Google Docs spreadsheet and return the first worksheet."""
  try:
    scope =  ['https://spreadsheets.google.com/feeds']
    credentials = ServiceAccountCredentials.from_json_keyfile_name(oauth_key_file, scope)
    gc = gspread.authorize(credentials)
    worksheet = gc.open(spreadsheet).worksheet(tab)
    return worksheet
  except Exception as ex:
    print('Unable to login and get spreadsheet.  Check OAuth credentials, spreadsheet name, and make sure spreadsheet is shared to the client_email address in the OAuth .json file!')
    print('Google sheet login failed with error:', ex)
    sys.exit(1)

worksheet = login_open_sheet(GDOCS_OAUTH_JSON, GDOCS_SPREADSHEET_NAME, 'Sheet1')

list_values = worksheet.get_all_values()
last_values=list_values[-300:]
filedir=os.path.dirname(os.path.realpath(__file__))
filename = filedir+"/email_attachment.csv"
writer = csv.writer(open(filename, 'wb'))
writer.writerows(last_values)

send_email.py

#A majority of this code was from the gmail api example website
#See comments below where updates should be made

import base64
from email.mime.audio import MIMEAudio
from email.mime.base import MIMEBase
from email.mime.image import MIMEImage
from email.mime.multipart import MIMEMultipart
from email.mime.text import MIMEText
import mimetypes
import os

#from __future__ import print_function
import httplib2
import os

from apiclient import discovery
import oauth2client
from oauth2client import client
from oauth2client import tools
from apiclient import errors

SCOPES = 'https://www.googleapis.com/auth/gmail.compose'
#This should be the generated json file name
CLIENT_SECRET_FILE = 'client_secret.json'
#This should be the name of application when Oauth 2.0 authentication was set up
APPLICATION_NAME = 'Gmail API'
#The email address to send from
EMAIL_FROM='email_from@gmail.com'
#The email address to send the message to
EMAIL_TO=email_to@xxx.com'
#The subject of email
EMAIL_SUBJECT='Temperature Update'

try:
  import argparse
  flags = argparse.ArgumentParser(parents=[tools.argparser]).parse_args()
except ImportError:
  flags = None

def SendMessage(service, user_id, message):
  """Send an email message.

  Args:
    service: Authorized Gmail API service instance.
    user_id: User's email address. The special value "me"
    can be used to indicate the authenticated user.
    message: Message to be sent.

  Returns:
    Sent Message.
  """
  try:
    message = (service.users().messages().send(userId=user_id, body=message).execute())
    print 'Message Id: %s' % message['id']
    return message
  except errors.HttpError, error:
    print 'An error occurred: %s' % error

def CreateMessage(sender, to, subject, message_text):
  """Create a message for an email.

  Args:
    sender: Email address of the sender.
    to: Email address of the receiver.
    subject: The subject of the email message.
    message_text: The text of the email message.

  Returns:
    An object containing a base64url encoded email object.
  """
  message = MIMEText(message_text)
  message['to'] = to
  message['from'] = sender
  message['subject'] = subject
  return {'raw': base64.urlsafe_b64encode(message.as_string())}

def get_credentials():
  """Gets valid user credentials from storage.

  If nothing has been stored, or if the stored credentials are invalid,
  the OAuth2 flow is completed to obtain the new credentials.

  Returns:
    Credentials, the obtained credential.
  """
  home_dir = os.path.expanduser('~')
  credential_dir = os.path.join(home_dir, '.credentials')
  if not os.path.exists(credential_dir):os.makedirs(credential_dir)
  credential_path = os.path.join(credential_dir,'sendEmail.json')

  store = oauth2client.file.Storage(credential_path)
  credentials = store.get()
  if not credentials or credentials.invalid:
    flow = client.flow_from_clientsecrets(CLIENT_SECRET_FILE, SCOPES)
    flow.user_agent = APPLICATION_NAME
    if flags:
      credentials = tools.run_flow(flow, store, flags)
    else: # Needed only for compatibility with Python 2.6
      credentials = tools.run(flow, store)
    print('Storing credentials to ' + credential_path)
  return credentials

if __name__ == "__main__":

  try:
    filedir=os.path.dirname(os.path.realpath(__file__))
    credentials = get_credentials()
    http = credentials.authorize(httplib2.Http())
    service = discovery.build('gmail', 'v1', http=http)
    filename=filedir+"/email_attachment.csv"
    with open (filename, 'r') as myfile:
      data=myfile.read()
    SendMessage(service, "me", CreateMessage(EMAIL_FROM, EMAIL_TO, EMAIL_SUBJECT, data))

  except Exception, e:
    print e
    raise

Spreadsheet extraction email code for download

To configure and run:

I made sure that my scripts had executable permissions. To do this in the same directories as the scripts run this command:

chmod 755 *

My next step was setting it up to send a daily email. I placed the collect_and_send_data.sh script into /etc/cron.daily folder.

sudo cp collect_and_send_data.sh /etc/cron.daily/

I then created a cronjob to run daily at 8:02pm

crontab -e

Select the editor of your choice. Then add the following after the last line:

02 20 * * * /etc/cron.daily/collect_and_send_data.sh

The format of the sent email isn’t extremely easy to look at because I wanted to display it as the email contents and not an attachment. I was afraid I wouldn’t ever open the attachment if I had to go to that extra step. Changing it to an attachment would be fairly easy using this Gmail API example documentation. In the future, I may add-on an average for each of the values and the max and min for easier reading.  Adding in an email alert feature if it is outside of a temperature range would be a great addition. For now, I will know when things aren’t working without having to check on it.

I can’t wait to add-on to this raspberry pi project! I received some more components in the mail this week, I just have to find a little more time for implementing them.

Time:

  • It took me several hours to learn and write the code for this. I imagine it would be much faster to take this code and reuse it.

Do you have automated emails sent to you from your raspberry pi? Do you have other automation in your greenhouse or garden? Please share about it in the comments!

[Image Credit: ©2017 Garden4Dinner]

Save