DEV Community

Cover image for I wanted Home Assistant in my system tray, so I built Lilac
Tulio Calil
Tulio Calil

Posted on

I wanted Home Assistant in my system tray, so I built Lilac

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:

GitHub logo tuliocll / lilac

Your Tuya / Smart Life home in the tray

Lilac

Control your Tuya / Smart Life gadgets from the menu bar — over your own network.

Moonlight theme — air conditioner page Past Dream theme — device list High Contrast theme — colour light page

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)

Collapse
 
alexcodebytes profile image
Oleksandr •

Building a desktop app with a 6MB runtime instead of Electron just to see if it works is peak dev behavior, respect.

Collapse
 
tuliocalil profile image
Tulio Calil •

🤣🤣🤣
Most of the credit goes to tinyjs here, it handled way more than I expected.