Build the future of communications.
Start building for free

How I Potty Trained My Kid Using Twilio and an AWS IoT Button

AWS IoT Button for Potty Training

As a dad of 2 young kids, a non-trivial portion of my day is devoted to poop. Lots and lots of poop.

When my oldest began potty training, they would wake up in the middle of the night needing to use the bathroom. And while toddlers’ lungs make a great alarm, we needed to find a solution that wouldn’t disturb the younger one sleeping 10 feet away.

I’m always on the lookout for how I can win my kids’ cooperation in novel and fun ways.  Bonus points if it involves getting to tinker on an engineering project myself.

Enter the Poop Button.

Behind the smiling emoji facade is an Amazon IoT button, a variant of Amazon’s dash button. When my kid presses this button, it triggers an AWS Lambda function that uses Twilio’s Python Helper Library to call my iPhone from a Twilio number. The Twilio number is stored in my contacts with “emergency bypass” turned on, so even when it’s 2am and I’m on “do not disturb” I still get the call.

Before we start: IoT poop button prerequisites

Let’s walk through setting one up – here’s what you’ll need to build your very own poop button.

Buy a AWS IoT Button

The hardware behind the IoT Poop Button is the 2nd Generation AWS IoT Button. You can purchase your own through this link.

Set up a local dev environment

To write the code for the Lambda, you’ll need a computer with Python 3.6 installed. If you’re on a Mac that came with Python 2, I recommend installing 3.6 via Homebrew:

brew install python

If you have Python 2 installed as well, make sure nothing on your PYTHONPATH
points to your Python 2 packages:

if [ $(echo $PYTHONPATH | grep python2) ]; then export PYTHONPATH=; fi

Sign up for a Twilio account

If you don’t have an account already, sign up for Twilio. After you’ve signed up, buy a number. Make a note of the phone number you’ve bought, your Account SID, and your Auth Token. You’ll need them all later.

Now, go to the list of Verified Caller IDs and make sure the number you want the poop button to call is listed. If not, add it.

Sign up for an AWS account

Sign up for AWS if you haven’t already. Note, an AWS account is not the same as an Amazon account, so even if you have an Amazon account already, you’ll need a separate AWS account.

Make a phone call with Twilio in Python

All set? Ok, time to get down to the code!

Project directory and dependencies

Because Lambdas have no way to manage code dependencies, we have to vendor all of our dependencies into our project directly. Then once everything’s ready, we create a zip file that we can upload to AWS when configuring our Lambda function.

For this function, our only Python dependency is Twilio’s client library. Let’s configure our project directory and install it:

mkdir twilio_poop_button
cd twilio_poop_button
printf "[install]nprefix=" > setup.cfg
pip3 install twilio --target .

You should now see a bunch of subdirectories and Python files in your project directory. Remember, twilio has its own dependencies, so don’t be surprised to see things like requests and pytz.

(The setup.cfg bit is only required if you installed python 3 via Homebrew. It circumvents a nasty bug in Homebrew’s python.)

Python phone call source code

Now, create a file called and paste this code into it:

import os
from urllib.parse import urlencode

from import Client

