How I Made Power Outage Notifications Using a Serverless Function for Free

Posted on June 5th, 2019

Photo by NASA on Unsplash

The Reason

One day I'm at work and I happen to check my email. I notice one that is generated from a neighborhood community app with the title "Is anybody's power out?".

I'm a big smart-home type of guy so of course I have cameras and sensors hooked up at my house. I quickly check my camera feed and sure enough: can't connect. Some of my smart-home tech is saying that it hasn't connected for about an hour.

Now I'm getting a little panicked because last summer we completely finished our basement. I bought the best sump pump with the best battery backup that I could find, but I've never put it to the test. I have no idea how long it will last and of course it has been raining for the last couple of days. I work 35 minutes away so I start calling family to see if any of them have a generator I can borrow.

We get our electricity through the city and our city has a website. On this website is a link showing a map of the city with any power outages all shaded in. Sure enough, my house is part of an outage. Pretty handy. I keep hitting the refresh button to see if there is any update.

Another hour goes by and finally the map updates. My house is no longer in the shaded portion. I check my cameras and they are back on! I can finally relax.

There has to be a better way

So, no matter how smart my home is with water sensors and alarms, if my home has no power, it has no way to communicate with me.

Until I can afford putting in a natural gas generator that automatically turns on when there is no power, I need to be able to get home and hook up a portable generator within a couple hours of the power going out or else I could be out thousands of dollars in basement flood damage.

So I check the web to see if there are any services that I could somehow plug into that would alert me when the power is out. Nothing. And of course my power provider doesn't have any service like that for my small town....

But wait.... They have that map. I wonder how that map gets the data to know what parts are shaded in.

Checking behind-the-scenes on websites

If you load a website using Google Chrome, you can hit F12 to see the developer tools. One of the tabs is called "Network". This tab will show you all of the assets that this website needed to pull in like images, fonts, data and it will show where it is coming from.

Chrome Dev Tools. I have mine set to dark mode.

When you initially load this tab it will probably be blank. Just refresh the website and it will start to populate the data.

The part I'm interested in is any data that it is pulling in from a server. So I filter on XHR. Sure enough, I find one that is called get_live_data . Clicking on it reveals the data of any outages!

Outage information being pulled from a server using an API

But surely they have some sort of authentication and security that makes it so not just anyone can access this data. I decide to find out. I load up a free tool that checks APIs called Postman.

From the Headers tab back on the dev tools, I found the Request URL . When I plug that into Postman it returns the same outage results! This API is open to the public and I can use it in my own applications. Sweet!🙌

But wait, it gets better. Exploring the map further I found a link for getting outage details. When I look at the API behind that I find that it gives me a list of street names that have outages. This is perfect because now I can just check for the name of my street instead of the longitude and latitude.

Note: I know this is very specific to my situation. This won't actually help anyone unless they live in the same town as I do. However, my goal is to show how I looked behind-the-scenes of a website to see how it worked.

So I have my data. But now I need a way to continuously check to see if my street shows up. Normally I would set up a web server that would have a schedule to call the API and parse through the street names. However, here are my problems:

I can't host this web server from my house because the whole point of this is to find out when my house has no power.

Hosting a web server that runs code in the cloud is probably going to cost money. Even if it is just $5 a month, I don't really want to pay for this.

Serverless Functions to the Rescue

I've heard people talk recently about how great serverless functions are and how they are going to change development but I never really looked into them or found a need for them.

Basically, you can write some code that will run based on a trigger. No need to setup a server with an operating system and make sure it is running and up-to-date and secure. And no need to install a whole website framework.

This sounds interesting so I check out one of the most popular services which is by Amazon called AWS Lambda. They even have a free tier as long as you run the function less than 1 million times per month. Pretty generous!

So I sign up for AWS. This does require a credit card so that if I go over their free tier I will be charged. They do have a billing budget alert system that you can set up so you can be notified before that happens.

AWS Lambda allows you to write in several different languages. I've been trying to learn Python recently so this is the one I chose.

Setting Up Your Environment

At this point I'm ready to jump right in and create the function from the AWS website itself. This is definitely possible and it has a built in code editor and all the tools you'd need. However, I found a more streamlined approach is to develop the code locally on my computer and use an npm package called serverless to bring it into AWS. So this assumes you have Python 3 installed on your system and also Node JS v6.5.0 or later.

Step 1: Create the framework

From your terminal, install serverless

npm install -g serverless

Now go to where you want to store this project. I have a folder called "code" so I ran this from that folder in terminal

serverless create

This creates a new folder inside code called power-outage-bot . Inside that folder it created two files:

handler.py

serverless.yml

You can load these up in your favorite code editor. I use the free PyCharm Community IDE.

Now add a new file to the power-outage-bot folder called requirements.txt. The only thing this file should have in it is the word: requests .

requests

This will make sure the requests module will be loaded into Python.

Step 2: Authenticate with AWS

Next, setup your local system to connect to your AWS account.

Log in to your AWS Console, go to Services > Security > IAM. Find the "Users" section and click on "Add User" blue button.

Choose a username and choose only "Programmatic access" checkbox.

On the second page, choose "Attach existing policies directly". Look for "Administrator Access" and check it.

Finally, once the user is created, it will show your "Access key ID" and "Secret access key". Copy these somewhere safe.

Go back to your terminal and add the keys by typing (replacing the <> sections)

export AWS_ACCESS_KEY_ID=<Access key ID> export AWS_SECRET_ACCESS_KEY=<Secret access key>

Step 3: Setup Your Telegram Bot

