Lights

This is a website I made in a few hours that controls my WiFi connected lightbulb. It uses a custom API written in PHP on the back-end and uses JavaScript (and jQuery) to make requests to the API. The website can be used locally and remotely allowing me to turn my lights on and off from anywhere in the world. A request to the API is made every 2.5 seconds checking the status of the lightbulb, making any changes to the DOM necessary to reflect that status. This allows for cross-device live updates, meaning if I have the site open on my laptop and phone, I can turn my lights off on one device and see that change on the other device without having to refresh the page. The code above acts as the initialization to the main page. It first checks the IP of the user to see whether or not they're on the local network, it then checks whether or not they're an admin, followed by checking whether or not the guest mode is enabled for the website and finally checking what device the user is on. If the user is on the local network and guest mode is enabled, then they are allowed in. If the user is an admin, the other two conditions don't matter. If the user is an admin, then two different sections are shown on the website that are otherwise hidden. The first section allows the user to enable or disable the guest mode. The second section is just a debugging area that records the AJAX responses that are received from the API. Even the admin cannot see this section as it must be manually enabled since it's not useful outside of adding or removing features and ensuring everything's working correctly. This is the main function used by the API to communicate with the lightbulb. First of all, the back-end script double checks to make sure the user is on the local network and the guest mode is enabled or that the user is an admin. The function takes four different arguments. The first one is the IP address of the lightbulb, the second is the network port being used, the third is the action that's being performed and the fourth one is the parameters associated with the aforementioned action that's being performed. So for example, the action could be "set_power" and the parameter would be "on", which would turn the light on. The PHP function then connects to the lightbulb using "fsockopen". The stream is then set to non-blocking mode to allow the "fgets" call to immediately return data without waiting for it to be available. Afterwards, an array is set up that will eventually contain elements that constitute the action that the user wants to perform. The function's arguments are then added to the array and it is written to the socket connection. "fflush" is then used, forcing data to be written immediately rather than storing it all in a buffer until it is ready to be written. The script then waits a second for a response and then puts the response into an array and closes the connection. Finally, the function returns a JSON encoded version of the response provided by the lightbulb as an output. The code above is part of the API and is responsible for actually calling the main function and determining what the user wants to do. The user's desired action is acquired via an AJAX POST request. There are various "if" statements set up to deal with the user's request. This section of the API code handles the admin-exclusive functions such as enabling/disabling guest mode or changing the lightbulb's configuration (such as IP address and network port). The "get-config" action is also part of the admin exclusive features and it essentially returns a JSON encoded array that specifies the current IP address and port of the lightbulb and whether or not guest mode is enabled. The JavaScript of the main page then parses the JSON object and adds its content to the appropriate elements in the DOM. The JavaScript of the website starts by defining two functions. One that checks whether or not the lightbulb is turned on, and another that requests the lightbulb's configuration from the API. If the light is turned on, the "on" button's text is colored blue. Accordingly, if it's off, the "off" button's text is colored blue. The light's power status is also added to the admin-exclusive debugging area called "console". In the second function, the configuration given by the API is parsed (since it's in JSON format) and its data is added to the DOM. Similar to how the "on" and "off" button are colored blue, the same is done for the guest mode setting's "on" and "off" buttons. These two functions are called immediately, with the "power_status" function being called every 2.5 seconds. The code above handles the "click" events for the various buttons. The ID attribute of these elements determines their corresponding action. This way, their class name can be used and the script doesn't need to listen for clicks from each individual button. Each category of action has a different defining class that determines what element the script listens to for a click event. The code could be compressed further to only include listener events specifically for the on and off buttons as well as a class for the rest of the buttons, but separating them makes it much easier to add features and isolate the categories. This piece of code handles the AJAX call that can be made to the API in order to change the lightbulb's configuration. The two input fields the script gets its values from are only visible and usable by an admin. After the configuration is changed, the two functions I described in the beginning are called to ensure the API can actually communicate with the newly defined device. The image above depicts the finished website's mobile version. The entire website is extremely responsive and can go all the way down to 350 pixels in width before the elements start colliding with one another. And this image shows the website's desktop version. In both images, the admin areas are visible. For guest users, the dark bar at the top (where the input fields for the IP and port are) isn't visible; neither is the "Settings" block at the bottom. Update: I have completely changed this web app's UI to something more stylish and detailed as shown in the image above. The PHP API remains the same.