How to Build Your Own Smart Home in an Afternoon

PubNub Developer Relations - Feb 20 - - Dev Community

The Internet of Things (IoT) has significantly transformed over the years, and smart homes are becoming increasingly prevalent. With the latest advancements, homeowners now have access to a variety of smart home technologies, ranging from sophisticated security systems to intelligent home assistants.

New products and technologies have emerged, such as SmartThings and Apple HomeKit-compatible devices alongside Spotify-enabled smart speakers like HomePod, and Z-Wave and Zigbee powered devices. Even routers now come with smart home support. Despite these changes, this article still offers valuable insights for anyone interested in developing smart home projects.

Learn more about how PubNub supports thousands of customers worldwide in our PubNub for Developers resources. You can also find several customer success stories that illustrate the effectiveness of our offerings in the real-world. Feel free to reach out to devrel@pubnub.com if you have any suggestions or questions regarding the content of this article.

Nowadays, IoT technologies have made smart homes and home automation more common. Homeowners now use advanced products like Ring's suite of devices for home security. This range includes motion sensors, video doorbells, smart locks, and state-of-the-art security cameras integrated with smart home products powered by SmartThings and Apple HomeKit.

Smart home systems can also assist homeowners in their daily routines. Products like Nest Thermostat can auto-regulate temperatures, while smart displays like Lenovo's Smart Display can show personalized content. Philips Hue enables homeowners to control light switches through smart bulbs, while Apple HomePod and Spotify enabled Bose products offer smart speaker functionalities. Voice control management is also possible using personal assistant devices like Google Home, Amazon Alexa, Amazon Echo, or Siri, which facilitate control over various IoT devices along with setting up timers. Despite the expansive list of existing smart home devices, this tutorial guides you in developing your own smart home system using PubNub.

At PubNub, we specialize in bringing the realm of IoT closer to the masses. With our real-time data API and Virtual Spaces Platform, you can depend on PubNub to power your smart home hub. Let’s explore how you can create an intuitive and efficient smart home system with three smart home technologies.

Let's Build a Smart Home System

What differentiates a smart home from a regular one? Two crucial factors: automation and centrality. A smart home automates basic tasks and can transmit information both ways from a central location, typically a home app. In this tutorial, we'll use PubNub to implement these two pillars of IoT and build the following technologies:

  • Smart Light – automation of lights

  • Light Detector – data centralization

  • Door Detector – data centralization

Requirements

For creating these three IoT technologies that constitute a smart home system, we need the following materials and hardware:

Raspberry Pi

To program our IoT technologies and allow them to communicate between you and your home, you need a microcontroller. One of the easiest microcontrollers to use is the latest version of the Raspberry Pi, which, as of January 2022, is Raspberry Pi 4. If you’ve never set one up before, the instructions can be found on the product's website.

Relay

To protect our Raspberry Pi from high voltages that could damage it, we'll need a relay. A relay is essentially a switch that can handle high currents and voltages so that the RPi doesn’t have to.

A Lamp

To create a smart lamp, you’ll need a simple, inexpensive lamp.

Phototransistor

For building a smart light sensor, you’ll need a phototransistor. This component functions as a switch, allowing current to flow only in the presence of light, making it an ideal sensor to determine if house lights are on.

Tin Foil and Wire

To create a door sensor, you will need to use tin foil contact pads that allow electricity to pass if the pads come into contact. This way, you can attach a pad to a door and its frame so that every time the door is closed, the pads make contact, and let the Raspberry Pi know that our door is closed. You'll need to create two tin foil pads, with wires connected to each of the pads. Attach one pad to a door, and the other to the frame using painter's tape.

Now, let's move ahead to the hardware setup.

Hardware Setup

Smart Lamp

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).

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.

Now in order to connect the lamp-relay module to the RPi, connect them to the GPIO pins of the RPi.

Light Sensors

The light sensor circuit is much more simple to implement. Wire your phototransistor to one of the RPI’s GPIO pins.