This function is going to need some way to alert me when I'm out of power. You can set up email or sms if you want, but you'll need to find a service that provides those since we aren't using a server and that might involve a monthly fee. I'm sure there are free tiers available though if you search.

For me, I decided to use Telegram. It is a free app I have on my phone that you can use to communicate with other people on Telegram. Telegram is very developer friendly and it allows you to create free bots that your code can plug into in order to communicate.

Once you install Telegram on your phone and setup an account, you can start a new conversation with @botFather. You can also do this from your web browser on your computer. Type /newbot to @botFather and it will walk you through setting up a new bot. At the end, @botFather will give you a token that you can use to connect to the bot from our Python code. Let's store it in an environment variable as well from the terminal.

export TELEGRAM_TOKEN= "4592342328:APHruyw7ZFj5qOasDFSDFdsfdDFSFJxil-zsdF98"

@botFather will also show a link to the new bot that was created. Go to this and start a conversation. You can send anything, just say something so that it starts a channel.

Since you are the only one that is going to be talking to this new bot you just created, we only care about sending alerts to this one chat conversation. We need to get the chat id.

Paste the following into your browser but replace the <API-access-token> with your token:

https:

Now you should be able to find your chat id from the result. It will be the id inside the chat section.

{ "ok" : true , "result" :[ { "update_id" :XXXXXXXXX, "message" :{ "message_id" : 2 , "from" :{ "id" : 123456789 , "first_name" : "Mushroom" , "last_name" : "Kap" }, "chat" :{ "id" : 123456789 , "first_name" : "Mushroom" , "last_name" : "Kap" , "type" : "private" }, "date" : 1487183963 , "text" : "hi" } } ] }

Lets save this to an environment variable also

export CHAT_ID=123456789

We don't need to setup any sort of web-hook with Telegram because all communication is going to be one way. The bot will be notifying me but I'm not listening for any responses in my code.

Time to Code

Finally, with the setup out of the way, lets start coding!

Load handler.py into your editor. Our Python code will need some packages so make sure this is at the top

import json import os import sys here = os.path.dirname(os.path.realpath(__file__)) sys.path.append(os.path.join(here, "./vendored" )) import requests

Next, lets setup some constants that we'll use throughout our code so it will be easier to read and update

TOKEN = os.environ['TELEGRAM_TOKEN'] CHAT_ID = os.environ['CHAT_ID'] BASE_URL = "https://api.telegram.org/bot{}" .format(TOKEN) STREETS_TO_CHECK = [ 'SESAME ST', 'MAIN ST' ]

Instead of just checking one street, I decided that I wanted to be alerted if any of my friends and family were hit by a power outage so I could let them know. That is why I made a list of streets instead of just a string for my street.

First we want to call the power company's API and get their list of streets that have outages. Then we want to filter that list to only show streets that we care about.

def check_power_outages () : url = "http://path_to_public_power_api.com/get_outage_details" response = requests.request( "POST" , url) data = json.loads(response.text) return list(filter(filter_streets, data[ "Streets" ]))

Python's filter function that I'm using in the return statement uses a filter method called filter_streets . Here is what that looks like:

def filter_streets (street) : return street in STREETS_TO_CHECK

Pretty self explanatory. This filter(filter_streets, data["Streets"]) is going through each of the streets that have outages and filtering out the ones that are included in my list of streets to check. Finally we are putting the results into a Python list.

Our main function that is going to be ran by AWS is called hello . Here is the code inside.

def hello (event, context) : filtered_streets = check_power_outages() if filtered_streets: response = '' try : for filtered_street in filtered_streets: response += 'Power out on ' + filtered_street + '! ' response += 'http://link-to-power-outage-map.com' data = { "text" : response.encode( "utf8" ), "chat_id" : CHAT_ID} url = BASE_URL + "/sendMessage" requests.post(url, data) except Exception as e: print(e) return { "statusCode" : 200 }

First we're calling our check_power_outages method we just built and getting a list of streets that we care about back.

In Python, if a list is empty than it will return a False when called in an "if" statement. So when we say if filtered_streets: when there are no outages, then it will simply skip to the end without doing anything.

However, if it found one or more streets, it will start looping through the streets and building our message that we want to send to the bot.

Finally it puts our response inside a Python dictionary that also includes the chat id. Then we send this to the chat bot using requests.post(url, data) .

Deploy it to AWS

Take a look at the serverless.yml that was generated. This is your configuration file for AWS. Here is what mine looks like:

service : power-outage-bot provider : name : aws runtime : python3.7 stage : dev region : us-east-1 environment : TELEGRAM_TOKEN : ${env:TELEGRAM_TOKEN} CHAT_ID : ${env:CHAT_ID} functions : hello : handler : handler.hello events : schedule: rate(15 minutes)

I've set up my trigger to just be a schedule that runs every 15 minutes. This keeps me well under my 1 million requests per month.

Finally, we're ready to send it up to AWS. Back on your terminal, type

serverless deploy

That's it. This function should now be running every 15 minutes. The serverless framework also setup logging to AWS CloudWatch. If you go to that service in your AWS Console you should see a log section and, after some time, it should start to populate.

Tip: If you need to debug a little, any print() statement in Python will get logged.

Conclusion

Now I have an alert system to tell me when I lose power at my house. I don't have to keep a server running anywhere and it is completely free. Not bad.

Obviously this particular setup only works if you live in the same town as I do, otherwise the city power outage API is worthless to you. But hopefully this will give you some ideas on how you can make your own notification system based on some other sort of data you have access to.

References

I found these posts to be extremely helpful when I was working on this project: