Raspberry Pi Email Tutorial Sending & Receiving with SendGrid

View previous topic View next topic Go down

Raspberry Pi Email Tutorial Sending & Receiving with SendGrid

Post by jamied_uk on 23rd April 2016, 18:48

http://www.rototron.info/raspberry-pi-sending-responding-to-emails






SendGrid provides a powerful API to send and receive emails.  They offer a free plan which allows 12,000 messages per month.  I’m using a Raspberry Pi 2 with a clean install of the latest version of Raspbian Jessie.  Please make sure your Pi is up-to-date with sudo apt-get update and upgrade.
sudo apt-get update
sudo apt-get upgrade

I have received a lot of requests for more Node.js code so I’m providing both Python and JavaScript examples.  The version of Node.js that comes with the latest version of Raspbian is too old, so curl and apt-get install are used to download the latest version which as of this post is 5.9.
curl -sL https://deb.nodesource.com/setup_5.x | sudo -E bash
sudo apt-get install -y nodejs

 
A free SendGrid account is required.  Browse to the SendGrid pricing page, scroll all the way down and click Try for Free.  This plan gives you 12,000 email per month.

SendGrid reviews each new account (I assume to reduce SPAM).  Therefore, it could take a while for your account to be approved.  However, I’ve never had it take more than 5 minutes.  After your account is provisioned, sign in and browse to Settings – API Keys.  Click Create API Key and select General API Key.  The API key provides authentication for your code when sending emails and is more secure than using login credentials.

Give the key a name and then click Mail Send Full Access to give the key mail send permission.

The newly generated key is only displayed once so you need to copy it to the clipboard.  It is important to keep the key secure.  If your key gets compromised, your account could get hacked.  You can always delete and create another key which is what I’ll do after this tutorial.  It’s also a good security practice to periodically regenerate your API keys.

The first example uses Python to send an email.  Use pip to install the SendGrid python library.
sudo pip install sendgrid

The code below imports the SendGrid library that was just installed.  A client is instantiated using the SendGrid API key that was previously copied to the clipboard.  An email message is instantiated and the fields are populated with To, From, Subject and HTML body.  The program sends the email and exits.  Run the program and an email will be sent.
# https://github.com/sendgrid/sendgrid-python
import sendgrid

# api key
client = sendgrid.SendGridClient("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")

message = sendgrid.Mail()
message.add_to("recipient@address.com")
message.set_from("your@address.com")
message.set_subject("Sending with SendGrid is Fun")
message.set_html("and easy to do anywhere, even with Python")

client.send(message)

Please note that for demo purposes, I hard-coded the API key into the program.  This is dangerous in terms of security.  The safer way to handle keys is to store them in a settings file that is outside your program.  Also make sure that the settings file is secure and excluded from GitHub because hackers continually trawl public repositories with automated scripts.
 
Next the code is rewritten in Node.js.  Create a new folder and CD into it.  Use npm init to initialize a new program.  You will be prompted for info regarding your new program.  It’s OK to hit enter to skip any or all prompts.  The SendGrid package for Node.js is installed with npm install.

mkdir sendgrid_node
cd sendgrid_node
npm init
npm install --save sendgrid

The JavaScript code is almost identical to the python code above.
// https://github.com/sendgrid/sendgrid-nodejs
const sendgrid = require("sendgrid")("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")

const email = new sendgrid.Email();
email.addTo("recipient@address.com");
email.setFrom("your@address.com");
email.setSubject("Sending with SendGrid is Fun");
email.setHtml("and easy to do anywhere, even with Node.js");

sendgrid.send(email);

Save the code to a file called index.js.  Type node index to run the program which functions the same as the python version.
node index

Let’s spruce up the above code by having it read a DHT22 temperature/humidity sensor and email the readings.  Use npm to install the raspi-sensors package which allows the Pi to read a DHT22.
npm install –save raspi-sensors
Here is a schematic showing how to connect the DHT22 and an LCD display which will be used later.  I have additional Raspberry Pi tutorials on just the DHT22 and LCD displays.  DHT22 pin 1 goes to 3.3V on the Pi.  The data pin 2 goes to GPIO4.  Please note that the raspi-sensors library uses the Pi board numbering instead of the BCM numbering so GPIO4 is referenced in the code as pin 7.  The 3rd pin is left disconnected and the 4th pins is connected to ground.  A 10K ohm resistor is placed between pin 1 and 2.

Here is the updated code.  The raspi-sensors package is imported and a DHT22 is instantiated on Pi pin 7 (GPIO4).  The DHT22 fetch method takes a single callback which is called twice returning sensor readings (once for temperature and once for humidity).  The fetch method is wrapped in a promise to insure both temperature and humidity are returned before the email is sent.  When the promise resolves the email is sent.  Sudo is required to run the node app because it accesses the GPIO pins.
"use strict"
const sendgrid = require("sendgrid")("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")

const RaspiSensors = require('raspi-sensors');
const DHT22 = new RaspiSensors.Sensor({
type : "DHT22",
pin : 0X7
}, "DHT22");

const dht22Promise = new Promise((resolve, reject)=> {
let t = null, h = null;

DHT22.fetch((err, data)=> {
if(err) {
reject(err.cause)
}
if(data.type === "Temperature") {
t = data.value.toFixed(1);
} else {
h = data.value.toFixed(1);
}
if(t !== null && h !== null){
resolve({ temperature : t, humidity : h });
}
});
});

dht22Promise.then((data)=> {
const email = new sendgrid.Email();
email.addTo("recipient@address.com");
email.setFrom("your@address.com");
email.setSubject("DHT-22 Sensor");
email.setHtml(data.temperature+ "°C / " + data.humidity + "%");
sendgrid.send(email);
}).catch((err)=>{
console.log(err);
});

 



SendGrid Inbound Parse Webhook API allows an app on the Pi to receive and parse email via HTTP push notifications.  A domain name is required to receive email.  You cannot use an email service like Gmail or Hotmail.  If you don’t already have a domain, there are several Internet registrars where you can buy domains such as GoDaddy, eNom and Google Domains.  I’ll be using rototron.info.  Login to your registrar and point the MX Record of your domain to mx.sendgrid.net.  Registrars have different systems to create MX records, but they all should have 3 basic fields:  address, host and priority.  Here is a pic of the GoDaddy settings for my domain.  Points To is the SendGrid address (mx.sendgrid.net).  If you are not using a subdomain, then the host is left blank or set to the @ symbol.  Priority 10 works well.

The first webhook example uses Python.  A light weight web framework called Flask is required.  It can be installed with pip.
sudo pip install Flask

Flask is imported and an app is instantiated.  A decorator is place before the sendgrid_parser function.  It will handle all HTTP posts to the parse route.  The post from SendGrid includes a form with fields for To, From, Subject, Body Text,  Attachment Count and more.  Every time an email comes in the program will log the email data.  It is important to return “OK”.  Otherwise, SendGrid will keep re-posting the email.
from flask import Flask, request
app = Flask(__name__)

@app.route('/parse', methods=['POST'])
def sendgrid_parser():
# Get header info
recipient = request.form.get('to')
sender = request.form.get('from')
subject = request.form.get('subject')
body = request.form.get('text')

# Process attachments if any
attachment_count = int(request.form.get('attachments')) if request.form.get('attachments').strip() else 0
attachments = []
if attachment_count > 0:
for num in range(1, (attachment_count + 1)):
attachment = request.files.get(('attachment%d' % num))
attachments.append(attachment.read())

print("To: " + recipient)
print("From: " + sender)
print("Subj: " + subject)
print("Body: " + body)
print("Attachment Count: {0}".format(attachment_count))

return "OK"

if __name__ == '__main__':
app.run()

Run the python program to start listening for HTTP posts.
* Running on http://127.0.0.1:5000/ (Press CTRL+C to quit)

Currently, the web server is only running locally on the PI on port 5000.  A free service called Ngrok will be used to host the web server on the public Internet.  Browse to ngrok.com and sign up for an account.  Afterwards, go to the download page and download the Linux/Arm version.

Go to the dashboard auth page and copy the Tunnel AuthToken to the clipboard.  This will provide HTTPS secure communication.

Open a terminal and create a directory called ngrok.  CD into the new folder and unzip the Ngrok download.  Run Ngrok with the authtoken switch using the token you just copied to the clipboard.  This only has to be done once.  Use the http switch to start the Ngrok service on port 5000.

cd ~
mkdir ngrok
cd ngrok
unzip ~/Downloads/ngrok-stable-linux-arm.zip
./ngrok authtoken
./ngrok http 5000

The status screen indicates your Pi is now on the Internet.  Copy the https forwarding address to the clipboard.  This is your Pi’s public web address.

Back on the SendGrid settings webpage, click the Inbound Parse tab and then click the Add Host & URL button.

Enter your domain for the hostname.  For the URL field paste the Ngrok address followed by the folder parse.

Send an email to any address at your domain.  SendGrid will receive the email and post the data to your Python program.  The contents will be printed to the console.
 
Now for the Node.js version.  The npm package Express is used instead of Flask for the web framework.  Multer is a middleware used to handle the email form data.  In a new folder, both can be installed with a single npm install.
cd ~
mkdir webhook_node
cd webhook_node
npm init
npm install --save express multer

The code imports express and multer.  The upload variable handles email attachments.  You can add additional array elements if you need more than 2 attachments.  An app is instantiated with port 5000 specified.  A route is set to handle all posts to the parse path.  App listen starts the web server.  Again, the functionality is the same as the python version.
const express = require('express');
const multer = require('multer');
const upload = multer().fields([
{ name: 'attachment1', maxCount: 1 },
{ name: 'attachment2', maxCount: 1 }
]);
const app = express();
app.set('port', process.env.PORT || 5000);
app.post('/parse', function (req, res) {
upload(req, res, function(err){
if(err){
console.log(err);
res.sendStatus(200);
} else {
const recipient = req.body.to;
const sender = req.body.from;
const body = req.body.text;
const subject = req.body.subject;
const attachment_count = req.body.attachments;
const attachments = [];
for (i = 1; i <= attachment_count; i++){
attachments.push(req.files['attachment' + i]);
}
console.log('To: ' + recipient);
console.log('From: ' + sender);
console.log('Subj: ' + subject);
console.log('Body: ' + body);
console.log('Attachment Count: ' + attachment_count);
res.sendStatus(200);
}
})
});

const server = app.listen(app.get('port'), function() {
console.log('Listening on port %d', server.address().port);
});

Now let’s upgrade the above code to display the email subject and body on a 16×2 LED display.  An npm package called lcd will drive the LCD display.
npm install --save lcd

The lcd package is imported and an lcd is instantiated specifying the command/data GPIO pins and the dimensions 16×2.  Instead of logging the email contents to the console, the LCD display is used.  First the display is cleared.  This method takes a callback that fires when the LCD is ready to accept text.  The print method is used to show the subject on the first line.  This method also takes a callback that fires when the operation completes.  Next setCursor is used to position additional text on the beginning of the 2nd line.  Print is used again to display the email body text.  Replace is used to strip any new line characters from the text which would otherwise show up as gibberish.
const Lcd = require('lcd');
const lcd = new Lcd({rs: 21, e: 20, data: [16, 7, 8, 25], cols: 16, rows: 2});

const express = require('express');
const multer = require('multer');
// Add additional attachments to array if you need more than 2
const upload = multer().fields([
{ name: 'attachment1', maxCount: 1 },
{ name: 'attachment2', maxCount: 1 }
]);
const app = express();
app.set('port', process.env.PORT || 5000);
app.post('/parse', function (req, res) {
upload(req, res, function(err){
if(err){
console.log(err);
res.sendStatus(200);
} else {
const subject = req.body.subject;
const body = req.body.text;

lcd.clear(()=> {
lcd.print(subject, ()=> {
lcd.setCursor(0,1);
lcd.print(body.replace(/(\r\n|\n|\r)/gm,""));
});
});

// Must send OK to SendGrid
res.sendStatus(200);
}
})
});

const server = app.listen(app.get('port'), function() {
console.log('Listening on port %d', server.address().port);
});
avatar
jamied_uk
Admin

Posts : 2296
Join date : 2010-05-09
Age : 34
Location : UK

http://address-shortner.co.uk

Back to top Go down

View previous topic View next topic Back to top

- Similar topics

 
Permissions in this forum:
You cannot reply to topics in this forum