Controlling Your Christmas From the Web With Phidgets
by InformalAbsence in Circuits > Raspberry Pi
179 Views, 1 Favorites, 0 Comments
Controlling Your Christmas From the Web With Phidgets
In my last Instructable, I showed you how you could set your tree or other Christmas lights up on an automated timer using a Raspberry Pi and a couple of Phidgets. Although having it on a timer was an easy set it and forget it, why not make it so you can also control it from bed?
Well in this Instructable I will be showing you how you can make your Phidgets controllable on the net using a Raspberry Pi, a bit of Python, HTML5 and JavaScript. If you haven't yet gone through my first instructable I recommend you do as I will be building off of it throughout this tutorial.
Supplies
- Your treelights.py Script from earlier
- 1x Raspberry Pi (With Raspberry Pi OS installed)
- 1x Phidgets Plug PSU1000_0
- 1x VINT Hub HUB0000_0
- 1x VINT Cable
- 1x USB Type-A to Mini-B
Modifying Our Original Script Pt.1
The first thing we are going to have to do is change up our original script. Originally we would just loop over and over, checking to see if we were within the scheduled time or not, and then change the status of the plug accordingly. Now however we want to listen for our webserver to tell us whether or not the plug should be on, or off.
In order to accomplish this, we are going to need to change up a few things. First things first, at the very top we are going to need to import a few extra libraries. In order to create a server that will listen for our on and off requests we need to first import http.server and sockerserver like so
from http.server import BaseHTTPRequestHandler import socketserver
Then we will scroll past our setup code for the Phidgets as we can leave everything here alone until we get to our while True loop. Here we need to delete the entire loop, as we won't be using it anymore. Instead, we are going to need to create a class called WebHandler and pass it our BaseHTTPRequestHandler like so
class WebHandler(BaseHTTPRequestHandler):
Now that our class is set up we want to listen for an HTTP post request. To do this, within our class we need to add a function do_POST(), and remember to pass it "self" as it is a class function. Putting together what we have so far it will look something like the following
class WebHandler(BaseHTTPRequestHandler): # Create function that will "do" something apon a GET request def do_POST(self):
Once this is set up every time we make a post request to our server, it will execute this do_POST(self) function, and even better, the URL that we send the post request to will be contained in self.path. That means that we can create two imaginary paths, one to turn on the lights and one to turn them off, and check them using an if statement. The following is what I used inside my do_POST(self) function,
# Create function that will "do" something apon a GET request def do_POST(self): # if request is to turn on the lights, turn them on if self.path == "/turnOn": # Turn On plug.setState(True) # Send 200 OK response self.send_response(200) # if request is to turn off the lights, turn them off elif self.path == "/turnOff": # Turn plug off plug.setState(False) # Send 200 OK response self.send_response(200) else: # Unknown request, send 404 NOT FOUND self.send_response(404)
As you can see in the code above when someone makes a post request to "HTTP://<our-server-ip>/turnON" our server is going to turn on the Phidgets Plug using the setState(True) command, and then send back a 200 OK response to the client.
Modifying Our Original Script Pt.2
We are almost done, but there is still a bit more that we have to add. Underneath the class function that we created in Step 1, we need to actually initialize the server. To do this we first need to start a socketserver and tell it what address and port to bind to. First, you will want to create a TCPServer variable called httpd and then use the sockerserver.TCPServer() to bind the address. That will look something like this
httpd = socketserver.TCPServer(("", 8081), WebHandler)
Finally, before we can run our program, we have to "serve" the connection. To do this we are first going to want to start a try-except statement, and then within our try: statement start listening at the address we previously bound. All put together you should get something that looks like the following
# Initalize socket for server listening on 0.0.0.0 port 8081 httpd = socketserver.TCPServer(("", 8081), WebHandler) try: # Start Listening httpd.serve_forever() except Exception as err: # On Err, or KeyboardInterrupt, close server so that port is not tied up forever print(f"Err: {err}, Quitting!") httpd.server_close()
This try-except loop is important, as if for any reason your program is to crash or more likely you try to stop it, it will close the server so that you can re-use the address. Otherwise, you would either have to restart your networking every time you shut down the server, or change the address that you use.
Now that everything is put together you should be able to run the script, but just in case you are having issues I have included the newly written out program below.
Downloads
Testing Our New Script
Now that we have modified our original script to listen for HTTP post requests it's a good idea to check and make sure that everything is working as intended. To start we will need to open up a terminal on our Raspberry Pi and run our script. If you put it on the desktop it might look something like this
python3 ~/Desktop/treelights.py
You'll notice that nothing appears on your terminal, and that is fine because in just a moment we will start seeing our requests. You will now need to open another terminal and type the following command
curl -X POST http://localhost:8081/turnOn
Provided that your Phidgets are plugged into your Raspberry Pi, you should hear the click of the relay inside the Phidgets Plug, and you should also see something like
127.0.0.1 - - [22/Dec/2021 13:23:12] "POST /turnOn HTTP/1.1" 200 -
Printed in your first terminal which is running the Python script. At this point, we now have our functioning backend, but it isn't exactly convenient. The whole point of having the Raspberry Pi controlling the Phidgets is for stealth, and having to keep a monitor and keyboard plugged into it isn't very stealthy.
Making a Frontend Webpage
Now that our backend is complete, we need to make a frontend webpage to control our plug from. For now, it's not going to be anything fancy, I'm not aiming for style points just yet.
To start we are going to want to make a new file called index.html, if you have some previous experience working with HTML you may recognize this as a pretty standard file, but if you have never worked with it before don't worry.
First, we are going to start with some pretty boilerplate HTML items. We will start by defining our doctype and language like so
<!DOCTYPE html> <html lang="en"> </html>
Next, inside our HTML tags, we are going to add a head and a body
<!DOCTYPE html> <html lang="en"> <head> <meta charset="utf-8"> <title>Light Remote</title> </head> <body> </body> </html>
You will notice inside the head we have defined the encoding of our characters, as well as a title. I chose to call it light remote, but you can name it whatever you want. The title is displayed at the top tab and just helps to identify what the page is without actually having to be looking at it.
Now we are going to need a little bit of JavaScript to make our post requests to the backend we created earlier. In order to do this we will create some <script></script> tags inside the HTML tags, and insert the following two functions
<script> // Turn On function function turnOn() { fetch("http://127.0.0.1:8081/turnOn", { method : "post" }); } // Turn Off function function turnOff() { fetch("http://127.0.0.1:8081/turnOff", { method : "post" }); } </script>
As you can see we are simply making a post request to the URL HTTP://127.0.0.1:8081/<turnOn/turnOff> depending on which function is executed. This is effectively what we were doing earlier with our curl requests. Just note that 127.0.0.1 is your localhost IP address, and if you want to make this accessible from outside of just your Pi you will need to change it to your Pi's local IP address. If you don't know how to do this come back to your index.html file after reading Step 6.
Now in order to tie it all together, we simply have to add an on and off button inside the body tags. That will look something like this
<body> <button id="turnOn" onclick="turnOn()">Turn On Plug</button> <button id="turnOff" onclick="turnOff()">Turn Off Plug</button> </body>
Looking into the button tags we can see that when the button is clicked, it will execute the turnOn() or turnOff() function that we made earlier. With that set, you should be able to open this HTML file in your Raspberry Pi and turn on and off your tree. Now all we need to do is make it accessible to everyone in the local network.
Just in case you're having problems I have included my index.html source file, just note that if you want to use it directly you will need to remove the .txt extension from it, or copy the internal code over to your index.html file.
Downloads
Installing NGINX
Now that we have a frontend web page and a backend server to handle our requests, we need to make it so everyone can access the webpage. To do this we are going to use a service called NGINX which will allow us to easily host a local webserver.
You can find the full list of instructions on how to install and troubleshoot NGINX here, but I also walk you through how to install it on your Raspberry Pi. To start, you will need to install the prerequisites
sudo apt update; sudo apt install curl gnupg2 ca-certificates lsb-release debian-archive-keyring -y
Next you will need to import an official NGINX gpg key so that we can verify that what we downloaded is in fact NGINX
curl https://nginx.org/keys/nginx_signing.key | gpg --dearmor \ | sudo tee /usr/share/keyrings/nginx-archive-keyring.gpg >/dev/null
If you wish you can verify the authenticity of the key, but as it's not exactly needed I won't be doing it. Next we will need to set up the apt repository by running
echo "deb [signed-by=/usr/share/keyrings/nginx-archive-keyring.gpg] \ http://nginx.org/packages/debian `lsb_release -cs` nginx" \ | sudo tee /etc/apt/sources.list.d/nginx.list
Next we are going to pin the repository so that we download from the newest stable repository first
echo -e "Package: *\nPin: origin nginx.org\nPin: release o=nginx\nPin-Priority: 900\n" \ | sudo tee /etc/apt/preferences.d/99nginx
And finally, we are going to install NGINX by running
sudo apt update; sudo apt install nginx -y
Testing and Configuring NGINX
Now that we have installed NGINX, we want to make sure it's running. First in your terminal type
ip -c -h a
And find your local IPv4 address. It will usually either start like 10.X.X.X or 192.168.X.X. Then you should be able to type "http://<your-ip-address>" where your-ip-address is the address which you found above, in my case 192.168.1.110 and you will land on the default NGINX start page. Now we can replace it with our own web page.
To start, on your Pi you will need to navigate to the location of your index.html which by default will be in www. You should be able to run the following command
cd /var/www/html/; ls
and you will see something like "index.nginx-debian.html". Now all we have to do is move our index.html file from earlier to here, and delete the current one. You can do this all in one swoop provided you are in the www/html directory by running
sudo mv <path-to-your-index.html> .;sudo rm index.nginx-debian.html
You should now be able to refresh the page and see our two buttons, Turn On Plug and Turn Off Plug, and provided your Phidgets are still plugged in and your Python script is running you should finally be able to click the button on the website, and hear your plug's relay click.
Plug Back Into Your Tree
Now all that's left to do is plug back in your Phidgets Plug, in-between the wall socket and tree, and then give your Raspberry Pi power from a separate outlet. Once your Pi is powered up you will need to check its IP address one last time, as well as start your Python script as outlined below and you should be good to go.
python3 ~/Desktop/treelights.py &
Now you can turn on and off your Christmas lights from anywhere in the house using Phidgets.
Conclusion
This Instructable ended up much longer than the first one, and also a lot more technical, so I really appreciate those of you who stuck through it and gave it your all. Dealing with web services on a local network is a pain, so if you have any suggestions on how to make this project better, or easier to understand please let me know.
As always the source files for the project have been linked so if your code is giving you trouble you can give mine a shot. Thank you for taking the time to read through the article, and Happy Holidays!