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.

Two things that cost me some time on Windows: the webview blocks CSS masks and web fonts when loading from file://, so icons and fonts get embedded as data URIs. And windows there can't be transparent, so anything rounded or with a drop shadow shows white edges around 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.