Friday, November 13, 2026

Controlling a $10 LED Strip From the Command Line

This is a Just the Facts (JTF) post: generated by AI from my own raw material (voice memos, session notes) and a short outline I wrote, then checked by me. What's a JTF?

Previously in this series: Review: GANZQH 100ft LED Strip Lights

A cheap, no-name LED strip settled a lighting stand-off with our nephew. Right after installing it, I wanted to know if I could control it from my own code. It turns out I can. Three versions later, ledassist can turn the strip on and off, set colors, run effects, and hold a connection open so that changes happen almost instantly.

But is it hackable?

  • The no-name LED strip neatly solved a lighting stand-off with our nephew. He kept leaving all the basement lights on, and now the strip lights the way instead.
  • It came with a remote and the MohuanLED app. The app isn't very polished, but it works.
  • Shortly after the install I had to know: is this thing hackable? In other words, could I control the LEDs from a tool I built?
  • A quick Google search for 'Mohuan LED api' turned up a promising GitHub project: MohuanLED-Bluetooth_LED.
  • ledassist follows the Bluetooth protocol as documented in bj_led.

Version 1

  • I built ledassist with Claude over a voice interface, literally while taking out the trash.
  • The command could turn the LEDs on and off, set their color, send an effect, and send a raw debug string (-a send) for experimenting with the protocol.
  • When I tried it on my Windows WSL2 setup, it didn't work.

Version 2

  • This version actually worked. I confirmed that I could control the LED strip. Success!
  • WSL2 has limited Bluetooth support, so I found a workaround. The WSL2 shell script now starts a native Windows Python script that talks to the strip, and Windows Python has no trouble seeing the Bluetooth stack. The Bluetooth side lives in led.py.
  • Building -a identify was gratifying. It finds every unknown strip, lights each one a different color, and adds it to the aliases file under that color's name. I can then look at each lit strip and rename it to something memorable in the aliases file.
  • Version 2 worked, but it was super laggy. Operations took between about 6 and 36 seconds to complete.

Version 3

  • I added -a listen, which starts a daemon (leddaemon.py). The daemon connects to the strip, keeps that connection open, and listens for incoming LED commands. I also added -a hangup to stop the daemon.
  • With the daemon running, setting the strip to a color takes only 0.09 seconds. Without it, the same command took 5.7 seconds.
  • The cost is the up-front connection. -a listen took 27 seconds in my test, and most of that was a first attempt that timed out.
  • -a listen is what turns this from a simple proof of concept into something I can actually use.

A hardware home run

  • I'm in awe. For $10.00 and a bit of Claude time, I built a new kind of hardware user experience.
  • With a bit of code, I could wire the LEDs to any event stream I can imagine: load averages on remote servers, the humidity in our basement, or how many minutes are left in my next time box.
  • The ledrun example below shows that a few lines of code can make an impressive effect. It runs any command, turns the strip white while the command runs, then turns it green if the command succeeded or red if it failed. The video below shows it in action.
  • To try this yourself:
    • Install Python 3 and bleak. On WSL2, use Windows Python and run py -m pip install bleak.
    • Put ledassist in a bin directory. Put led.py, leddaemon.py, fade.py and css-colors in a sibling libexec/ledassist directory.
    • Run ledassist -a scan. Compatible strips show up with names that start with BJ_LED.

Code

LA='ledassist -i bedroom'
ledrun() {
  $LA -a listen
  $LA -a color -n white
  eval "$@"
  if [ $? -eq 0 ] ; then
    color=green
  else
    color=red
  fi
  $LA -a color -n $color
  echo "$@: $color"
}
$LA -a color -n purple
$ ledrun "sleep 3 ; wc -l /etc/hosts"
12 /etc/hosts
sleep 3 ; wc -l /etc/hosts: green
$ ledrun "sleep 3; wc -l /etc/does-not-exist"
wc: /etc/does-not-exist: No such file or directory
sleep 3; wc -l /etc/does-not-exist: red

Behind This Post

The outline I wrote
- But is it hackable?
  - The no-name LED strip elegantly solved a lighting stand-off with
    our nephew
  - Shortly after the install I had to know:  is this thing hackable? That
    is, could I control the LEDs from a tool I built?
  - A quick google search for 'Mohuan LED api' turned up a promising github
    project
- Version 1
  - Literally while taking the trash, I worked with Claude over a
    voice interface to build =ledassist=
  - The command's interface included support for turning LEDs on,
    off, setting their color, sending an effect, as well as sending a
    raw debug string.
  - When I tried the command on my Windows WSL2 setup, it didn't
    work.
- Version 2
  - This version actually worked and I was able to verify that I could
    control the LED strip. Success!
  - I found a novel solution for dealing with the fact that WSL2 has
    limited bluetooth support. The WSL2 unix shell script now kicks
    off a native Windows Python script that talks to the strip. The
    Windows version of Python has no problem seeing the bluetooth
    stack
  - I built =-a identify= which was gratifying: this command finds
    every unknown strip and lights it a unique color. It also sets the
    device's ID to that color name in the aliases file. I can then
    visually inspect each lit strip and trivially give it a memorable
    nickname in the aliases file.
  - While functional, version 2 was super laggy. Operations take
    between about 6 and 36 seconds to complete.
- Version 3
  - I added support for =-a listen= which starts a daemon that secures
    a connection to the strip and listens for incoming LED commands. I
    also added suppot for =-a hangup= to halt the daemon.
  - With the daemon in place, setting the LED strip to a color takes
    only 0.09 seconds, down from 5.7 seconds without it
  - The cost is the up-front connection: =-a listen= took 27 seconds
    in my test, most of it a first attempt that timed out
  - =-a listen= takes this from a simple proof of concept to something
    I could actually use.
- A hardware home run
  - I'm in awe: for $10.00 and a bit of Claude time, I was able to build a novel
    hardware user experience.
  - With a bit of code, I could wire the LEDs into any event stream I
    can imagine: load averages on remote servers, the humidity in our
    basement, how many minutes I have left in my next time box.
  - The example =ledrun= below shows that a few lines of code can make
    for an impressive effect.
  - To test this yourself: install Python 3 and =bleak= (on WSL2, use
    Windows Python and =py -m pip install bleak=), put =ledassist= in
    a =bin= directory with the other files in a sibling
    =libexec/ledassist= directory, and run =ledassist -a scan=.
    Compatible strips show up with names starting with =BJ_LED=.

No comments:

Post a Comment