DEV Community

Cover image for I didn't want to wait for Elgato
Mario Scheliga
Mario Scheliga

Posted on Originally published at marioscheliga.com

I didn't want to wait for Elgato

TL;DR

  • My agent checked 7 ways into my new Elgato lamp, declared it closed and suggested a smart plug.
  • One evening later the lamp trusted my own certificate authority, and lume 0.1.0 was on GitHub: one Go binary that controls Elgato Key Lights and Philips Hue from Linux and macOS, MIT.
  • When your agent says "it's closed", ask what it actually read. Then make it read the device's state instead of guessing.
  • You own the hardware. You can make it trust you.

In August two Elgato Key Light Air MK.2 arrived for my studio. The older Key Lights had an open HTTP API, and every Stream Deck plugin and Home Assistant integration is built on it. The MK.2 still answers on port 9123, but only to clients signed by Elgato's own certificate authority. No documented Elgato Key Light API, no Linux app, nothing in the community tools. My agent took one look and gave up.

You can make hardware you own do what you want, even before the vendor documents how, and a coding agent makes that an evening's work.

A missing API is almost fine with me. It just wasn't documented yet, and I'm sure Elgato is working on it. I didn't want to wait. So this is a hardware hacking story with Claude Code: what I did, what the agent did, and where it crashed the lamp.

The agent said no

On 27 August at 21:40 my agent committed a file called "the interface is closed". It was a good report. A clean table, 7 ways in, each one checked: plain HTTP on 9123 (connection closed), HTTPS without a client certificate (aborted), HTTPS with a self-signed one (TLS alert, unknown CA), other ports (none open), HomeKit and Matter (not offered), a firmware update (no change), the community projects (they only know the old, open generation).

Its suggestion: a switchable smart plug, and set brightness once in the phone app.

Fair enough, if you just want light. I wanted the lamps in my Stream Deck and in my agent's hands.

One evening of reverse engineering with Claude Code

The next evening I asked a different question: Elgato's own software has to get into the lamp somehow. Does it ship a certificate, or some kind of onboarding?

It does. The MK.2 learns whom to trust during Bluetooth pairing. So I factory-reset the rim light (it forgets the Wi-Fi, fine), put it in pairing mode and plugged it into my MacBook's power adapter, because the rim's own power source was too weak. I needed that adapter back later.

By 19:55 the agent could switch the lamp on and off. Brightness and temperature did nothing. My messages got shorter.

The format has to be in Elgato's software. Why are you guessing???

That's me at 21:16 (translated, the original has more question marks than grammar). At 21:30 Postman became our workbench: the agent wrote the collection, I clicked send and watched the lamp. At 21:43 the lamp vanished from my phone app. Every command rebooted it. The error reply said "id": "unknown".

At 22:07 I sent the agent the JSON-RPC spec. The spec allows numeric ids. The lamp's firmware crashes on them and wants strings. At 22:14 the rim reacted, instantly. My message at 22:15 was "works :) I'm so happy".

The key I didn't use

There was a shortcut. The vendor's desktop app has to authenticate somehow, so you could pull its key out and sign your own requests with it. That's roughly where my conversation got flagged for cybersecurity.

I went with my own self-signed certificate authority instead. Reset the lamp, hand it my CA over Bluetooth, and from then on it trusts me, locally and permanently. And it works. No vendor key in lume, anywhere.

Diagram: a lamp paired with the vendor app trusts only the vendor’s certificate authority, so a laptop with a self-signed certificate is locked out. After a factory reset, lume pki creates your own authority, Bluetooth pairing installs it on the lamp, and lume then talks JSON-RPC over a mutual-TLS WebSocket.
The whole trick is whose certificate authority the lamp gets during pairing.

lume: control Elgato Key Lights from the command line

At 22:19 I asked the agent to turn what we knew into a CLI we could open source, because other people with an MK.2 on Linux are stuck exactly where I was. It got the name lume at 22:45, Philips Hue came along for the ride, and lume 0.1.0 was released at 02:06 on 29 August: around 5,500 lines of Go, a Homebrew tap and a documentation site.

The idea is profiles instead of devices. One lamp or a whole studio, same command:

$ brew install marsch/tap/lume
$ lume setup          # scans the network, creates your CA, pairs with a Hue bridge
$ lume studio on
  ● key      80 %  4500 K
  ● rim      47 %  5600 K
  ● akzent   30 %  #ff7a2f
$ lume key on --brightness 80 --kelvin 4500
$ lume all off
Enter fullscreen mode Exit fullscreen mode

A Stream Deck button is just lume studio toggle. My agent uses the same commands.

How the Key Light Air MK.2 API works

After onboarding, the lamp speaks JSON-RPC 2.0 over a WebSocket on port 9123, with mutual TLS against your CA. Setting a light looks like this:

{"jsonrpc":"2.0","id":"1","method":"put.elgato.lights",
 "params":{"numberOfLights":1,"lights":[{"on":1,"brightness":30,"temperature":200}]}}
Enter fullscreen mode Exit fullscreen mode

Temperature is in mired, 143 (7000 K) to 344 (2900 K). Three things cost real time, and lume handles all of them:

  1. The id must be a string. A number reboots the lamp.
  2. put gets no answer at all, not even an error. lume sends a get right after, so the answer confirms the write and shows the state.
  3. The lamp takes one WebSocket connection at a time and frees the slot slowly. Close cleanly, don't hammer it.

The Bluetooth half of onboarding still runs through a small Python companion, keylight-local. Control and the CA are native Go. The full notes are in the protocol write-up, so the next person doesn't have to reboot their lamp all evening.

AI reverse engineering, the same month

This month people report that Claude Opus 5.5 stops mid-answer when asked to help reverse engineer a smart TV (XDA). On 26 October OpenAI's Roon said Codex has no ban on reverse engineering and the refusals weren't intended (Crypto Briefing). Anthropic hasn't said anything official.

My take from one lamp: it works without any special reverse engineering skill, and it can be useful. lume is interoperability work on hardware I own. The README says how it was done, including that it isn't clean-room in the strict sense.

If your agent says it's closed

  • Ask what it read. Mine had probed the network. It hadn't looked at how the vendor's own app gets in.
  • Make it read state after every write. "I sent the command" means nothing until a get says the lamp changed.
  • Hand it the spec. One link to JSON-RPC ended half an hour of reboots.

Want your Key Light back? brew install marsch/tap/lume, or read the code on GitHub.

Next: my glasses

The lamp was the warm-up. Next I took on my Even Realities G2 glasses, with an iPhone app in the middle, to build my own display for what my agents are doing. That one gets its own post.

Title image: AI-generated illustration.

Top comments (0)