Simone's Blog (no JS mode)

Connecting a desk lamp to my personal website with WebSocket

Back in 2013, I was experimenting with WebSocket and Ruby Sinatra in my tiny college dorm. I immediately fell in love with the technology and the potential it could unleash! Imagine all the cool web games and superfast chat apps I could develop instead of polling every 0.5 seconds my backends. So, like every developer learning new tech, I developed a small side project called Lucetta which simply was a virtual light bulb that you could turn on and off in real time.

This super tiny project taught me a lot about the WebSocket protocol, the Reactor pattern and EventMachine in Ruby. Afterwords three years ago, I decided to embed this somewhat useless widget into my website and renamed it Lite Bulb. As I am unable to maintain a traditional Guestbook section, Lite Bulb can be used to share meaningful and creative comments. You can use binary, morse, whatever you like, the on/off is left up to the interpretation of the visitor.

A few weeks ago, in a moment of boredom and some curiosity about IoT I decided to take this project a little further. I asked myself a simple question: how hard can it be to turn a real light bulb on and off programmatically and hook it up to my WebSocket experiment? After all, IoT products have been around for years, I'm sure there's plenty of documentation on this. Well, I pretty much ended up going down a rabbit hole, thanks also to a colleague of mine who introduced me to this topic.

I was absolutely shocked by the sheer amount of vendor lock-in that these products impose on the end users. Essentially, you don't own anything, and everything has to go through some kind of gateway or cloud service to simply manage an appliance in your house. Some of these services even offer monthly payment subscriptions for such a thing, which is mental to me...
You'd probably say, 'Yeah, Simone, where have you been in the past 10 years?' And you'd be right, but still, that's crazy. Anyway, the two sane products that I ultimately bought were a Shelly light bulb and a Tasmota smart plug, as they gave me the feeling of actually being in control of the hardware. Initially, I also purchased an IKEA smart bulb, only to realize that I needed a 65€ gateway to effectively control it. Needless to say, I returned it shortly after.

diagram for connecting sebulbo to my websocket server

The Shelly light bulb worked like a charm. Connecting it to my local network was a breeze, and they offer a wonderful REST API to manage the bulb. After that, I wrote a simple Ruby script which we will call Sebulbo, this script connects to my WebSocket server and listens for content changes in my Lite Bulb protocol. It then performs a GET request to the Shelly lightbulb to change its value accordingly. The Shelly API is also very simple, you can just pass the state of the bulb to the turn parameter and authenticate via basicauth:

# status_to_change = "on" || "off"
RestClient::Request.new(
  :method => 'get',
  :url => "http://#{SMART_BULB[:ip]}/light/0/?turn=#{status_to_change}",
  :user => SMART_BULB[:user],
  :password => SMART_BULB[:password],
).execute
diagram for connecting sebulbo to my websocket server

When Sebulbo connects to the WebSocket server, it broadcasts to each client that the desk lamp is online. This is then shown on the frontend to every user connected to my website at that moment. So if I'm online, this is what you'll see  point right

The Lite Bulb Web Socket protocol is quite simple:

  1. on open it broadcasts the number of connected clients and the current light bulb status
  2. when a client sends the command USERS it returns the number of clients connected to the server
  3. when a client sends the command FLICK it toggles the bulb status on or off and returns it
  4. when sebulbo goes online it broadcasts DESK_LAMP set to true
  5. on close it broadcasts again the number of active clients

That's it. The result is cooler than I expected. Initially I thought I might encounter some latency issues, but everything works surprisingly smoothly from the WebSocket request to the Shelly API request. The light bulb is such a cute addition to my desk setup and as you probably guessed, starting today, when playing with Lite Bulb you can change my desk light as well :)

In the video above, you can see that Sebulbo makes a brief flashing animation, achieved with a series of GET requests, when new users connect to the web app or when they close the window, ending the WebSocket connection. This allows me to immediately notice if someone has connected to Lite Bulb without needing to keep my website open.

def blink_on_load(current_status)
  for i in 1..6 do
    shelly_request("http://#{SMART_BULB[:ip]}/light/0?turn=on&brightness=#{i * 10}")
  end
  shelly_request("http://#{SMART_BULB[:ip]}/light/0?turn=#{current_status}&brightness=10")
end

The code of the React widget on the frontend is available here, although it's still a bit messy and unpolished. Perhaps one day I will also release the code of my WebSocket server, but for now I'll keep it for myself because, to be honest, I'm not too proud of the quality. In the end, it's just a more elaborate fork of the Lucetta app that I mentioned earlier. Who knows, maybe Lite Bulb will evolve into a Pro Bulb™ with a monthly subscription model where you can also toggle the lamp on my bedside table.

Updated on 24 June 2024
Posted on 26 March 2024