def handler(event, context):
    client = Client(

    call = client.calls.create(
            'Message[0]': "Baby needs to go potty",


The astute reader will notice 4 variables taken from the environment:

  • TWILIO_ACCOUNT: The Account SID you copied from your Twilio account dashboard
  • TWILIO_TOKEN: The Auth Token you copied from the same place
  • TWILIO_PHONE: The phone number you purchased (In E.164 Format)
  • ALERT_PHONE: The phone number you want the button to call (Also in E.164 format)

The script instantiates a Twilio client and creates a new call using these environment variables. For the content of the call, we’re using a Simple Message Twimlet, a URL that takes a list of messages in its query string and returns a TwiML document. Twilio reads that document and generates a voice message from it.

Try out a phone call with Python and the Twimlet

Let’s give our code a shot locally to make sure it’s working. Remember to substitute the fake values below with your real ones:

TWILIO_ACCOUNT=xxxx TWILIO_TOKEN=xxxx ALERT_PHONE= 15017122661 TWILIO_PHONE= 15555555555 python3 -c 'import index; index.handler(None, None)'

Did your phone ring? If not, double check that the environment variables are correct and take a look at your Twilio phone number’s call logs to see if the call showed up. If you’re using a Twilio trial account, make sure the alert phone number is listed in your Verified Caller IDs.

If it did congratulations, you have a working Twilio integration! The only remaining step on the code side is to prepare the zip file to upload to AWS:

zip -r ~/ .

AWS IoT and Lambda integration

If you haven’t already, open up your shiny new Amazon IoT button. Take note of the DSN printed on the back. (It probably starts with “G030”.) This is your button’s identifier, and you’ll see the number on a lot of the auto-generated resources AWS creates for you.

Set up the button using a native phone app

Next, install and launch the AWS IoT Button Dev iOS or Android app to configure your AWS IoT button. I have an iPhone, so the screenshots you see are from the iOS app. Here’s how to connect your button using the app:

  1. Click the button to Set Up AWS IoT Button iOS Native App to register IoT Button
  2. Enter the button DSN and give it a name (or, if available, you can scan the box barcode)
  3. Hold the button for 6 seconds until the light starts flashing blue Enter an Amazon IoT Button DSNPairing process for a phone and Amazon IoT button
  4. Tap the “copy password and go to settings” link, connect to the SSID that was displayed in the app, and paste the password it put in your clipboard to connect to the button’s wifi network
  5. Go back to the app. It should recognize that you’re on the button’s wifi and switch to asking you for your home wifi network’s SSID and password. Configuring a WiFi network for the IoT button to use
  6. After that, you’ll set the button’s action. Since we haven’t created our Lambda function yet, let’s start by creating one of their boilerplate functions. Choose “Send Email (python)” and hit “Set action”. Basic python demo with the Amazon IoT button
  7. Enter your email address in the box, and you should be all set.Amazon IoT button send email with Python

Give it a whirl. Push the button. You should see the light blink white and then turn green after the request succeeds. The first time through, you’ll probably get an email from AWS asking you to verify your email. Even that email is proof enough that the button worked, but if you click through the verification link and push the button again you should receive an email with the subject “Hello from your IoT button”.

Create a new Lambda function

Log into your AWS console and search for the Lambda service.

Create a Lambda function

You should see the function we just created in the phone app. Click on the “Create function” button.

Search for a created Lambda function

Choose “Author from scratch”, name your function, choose Python 3.6 as the runtime, and “Create new role from template(s)” for role. Then name your role and choose “AWS IoT Button Permissions” from the Policy templates dropdown. Finally, click “Create function”. A loading indicator will spin for a bit, and then you’ll be taken to your new function.

Role and naming for a poop button Lambda function

Configure the function’s trigger

In the designer, choose AWS IoT from the list of triggers on the left.

AWS IoT as a Lambda trigger

Below, configure the trigger. Choose “Custom IoT rule” as IoT type and find the rule created when we created our initial function. (You should see the DSN in the rule name.) Make sure “Enable trigger” is checked and click “Add”.

Configuring a Lambda trigger

Upload the source code archive

Back up in the designer, click on the Lambda function and you should see a “Function code” section show up blow the designer. Change “Code entry type” to “Upload a .ZIP file”, make sure the runtime is set to Python 3.6, and enter “index.handler” as the Handler path. Then click the “Upload” button and select the zip file we created earlier. (~/

Upload a zip file to Lambda

Configure environment variables

Below the “Function code” section, you should see an “Environment Variables” section. Fill this section out with the same 4 variable names and values we used earlier for local testing. Encrypting the variables with a KMS key is recommended but beyond the scope of this post.

Enter environment variables to Lambda

Now, save all of your changes by scrolling to the top of the window and clicking the “Save” button.

Where to 'save' a Lambda function

Configure and run a test event

At the top of the window, click the “Select a test event” drop-down and choose “Configure test events”. We don’t care about the test input, so the default “Hello world” event will work just fine. Name it and click “Create”.

Configure a Lambda test event

Now click the “Test” button. You should see an execution result. (Hopefully a successful one which includes a phone call.) If your phone didn’t ring, check to make sure the code was actually uploaded and your environment variables are copied correctly using the same values you used to test locally.

Successful Lambda function execution

Try out the button!

At this point, you should have a fully configured button. Go ahead and give it a push!

Did your phone ring? If not, check to make sure the AWS IoT trigger doesn’t say it needs configuration, its identifier contains the DSN on the back of your button, and it’s enabled.

If it rang – congratulations. You have a poop button!

You may have noticed that you received both a phone call and an email. IoT buttons can trigger multiple Lambda functions at once. If you’d like to disable the email, either delete the original Lambda function or disable its AWS IoT trigger.

Troubleshooting a toddler

The great thing about this approach is all of the technology is built and operated by 3rd parties, and pretty reliable ones. In months of daily use, I haven’t yet seen the hardware or software fail. But any parent knows that kids are tough QAers, so let me propose some tips for common failure modes you’re likely to experience.


My kid had the button when they went to sleep, but now it’s [lost in the blanket | tossed across the room | stuck between the mattress and the bed]. They have to pee and are freaking out because they can’t find it!

Adhesive velcro strips work wonders. Stick one to side to the back of the button and the other side to the wall or a part of the bed they’re not likely to knock it off. Make sure it’s close enough to easily reach but far enough away to avoid accidental presses.


My kid pushes the button after being in bed for 15 minutes as an excuse to get out of bed.

Yeah, mine wised up to this one pretty fast. Now we’re very intentional about giving them multiple chances to go to the bathroom right before bed, but we keep the button with us until they’re asleep. Then one of us sneaks in and puts it in its place. They’ve since learned to use the opportunity to go before bed.


My kid refuses to push the button. When we ask them why, they respond “I chose not to.”

Believe it or not, we initially had slow button adoption. We normalized button use by requiring our kid to push the button in order to get out of bed in the morning. Of course, this hastened the realization that it was a “get out of bed” pass, which led to the issue above.

Poop is as poop does: living with the poop button

Our poop button is a life saver. Kid 1 is pretty consistently waking with a dry pull-up, and kid 2 is getting more continuous sleep in the absence of a sibling yelling for Mom and Dad. It’s amazing the difference a small hack can make.



On Twitter, I’ve been encouraged to do everything from apply to be on Shark Tank to start a Kickstarter, but I think if anyone is going to make a business out of a button with a poop emoji on it, it’s going to be @internetofshit:


As for AWS, whether they’re chagrined or awed at the crappy repurposing of their technology, I think it’s fair to say they didn’t see it coming:


For my part, I’ll continue to try to apply practical (ok, and sometimes not so practical) engineering to dad-problems. Whether that’s using Python, Twilio, AWS IoT and Lambda to prevent night-time emergencies or using InfluxDB and a Raspberry Pi to monitor temperature with a Grafana front end I’ve always got multiple projects going. If you have any bright ideas, drop me a line – maybe we can work on something together!

David has been developing web applications in python for over a decade. Originally from Texas, he’s been living in San Francisco for the past 6 years working in the tech startup scene. David currently works at Clara Labs on Clara, a scheduling service that combines the precision of machine intelligence and the judgement of an expert team. You can find him on Twitter, on Github, or on his bicycle.

Sign up and start building
Not ready yet? Talk to an expert.