Weather alerts

One great application for this sort of small, relatively low-cost, and Internet connected weather station is the ability to get alerts. For instance, if you live in a cold area, you could get an alert if your heating system has stopped working and your water pipes are at risk of freezing and bursting. We already looked at sending e-mails in Chapter 6, Program Output, so we'll use that same code for notifications.

To implement alarms, we'll also want a way to configure their thresholds and to enable and disable them. For this, we'll use BBIOServer to build a quick web UI. This will let you configure alarms while on the same local network as your BeagleBone, so you would setup your alarms before leaving your house, for example.

You can also display your ThingSpeak plots right in a BBIOServer page (or any webpage) by using their provided embed HTML.

To get the embed code, click on the button that looks like a message box at the top right of each plot you want to embed; the HTML will be provided in the box that pops up, as shown in the following screenshot:

Weather alerts

If you have not made your channel private, the embed code won't work as is, but you can make it work by adding the API key to a new key parameter at the end of the source URL, for example:

<iframe width="450" height="260" style="border: 1px solid #cccccc;"
        src="http://api.thingspeak.com/channels/34716/charts/%i?width=450&
                height=260&results=60&dynamic=true&key=3FYOJN7XYD8Y0MYV" >
</iframe>

We're now ready to build the new and improved weather station:

from bbio import *
from bbio.libraries.BMP183 import BMP183
from bbio.libraries.HTU21D import HTU21D
from bbio.libraries.IoT import thingspeak
from bbio.libraries.BBIOServer import BBIOServer, Page

import smtplib
from email.mime.text import MIMEText

THINGSPEAK_API_KEY = "3FYOJN7XYD8Y0MYV"

bmp = BMP183(SPI0)
htu = HTU21D(I2C2)
channel = thingspeak.ThingSpeakChannel(THINGSPEAK_API_KEY)
server = BBIOServer(blocking=False)
# We start the server in non-blocking mode with blocking=False,
# which let's the main loop continue to sample the sensors while
# it serves the web site in the background.

# The details for the sending account:
SMTP_host = "smtp.gmail.com"
SMTP_email = "[email protected]"
SMTP_pass = "password"
# The address to send alerts to:
to_email = "[email protected]" # could be an SMS proxy as well!

# This HTML chunk is used to embed the ThingSpeak plots into the
# BBIOServer page:
embedded_plot = 
"""
<iframe width="450" height="260" style="border: 1px solid #cccccc;" src="http://api.thingspeak.com/channels/34716/charts/{}?width=450&height=260&results=60&dynamic=true&key=3FYOJN7XYD8Y0MYV">
</iframe>
"""

# This stores the weather data. It's global so it's accessible from the
# BBIOServer callbacks
weather_data = {
    "temperature" : 0,
    "humidity" : 0,
    "pressure" : 0,
    "dew_point" : 0
    }

# This stores the alarm information. It's global so it's accessible from
# the BBIOServer callbacks
alarms = {
    "over_temp" : {
        "threshold" : 40,
        "enabled" : False,
        "tripped" : False,
        "description" : "Over temperature"
        },
    "under_temp" : {
        "threshold" : 0,
        "enabled" : False,
        "tripped" : False,
        "description" : "Under temperature"
        }
    }

def send_email(to, subject, body):
    msg = MIMEText(body)
    msg["Subject"] = subject
    msg["From"] = SMTP_email
    msg["To"] = to
    server = smtplib.SMTP_SSL(SMTP_host)
    try:
        server.login(SMTP_email, SMTP_pass)
        server.sendmail(SMTP_email, to, msg.as_string())
    except smtplib.SMTPAuthenticationError:
        print "warning: cannot login to email server!"

def alarm(trigger):
    """ Creates and sends the appropriate email for the given trigger. """
    body = "{} alert
".format(alarms[trigger]["description"])
    body += "Current weather:
"
    body += "Temperature: {:0.2f} C
".format(weather_data.get("temperature"))
    body += "Humidity: {:0.2f} %RH
".format(weather_data.get("humidity"))
    body += "Pressure: {:0.2f} kPa
".format(weather_data.get("pressure"))
    body += "Dew point: {:0.2f} C
".format(weather_data.get("dew_point"))

    send_email(to_email, "Weather Alert", body)

###
# These functions are all BBIOServer callbacks to configure the
# thresholds and enabled/disabled states of the alarms
def toggle_over_temp():
    alarms["over_temp"]["enabled"] = 
        not alarms["over_temp"]["enabled"]

def over_temp_state():
    return "Enabled" if alarms["over_temp"]["enabled"] else "Disabled"

def toggle_under_temp():
    alarms["under_temp"]["enabled"] = 
        not alarms["under_temp"]["enabled"]

def under_temp_state():
    return "Enabled" if alarms["under_temp"]["enabled"] else "Disabled"
   
def set_over_temp(val):
    global alarms
    try:
        alarms["over_temp"]["threshold"] = float(val)
    except ValueError:
        pass
   
def setUnderTemp(val):
    global alarms
    try:
        alarms["under_temp"]["threshold"] = float(val)
    except ValueError:
        pass
###

def check_alarms(temp, rh, pressure, dew_point):
    """ Checks each value and triggers any appropriate enabled alarms. """
    if (alarms["over_temp"]["enabled"] and 
            temp > alarms["over_temp"]["threshold"]):
        if not alarms["over_temp"]["tripped"]:
            alarm("over_temp")
            alarms["over_temp"]["tripped"] = True
    else: alarms["over_temp"]["tripped"] = False
           
    if (alarms["under_temp"]["enabled"] and 
            temp < alarms["under_temp"]["threshold"]):
        if not alarms["under_temp"]["tripped"]:
            alarm("under_temp")
            alarms["under_temp"]["tripped"] = True
    else: alarms["under_temp"]["tripped"] = False
       

def setup():
    # Create a main page which displays the live plots:
    home_page = Page("Data")
    home_page.add_html(embedded_plot.format(1))
    home_page.add_html(embedded_plot.format(2))
    home_page.add_html("<br />")
    home_page.add_html(embedded_plot.format(3))
    home_page.add_html(embedded_plot.format(4))

    # Create a page for configuring the alarms:
    alarm_page = Page("Alarms")
    alarm_page.add_text("Over temp:")
    alarm_page.add_entry(set_over_temp, "set")
    alarm_page.add_monitor(lambda : alarms["over_temp"]["threshold"],
                           "Current:", units="C")
    alarm_page.add_button(toggle_over_temp, "toggle")
    alarm_page.add_monitor(over_temp_state, "")

    alarm_page.add_text("Under temp:", newline=True)
    alarm_page.add_entry(setUnderTemp, "set")
    alarm_page.add_monitor(lambda : alarms["under_temp"]["threshold"],
                           "Current:", units="C")
    alarm_page.add_button(toggle_under_temp, "toggle")
    alarm_page.add_monitor(under_temp_state, "")
   
    # Create a page for viewing the raw data:
    raw_data = Page("Raw data")
    raw_data.add_monitor(
        lambda : "{:0.2f}".format(weather_data.get("temperature")),
        "Temperature:", units="C"
        )
    raw_data.add_monitor(
        lambda : "{:0.2f}".format(weather_data.get("humidity")),
        "Humidity:", units="%%RH", newline=True
        )
    raw_data.add_monitor(
        lambda : "{:0.2f}".format(weather_data.get("pressure")),
        "Pressure:", units="kPa", newline=True
        )
    raw_data.add_monitor(
        lambda : "{:0.2f}".format(weather_data.get("dew_point")),
        "Dew point:", units="C", newline=True
        )

    # Start the server:
    server.start(home_page, alarm_page, raw_data)

def loop():
    global weather_data
    pressure = bmp.getPressure()/1000.0
    rh = htu.getHumidity()
    temp = htu.getTemp()
    dew_point = htu.calculateDewPoint(rh, temp)
    weather_data["pressure"] = pressure
    weather_data["humidity"] = rh
    weather_data["temperature"] = temp
    weather_data["dew_point"] = dew_point

    check_alarms(temp, rh, pressure, dew_point)
   
    channel.post([temp, rh, pressure, dew_point])
    delay(30000)

run(setup, loop)

If you run that and head on over to your BeagleBone's IP address at port 8000 (for example, http://192.168.1.28:8000), you should see your ThingSpeak plots moving along like so:

Weather alerts

Navigate to the Alarms page, and you will be able to set over and under temperature thresholds, and enable e-mail alerts for each:

Weather alerts

Finally, if a threshold is crossed you'll get an e-mail alert letting you know the current weather conditions:

Weather alerts
..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset
18.227.111.208