How to Build Your Own Smart Home in an Afternoon

11 min read Cameron Akhavan on Nov 15, 2018
Try PubNub Today

Free up to 1MM monthly messages. No credit card required.

Subscribe to our newsletter

By submitting this form, you are agreeing to our Terms and Conditions and Privacy Policy.

As we approach 2019’s advent of new Internet of Things technologies, smart homes and home automation have become increasingly common rather than inaccessibly luxurious. Numerous IoT products have rise...

As we approach 2019’s advent of new Internet of Things technologies, smart homes and home automation have become increasingly common rather than inaccessibly luxurious. Numerous IoT products have risen, like Google Clips Amazon Alexa, to provide easy-to-install smart home technologies to even the least technologically-savvy consumers. However, with so many products available today, that doesn’t mean you can’t do it yourself.

At PubNub we specialize in bridging the wondrous world of IoT to anyone who seeks it. With our numerous APIs and simplistic programmable Data Stream Network, one can avoid spending tons of money on products by building their own. While it may seem like an intimidating DIY path, we will show you that you can intellectualize your home with instructions no more complicated than an IKEA manual.

We’ll show you exactly what we mean by walking you step-by-step in building three fun and simple DIY smart home projects.


Let’s start by asking, what makes a home smart? The answer is simple: automation and centrality. A household must automate all of its basic tasks and be able to transmit information to and from a central location (typically a mobile app).

We’re going to tackle these two dogmas of IoT in building these three technologies using PubNub:

  • Smart Light – automation of lights
  • Light Detector – data centralization
  • Door Detector – data centralization

Required Materials

Raspberry Pi

In order to program our IoT technologies and have them communicate between you and your home, you’re going to need what’s called a microcontroller. While there are many options for microcontrollers, perhaps the easiest one to use is the Raspberry Pi3.

If you’ve never set one up before, the instructions are incredibly simple and can be found here.


In order to make sure that we can plug in any electronic (no matter how strong the voltage) into our RPI without frying it, we’ll need what’s called a relay. A relay is essentially a switch that will be able to handle high current and voltages so that the RPI doesn’t have to.


A Lamp

You’ve heard of a lamp. To make a smart lamp, you’ll obviously need a lamp; however, since this will be your first time building a connected lamp, make sure you use a cheap lamp you don’t care about in case you mess up.


In order to build a smart light sensor, you’ll need a phototransistor. Like the relay, the phototransistor acts as a switch. The phototransistor will only allow current to flow through it in the presence of light, so this will be a perfect sensor to see if your house lights are on when you don’t want them to.


Tin Foil and Wire

In order to create a door sensor, we’re going to use tinfoil contact pads that allow electricity to pass if the pads come into contact. This way, we can attach a pad to a door and its frame so that everytime the door is closed, the pads make contact, and let the Raspberry Pi know that our door is closed.

Smart Home Smart Home

Hardware Setup

Smart Lamp

In order to make a smart lamp out of any ordinary lamp, you need to splice the power cable into two parts (when unplugged of course) and strip some of the casing to expose the wires. Every lamp has a power cable (typically red) and a ground cable (typically black).

Build Your Own Smart Home

Now grab your relay and connect the ground wires back together, connect one end of the power wire into the NO (Normally Open) port of the relay, and the other end into the NC (Normally Closed) port of the relay.

Build Your Own Smart Home

Now in order to connect the lamp-relay module to the Raspberry Pi, connect them to the GPIO pins of the Raspberry Pi like so:


Light Sensors

The light sensor circuit is much more simple to implement. Simply wire your phototransistor to one of the RPI’s GPIO pins like so:


Door Sensor

For the door sensor, just wire one pad up to a GPIO pin on the RPI and the other pad to ground (for diagram purposes we represent pads as LEDs).


Let’s Get Coding!

Before you do anything, make sure you sign up for a free PubNub account in the form below. This is how you get your pub/sub keys.

Step 1: Enable SSH

Secure Shell protocol (SSH) allows users to remotely access their Raspberry Pi terminals from their computers over WiFi. If supplemented with GitHub, users can push code from their computers to a remote repository, SSH into the Pi terminal, and pull the code wirelessly. This allows for a quicker and smoother development process.

To enable SSH on your RPI, follow the simple and quick instructions.

Step 2: Download Device Libraries and Dependencies

Raspberry Pi GPIO Library

For our devices, we need to operate an output voltage of 1 or 0 from the RPI to turn things on. Conversely, our sensors give the RPI an input voltage 1 or 0, depending on whether it is wet or dry. Both of these functions require the use of the RPI’s GPIO pins.

Since we’ll be coding in Python, it will be useful to install the GPIO Zero library, which will allow us to make simple function calls to activate or read in the pins.

NOTE: Remember that these libraries must be installed onto the RPI by SSH-ing into your device and running the commands below.

sudo apt install python-gpiozero

Step 3: Build Your App with PubNub

The full code for this project is located in this repository for reference.

Let’s create a python script to interface our sensors to the internet. Open up a new document in your favorite text editor and let’s begin!

