Local control of the lamps on your desk. One static binary, no cloud, no vendor app.
$ lume studio on
● key 80 % 4500 K
● rim 47 % 5600 K
● akzent 30 % #ff7a2flume talks to Elgato Key Lights over their mutually-authenticated TLS WebSocket and to Philips Hue over the bridge's CLIP v2 API. Both are on your own network, so lamps keep working when the internet does not.
A profile names one lamp or several, and every verb takes a profile. Controlling one light and controlling a whole studio are the same shape of command:
$ lume key on --brightness 80 --kelvin 4500
$ lume studio on # key, rim and the accent light, each at its stored values
$ lume all offThat is the whole model. A profile can also remember what each lamp should be doing, which turns a lighting setup you spent an evening dialling in into one command — and into one Stream Deck button.
brew install marsch/tap/lume # macOS and Linux
go install github.com/marsch/lume@latestOr take a binary, a .deb, an .rpm or an .apk from
releases. No runtime dependencies;
the binary is the whole program.
On macOS, a binary downloaded through a browser carries a quarantine flag and
Gatekeeper will refuse to run it — clear it with
xattr -d com.apple.quarantine ./lume, or use Homebrew, which does not set one.
From source:
git clone https://github.com/marsch/lume && cd lume
go build ./... # or: makeGo 1.23 or newer, and nothing else. CONTRIBUTING.md covers the development tasks, the layout, and how to add a backend.
lume setupThat is the whole of it. setup scans the network, works out which lamps are
actually usable and which are waiting on something only you can do, offers to
create your certificate authority and to pair with a Hue bridge while you hold
its button down, then names everything it found and writes the config.
$ lume setup
Scanning the network for 3 s…
● keylight-2399 192.168.1.131 ready off
! keylight-99da 192.168.1.29 not trusted reachable, but it does not accept this certificate
● hue 192.168.1.53 ready 10 rooms, 2 zones, 34 lights
room Büro → buero
light Hue go 1 → hue-go-1
…
Needs you
· The lamp at 192.168.1.29 is probably still paired with the Elgato app.
Factory-reset it, then run: lume onboard "<your wifi>"Every lamp is written down individually — each Elgato, each Hue room, each zone
and each single light — so anything you own can be named on the command line.
Existing entries are never touched, so running setup again after adding a lamp
is safe.
$ lume ls
$ lume buero on --brightness 40
$ lume all offWhether setup asks questions is decided the way Unix tools have always decided
it: a terminal on both ends means a person is there. Piped or redirected — a
script, a cron job, an agent — it asks nothing and changes nothing, and reports
instead.
lume setup --json # the same findings as data
lume setup --write # save them without askingThe JSON carries a next array: exactly the actions that need a human hand
(press this button, factory-reset that lamp), so an automated caller can do
everything else and hand back a short, precise list.
setup is a front end over commands that also stand alone:
lume discover # just look, change nothing
lume pair # hue: application key from the bridge
lume pki # elgato: your own certificate authority
lume onboard "MyWiFi" # elgato: take over a factory-reset lamp
lume config # where the config lives and what is in itA config, if you would rather write it yourself:
{
"lamps": {
"key": { "type": "elgato", "host": "192.168.1.29", "certs": "~/.config/lume" },
"rim": { "type": "elgato", "host": "192.168.1.131", "certs": "~/.config/lume" },
"akzent": { "type": "hue", "host": "192.168.1.53", "id": "Büro" }
},
"profiles": {
"studio": {
"key": { "brightness": 80, "kelvin": 4500 },
"rim": { "brightness": 47, "kelvin": 5600 },
"akzent": { "brightness": 30, "color": "#ff7a2f" }
},
"elgatos": ["key", "rim"]
},
"default": "studio"
}lume <profile> <verb> [options] act on one or more lamps
lume <command> [options] set things up
| verb | |
|---|---|
on |
turn on — --brightness 0-100, --kelvin, --color |
off |
turn off |
toggle |
flip; off lamps come back at their profile values |
brightness |
set 0-100, or step with +10 / -10 |
temperature |
colour temperature in Kelvin, clamped to what the lamp can reach |
color |
#rrggbb or a name — Hue only, the Key Lights are white |
identify |
blink, so you can tell which lamp it is |
status |
read back what the lamps are doing |
info |
what the device says about itself |
watch |
stream changes as they happen |
| command | |
|---|---|
setup |
scan, check what is usable, and write a config |
discover |
find lamps and bridges over multicast DNS |
pair |
get an application key from a Hue bridge |
pki |
create your own certificate authority (Elgato) |
onboard |
teach a factory-reset Elgato lamp your certificate |
ls |
list the lamps and profiles in your config |
config |
show where the config lives and what is in it |
backends, version, help |
Shorthands that save typing: lume key on 80 4500 is the same as
--brightness 80 --kelvin 4500, several lamps can be named at once with
lume key,rim off, and lume on key works if that is the order your fingers
reach for.
Anything can be done without a config file:
lume --host 192.168.1.29 --certs ~/.config/lume on --brightness 60
lume --host 192.168.1.53 --id "Hue go 1" color blue--json prints machine-readable results and the exit code is 1 if any lamp
failed. --quiet prints nothing but errors. A Stream Deck button is just
lume studio toggle.
Point id at a light, a room or a zone, by the name shown in the Hue
app, by its old v1 number, or by its v2 uuid — all three resolve. A room controls
every bulb in it through one command, so a whole room is a perfectly good "lamp"
as far as a profile is concerned.
Names are not unique across kinds: a room and a bulb inside it are often both
called "Schlafzimmer". Prefix the kind to say which you mean —
"id": "room:Schlafzimmer" — and lume setup writes that prefix automatically
wherever it would otherwise be ambiguous.
The application key lives in ~/.config/lume/hue-token (or $LUME_HUE_TOKEN,
or a path in the config's token field) — not in the config file, so the config
is safe to keep in a dotfiles repo.
The Key Light Air MK.2 dropped the old open HTTP API. What replaced it is a JSON-RPC WebSocket that will not talk to a client without a certificate the lamp trusts, and lamps paired with the vendor app trust only the vendor's chain.
lume's answer is to make the lamp trust you: lume pki generates your own
certificate authority, and onboarding installs it on a factory-reset lamp over
Bluetooth LE together with your Wi-Fi credentials. After that the lamp is yours,
locally, permanently, with no vendor key material involved anywhere.
Onboarding is a one-time, hardware-proximate ritual, and this build delegates the Bluetooth half to a tested Python companion rather than shipping a second, less proven BLE stack:
pipx install keylight-local
keylight-local pki && keylight-local onboard "MyWiFi"Then point lume at the certificates it wrote. Control and PKI are fully native.
Caution while experimenting: a malformed request can crash the firmware's JSON parser and reboot the lamp. If that lamp is the one lighting your face on a call, that is not a theoretical concern. lume always sends the shape the firmware expects; hand-rolled requests may not.
Protocol notes, written up so the next person does not have to repeat the work:
- Elgato Key Light Air MK.2 — mutual-TLS WebSocket, JSON-RPC framing, the string-id trap, BLE onboarding
- Philips Hue — CLIP v2, pairing, mirek, gamut, the event stream
Adding a backend means implementing one interface in internal/lamp:
Set, Identify, Close, plus optional Reader, Applier, Describer and
Watcher for the things a given device happens to support. Register it under a
type name and it becomes available to profiles and to --type.
This is interoperability work. The protocols were reconstructed from observed device behaviour and from the vendor's own desktop application; the firmware itself was not disassembled, and no vendor source code or key material is reproduced here. It is not a clean-room reimplementation in the strict sense, which would require the analysis and the implementation to be done by separate people — they were not.
lume works only with credentials you generate yourself. That is the whole reason onboarding exists, rather than the easier shortcut of borrowing a vendor signing chain, which was deliberately not taken.
Not affiliated with or endorsed by Elgato, Corsair or Signify.
MIT. See LICENSE.