Skip to content

Latest commit

 

History

4 Commits

Folders and files

NameName
Last commit message
Last commit date
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

lume

Local control of the lamps on your desk. One static binary, no cloud, no vendor app.

Documentation →

$ lume studio on
  ● key      80 %  4500 K
  ● rim      47 %  5600 K
  ● akzent   30 %  #ff7a2f

lume 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.

The idea: profiles, not devices

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 off

That 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.

Install

brew install marsch/tap/lume            # macOS and Linux
go install github.com/marsch/lume@latest

Or 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: make

Go 1.23 or newer, and nothing else. CONTRIBUTING.md covers the development tasks, the layout, and how to add a backend.

Getting started

lume setup

That 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 off

When something else is driving

Whether 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 asking

The 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.

The pieces, separately

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 it

A 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"
}

Commands

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

For scripts and Stream Decks

--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.

Backends

Philips Hue

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.

Taking over an Elgato lamp

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.

How it works

Protocol notes, written up so the next person does not have to repeat the work:

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.

Legal and ethical position

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.

Licence

MIT. See LICENSE.

About

Local control of Elgato Key Lights and Philips Hue — one static binary, no cloud, no vendor app

Topics

Resources

Contributing

Stars

0 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages