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.
ledassistfollows the Bluetooth protocol as documented in bj_led.
Version 1
- I built
ledassistwith 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 identifywas 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 hangupto 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 listentook 27 seconds in my test, and most of that was a first attempt that timed out. -a listenis 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
ledrunexample 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 runpy -m pip install bleak. - Put ledassist in a
bindirectory. Put led.py, leddaemon.py, fade.py and css-colors in a siblinglibexec/ledassistdirectory. - Run
ledassist -a scan. Compatible strips show up with names that start withBJ_LED.
- Install Python 3 and
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
Resources
- bj_led: the protocol library ledassist builds on
- ledassist: scan, on, off, color, effect, fade, identify, listen and hangup
- led.py: the Bluetooth LE side of ledassist
- leddaemon.py: the daemon behind -a listen
- fade.py: the color steps behind -a fade
- css-colors: the color names ledassist understands
- MohuanLED - The LED strip App
- MohuanLED-Bluetooth_LED github project
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=.



































