Door Sensor

For the door sensor, wire one pad up to a GPIO pin on the RPi and the other pad to ground (for diagram purposes the pads are represented as LEDs).

Design the System

Before you begin implementing the code behind, make sure you sign up for a free PubNub account, as you'll need your publish/subscribe keys to send information across the PubNub Network.

Enable SSH

Secure Shell protocol (SSH) allows users to remotely access their RPi 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.

Download Device Libraries and Dependencies

For our devices, you need to operate an output voltage of 1 or 0 from the RPI to turn things on. Conversely, the sensors give the RPi an input voltage of 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 you’ll be coding in Python, it will be useful to install the GPIO Zero library, which will allow you 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
Enter fullscreen mode Exit fullscreen mode

Build Your App with PubNub

The full code for this project is located in this repository for reference. Create a python script to interface our sensors to the internet. Open up a new document in your favorite text editor and import the libraries and other dependencies to enable the devices to communicate on the PubNub Network.

#---------------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)
#--------------------------------------------#
Enter fullscreen mode Exit fullscreen mode

You need to tell the 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)
#--------------------------------------------#
Enter fullscreen mode Exit fullscreen mode

NOTE: The lamp is declared as an LED as you want to control it as if it were a simple LED. The sensors are initialized 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 effortless to interface with sensors and devices.

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

#door counter
doorCount = 0
Enter fullscreen mode Exit fullscreen mode

In order for our IoT lamp to interact with a client, we need to utilize PubNub’s two-way pub/sub capability. This requires adding a listener to manage incoming messages and configuring a subscription to listen in on a specific 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 signifies no error or issue exists
            elif status.category == PNStatusCategory.PNReconnectedCategory:
                # This usually occurs if subscribe temporarily fails but reconnects. This indicates there was a temporary error but it has already been resolved
            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. A retry will be initiated automatically.
            elif status.category == PNStatusCategory.PNAccessDeniedCategory:
                # This indicates that PAM does not permit this client to subscribe to this channel and channel group configuration. This constitutes a specific error
            else:
                # This is usually an issue with the internet connection, this is an error, handle appropriately. A retry will be initiated automatically.
        elif status.operation == PNOperationType.PNSubscribeOperation:
            # Heartbeat operations can indeed encounter errors, so it is essential to verify if there's an error first. For more detailed information on how to configure heartbeat notifications through the status PNObjectEventListener callback, please refer to [PNConfiguration heartbeat config](https://www.pubnub.com/docs/sdks/javascript/api-reference/pn-configuration)
            if status.is_error():
                # There was an error with the heartbeat operation, handle here
            else:
                # Heartbeat operation was successful
        else:
            # Encountered unknown status type
Enter fullscreen mode Exit fullscreen mode

In the above callback, presence handles incoming presence data, while message handles Lamp commands. We turn the lamp on if the client sends the message "ON". Conversely, if the message is "OFF", we switch the lamp off.

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':
        lamp.on()
        #let your subscriber client know that the lamp has been turned on
        pubnub.publish().channel('ch1').message("lamp has been turned on").sync()
        sleep(3)
    #Turn the lamp on if client receives the message OFF
    elif message.message == 'OFF':
        lamp.off()
        #let your subscriber client know that the lamp has been turned off
        pubnub.publish().channel('ch1').message("lamp has been turned off").sync()
Enter fullscreen mode Exit fullscreen mode

We then ensure that our program is adding the MySubscribeCallback(), and is subscribed to the chosen channel, in this case, "ch1".

pubnub.add_listener(MySubscribeCallback())
#make sure to subscribe to the channel of your choice. In this case, we chose to create a channel called ch1 to publish to
pubnub.subscribe().channels('ch1').execute()
Enter fullscreen mode Exit fullscreen mode

Take note of the message event handler. Whenever your program receives a message from the application client, you parse the message using message.message. You then create event handlers using if statements to either turn the lamp on or off with the GPIO command .on() or .off() .

Lastly, recognize that when a message is published back to the client, the user is informed that their request has been successfully executed. In this case, you let the client know whether the lamp is on or off.

This section is optional (since you are not using it for this program), but a publisher callback can be created to execute a function every time a message is published.

def publish_callback(result, status):
    pass
    # Handle PNPublishResult and PNStatus
Enter fullscreen mode Exit fullscreen mode

The final segment of the program will continuously check the sensor inputs to discern if the sensors detect anything. A 'router' of sorts, the main while loop repeatedly checks the inputs: while True:\. Within this main while loop, sub-while loops can be constructed to poll the sensors specifically. Since the sensors have been initialized as Buttons from the GPIO Zero library, the function .is\_held\ is required.

Even though we are not using a button, sensors act as if they were buttons as they simulate the same "signal of holding down a button" when they are active (i.e., detecting something). There are several snippets of code that poll the door sensor and the lights, checking their status. If the sensor is active, a message is published back to the client letting it know whether the sensor is on or off. Similar principles can be applied to other smart home products, such as SmartThings, Apple HomeKit devices, or even your HomePod, significantly extending the capabilities of your smart home.

Uploading Your Code

To upload your code to your Raspberry Pi, always commit your code to your repository and then pull it to your Raspberry Pi. Open up your terminal, enter your working directory, and commit your code to the cloud using these commands.

git add .
git commit -m "updates"
git push
Enter fullscreen mode Exit fullscreen mode

SSH into your Raspberry Pi and navigate to the appropriate directory.

ssh pi@<YOUR IP ADDRESS>
cd <your directory>
Enter fullscreen mode Exit fullscreen mode

Then, pull the code from the cloud and run it. Maybe while you wait, take some time to listen to your favorite Spotify playlist or set timers for your tasks. Multitasking efficiency is one of the beauties of a connected world!

git pull
python <Your program>.py
Enter fullscreen mode Exit fullscreen mode

Building a Client with PubNub

Next, we need to construct a client script that will enable you to use a mobile phone or computer to send and receive data to and from our sensors. This requires creating an HTML file that uses buttons to publish commands to the Raspberry Pi and text boxes to retrieve data from the Raspberry Pi. This can be accomplished with a simple HTML script and addition of a few buttons, ID tags, and the PubNub JavaScript SDK. This system is flexible enough to integrate with various protocols, including Z-Wave and ZigBee, opening up additional areas for development.

<!DOCTYPE html>
<html>
  <body>
    <button type="button" onclick="publish('ON')">Lamp ON</button>
    <button type="button" onclick="publish('OFF')">Lamp OFF</button>
    <p id="lights">Lights are on?</p>
    <script src="https://cdn.pubnub.com/sdk/javascript/pubnub.4.20.2.js"></script>
    <script>
    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);
        });
      }
Enter fullscreen mode Exit fullscreen mode

Lastly, listen in on the channel where the Raspberry Pi is publishing our light sensor data. We can instantiate a listener with the following block of code:

    pubnub.addListener({
            message: function(m) {
                // handle message
                var channelName = m.channel; // 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;
                console.log(msg);
            },
            presence: function(p) {
                // handle presence
                var action = p.action; // Can be join, leave, state-change or timeout
                var channelName = p.channel; // 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;
            }
        });
        pubnub.subscribe({
            channels: ['ch2'],

        });
    </script>
  </body>
</html>
Enter fullscreen mode Exit fullscreen mode

Take note that you subscribed to ch2. This is because you want a dedicated channel for the light sensor data so that you do not need to actually differentiate the data from other sensors. If you forgot to publish to a dedicated channel in the Application process 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 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 see text prompts if your door is open or your lights are on. You've successfully created a smart home system using connected devices communicating with PubNub.

If you would like to learn more about how to power your IoT applications and smart home systems, take a look at our IoT resources.

If you have any other questions or concerns, please feel free to reach out to devrel@pubnub.com.

. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .