A while ago I kept seeing posts from people building little apps to turn their air conditioner on from the computer. Every time, I thought the same thing: I don't want an app for the AC, I want Home Assistant sitting in my tray. One click, every device in the house, no server running in a closet.
So I made one. It's called Lilac, and the code is here:
Lilac
Control your Tuya / Smart Life gadgets from the menu bar — over your own network.
Screenshots rendered with sample data. 🇧🇷 Leia em português
Warning
This project is vibe-coded. It was built in a long conversation between a human (Tulio Calil) and an AI (Claude, by Anthropic): the human set the direction, tested on real hardware and made the calls; the AI wrote most of the code, the tests and these docs. It has a real test suite (including interop tests against tinytuya), but it is experimental — expect rough edges and read the code before trusting it with anything important.
What it does
- 🔎 Finds Tuya devices on your LAN by listening to their UDP broadcasts — no account needed for this part.
- 📱 Logs into Smart Life with a QR code (same flow as Home Assistant's official Tuya integration) to fetch device names…
It controls Tuya / Smart Life stuff, the cheap Wi-Fi bulbs, plugs, breakers and IR blasters half of us have at home. It finds them on the network, logs into Smart Life once with a QR code to grab the local keys, and after that talks to the devices directly over the LAN. Whatever can't be reached locally (Zigbee things behind a gateway, infrared remotes) goes through the cloud.
Why tinyjs
The desktop part is built on tinyjs, a small runtime (around 6 MB) that runs your backend on txiki.js and uses the system webview. Choosing it was mostly curiosity. I wanted to see if it could handle a real app.
It handled more than I expected. The tray, the OS keychain, UDP broadcast and raw TCP all work from plain JavaScript. To be sure about the networking, I compiled txiki and the test suite runs the protocol code inside it against fake devices.
Still, I kept an exit door. The protocol package doesn't know what runtime it's on, the network comes in through a small interface, and there's a Node version of it for the CLI and tests.
I also lost some time to two “Windows limitations” that weren’t real. I was sure the webview blocked CSS masks and fonts loaded from file://, and that windows there couldn’t be transparent. I even opened an issue about it. The author asked for a minimal app to reproduce it, I built one, and everything worked. I had seen the problem in a different Chromium setup and never checked on the real thing. So, lesson learned: test on the actual runtime before blaming it.
What's next
Testing local control on more real devices, more infrared remotes (TV, fans), protocol 3.5, and the thing I'm most excited about: automations tied to the computer itself, like turning off the desk light when I lock the screen.
If you have Tuya devices and something doesn't work, the most useful thing you can do is open an issue with the output of npm run control -- "device name" --inspect. That's literally how the AC support happened.

Top comments (2)
Building a desktop app with a 6MB runtime instead of Electron just to see if it works is peak dev behavior, respect.
🤣🤣🤣
Most of the credit goes to tinyjs here, it handled way more than I expected.