First, let’s import our libraries and other dependencies to enable the devices and PubNub functionality.

#---------------Library Setup----------------#
import pubnub
from pubnub.pnconfiguration import PNConfiguration
from pubnub.pubnub import PubNub
from pubnub.callbacks import SubscribeCallback
from pubnub.enums import PNOperationType, PNStatusCategory
from gpiozero import Button, LED
from time import sleep
#----------------PubNub Setup----------------#
pnconfig = PNConfiguration()
pnconfig.subscribe_key = "YOUR SUBSCRIBE KEY"
pnconfig.publish_key = "YOUR PUBLISH KEY"
pnconfig.ssl = False
pubnub = PubNub(pnconfig)

Now, we need to tell our Raspberry Pi which devices are connected to which of its pins:

#------------Sensor Declarations-------------#
#lamp is connected to GPIO4 as an LED
lamp = LED(4)
#door sensor is connected to GPIO3 as a Button
door_sensor = Button(3)
#light sensor is connected to GPIO14 as a Button
light = Button(14)

NOTE: we declare the lamp as an LED as we want to control it as if it were a simple LED. We initialize the sensors as buttons as they output a constant active signal in the same way a button does when held down or pressed. This is the magic of the GPIO Zero library as it is very simple to interface with sensors and devices.

We should also create a counter variable to let us know how many times our door has been opened:

#door counter
doorCount = 0

In order to make sure that our IoT lamp can communicate with a client, we need to enable PubNub’s 2-way pub/sub capability. To make sure we can receive messages we need to add a listener to handle the incoming messages and declare a subscription to listen in on a particular channel.

class MySubscribeCallback(SubscribeCallback):
    def status(self, pubnub, status):
        # The status object returned is always related to subscribe but could contain
        # information about subscribe, heartbeat, or errors
        # use the operationType to switch on different options
        if status.operation == PNOperationType.PNSubscribeOperation \
                or status.operation == PNOperationType.PNUnsubscribeOperation:
            if status.category == PNStatusCategory.PNConnectedCategory:
                # This is expected for a subscribe, this means there is no error or issue whatsoever
            elif status.category == PNStatusCategory.PNReconnectedCategory:
                # This usually occurs if subscribe temporarily fails but reconnects. This means
                # there was an error but there is no longer any issue
            elif status.category == PNStatusCategory.PNDisconnectedCategory:
                # This is the expected category for an unsubscribe. This means there
                # was no error in unsubscribing from everything
            elif status.category == PNStatusCategory.PNUnexpectedDisconnectCategory:
                # This is usually an issue with the internet connection, this is an error, handle
                # appropriately retry will be called automatically
            elif status.category == PNStatusCategory.PNAccessDeniedCategory:
                # This means that PAM does allow this client to subscribe to this
                # channel and channel group configuration. This is another explicit error
                # This is usually an issue with the internet connection, this is an error, handle appropriately
                # retry will be called automatically
        elif status.operation == PNOperationType.PNSubscribeOperation:
            # Heartbeat operations can in fact have errors, so it is important to check first for an error.
            # For more information on how to configure heartbeat notifications through the status
            # PNObjectEventListener callback, consult <link to the PNCONFIGURATION heartbeart config>
            if status.is_error():
                # There was an error with the heartbeat operation, handle here
                # Heartbeat operation was successful
            # Encountered unknown status type
    def presence(self, pubnub, presence):
        pass  # handle incoming presence data
    def message(self, pubnub, message):
#message handler for Lamp commands
        #Turn the lamp on if client receives the message “ON”
        if message.message == 'ON':
              #let your subscriber client know that the lamp has been turned off
        	pubnub.publish().channel('ch1').message("lamp has been turned on").async(publish_callback)
        #Turn the lamp on if client receives the message “OFF”
        elif message.message == 'OFF':
              #let your subscriber client know that the lamp has been turned off
        	pubnub.publish().channel('ch1').message("lamp has been turned off").async(publish_callback)
#make sure to subscribe to the channel of your choice. In this case, we #chose to create a channel called “ch1” to publish to

Take note of the message event handler. Any time our program receives a message from our application client, we open up the message using “message.message”. We then create event handlers with if statements to either turn the lamp on or off with the GPIO command .on() or .off().

Lastly, notice that we publish a message back to the client, letting the user know that we have completed their request. In this case, we let the client know whether the lamp is on or off.

This part is optional (since we are not using it for this program), but you can create a publisher callback to execute a function every time you publish a message.

def publish_callback(result, status):
    # Handle PNPublishResult and PNStatus

The last part of our program is going to constantly check the sensor inputs to see if our sensors detect anything. We can achieve this by creating a main while the loop repeatedly checks the inputs.

while True:

Within the main while loop, we can create sub-while loops to poll the sensors specifically. Since we initialized our sensors as Buttons from the GPIO Zero library, we must call the function .is_held.

Although we are not using a button, our sensors act as if they were buttons as they simulate the same signal of holding down a button, while they are active (detecting something)

#polling loop to check if the door sensor is on
while door_sensor.is_held:
      #increment the door counter to count how many times the door has been opened
  doorCount = doorCount + 1
  door = "door has been opened: " + str(doorCount) + "times"
door = "door is closed"
#polling loop to check if the lights are on
while light.is_held:
  pubnub.publish().channel('ch2').message("lights are on").async(publish_callback)
pubnub.publish().channel('ch2').message("lights are off").async(publish_callback)

Here we check if the sensor is active. If so, we publish a message back to the client letting it know whether the sensor is on or off.

Step 4: How to Upload Your Code

In order to upload your code to your Raspberry Pi, you should always commit your code to your repository and then pull it to your Raspberry Pi. The process always follows this order

1.) Save your code on your computer.

2.) Open up your terminal and enter your working directory. Then commit your code to the cloud using these commands.

git add .
git commit -m "updates"
git push

3.) SSH into your RPI and cd into the proper directory.

cd <your directory>

4.) Pull the code from the cloud and run it.

git pull
python <Your program>.py

Step 5: Build a Client with PubNub

Next, we need to build a client script that will allow us to use a mobile phone or computer to send and receive data to our sensors. To do this we will need to create an HTML file that will use buttons to publish commands to the RPI and Text boxes to retrieve data from the RPI.

Start with a blanked HTML script like this:

<!DOCTYPE html>
    //your Code Here

Then we’ll create two buttons to publish ON or OFF commands to our RPI.

<button type="button" onclick="publish('ON')">Lamp ON</button>
<button type="button" onclick="publish('OFF')">Lamp OFF</button>

We should also create an ID tag to later inject the light sensor data we subscribe from the RPI.

<p id="lights">Lights are on?</p>

We must not forget to import the PubNub JavaScript SDK so we can actually enable pub/sub capabilities.

<script src=""></script>

We need to also set up our channel using our given pub/sub keys like so:

var pubnub = new PubNub({
  publishKey : 'pub-c-2d8f55f6-daa7-467b-923b-6a1e6570c9fc',
  subscribeKey : 'sub-c-1575c412-2116-11e8-a7d0-2e884fd949d2',
  function publish(a){
    var publishConfig = 
      channel : "ch1",   //Your publishing channel name
      message : a
    pubnub.publish(publishConfig, function(status, response){
      console.log(status, response);

Lastly, we need to listen in on the channel the RPI is publishing our light sensor data. We can instantiate a listener with this block of code:

        message: function(m) {
            // handle message
            var channelName =; // The channel for which the message belongs
            var channelGroup = m.subscription; // The channel group or wildcard subscription match (if exists)
            var pubTT = m.timetoken; // Publish timetoken
            var msg = m.message; // The Payload
            var publisher = m.publisher; //The Publisher
            document.getElementById("lights").innerHTML = msg;
        presence: function(p) {
            // handle presence
            var action = p.action; // Can be join, leave, state-change or timeout
            var channelName =; // The channel for which the message belongs
            var occupancy = p.occupancy; // No. of users connected with the channel
            var state = p.state; // User State
            var channelGroup = p.subscription; //  The channel group or wildcard subscription match (if exists)
            var publishTime = p.timestamp; // Publish timetoken
            var timetoken = p.timetoken;  // Current timetoken
            var uuid = p.uuid; // UUIDs of users who are connected with the channel
        status: function(s) {
            var affectedChannelGroups = s.affectedChannelGroups;
            var affectedChannels = s.affectedChannels;
            var category = s.category;
            var operation = s.operation;
        channels: ['ch2'],

Take note that we subscribed to ch2. This is because we want a dedicated channel for our light sensor data so that we do not need to actually differentiate the data from other sensors. If you forgot to publish to a dedicated channel in the Application process from before, make sure you go back and do so.

Once you save your script, open the file up in a web browser and you should have something like this:


You have now completed the steps into implementing three simple smart home devices on a Raspberry Pi! Test your code out by ssh-ing into your Raspberry Pi to run your python code and then interact with your client program. You should be able to control your smart lamp with the two buttons as well as seeing text prompts if your door is open or your lights are on!

If you like our IoT content, we have many more IoT-related tutorials below!

More from PubNub

Top Trends to Engage Your App Users
InsightsSep 27, 20224 min read

Top Trends to Engage Your App Users

Take a look at the top trends that are the most effective in attracting customers and reducing churn, and how you can incorporate...

PubNub Staff

PubNub Staff

Comparing Game Engines: Unity vs Unreal vs the Rest
Real-Time Gaming BlogSep 21, 20225 min read

Comparing Game Engines: Unity vs Unreal vs the Rest

Comparing the major game engines: Unity vs Unreal Engine vs Corona SDK vs GameMaker Studio, including the benefits and cons of...

PubNub Staff

PubNub Staff

Python Socket Programming: Client, Server and Peer Libraries
BuildSep 21, 20226 min read

Python Socket Programming: Client, Server and Peer Libraries

Sockets (aka socket programming) enable programs to send and receive data, bi-directionally, at any given moment. This tutorial...

PubNub Staff

PubNub Staff