One-shot CLI to control Tuya smart lamps locally (colour, brightness, on/off) — the OpenLamp/LumiDeck command-line frontend.
Project description
OpenLamp Engine
The core layer of the OpenLamp family: instant, 100% local control of smart LED lamps (WLED recommended, Tuya also supported), exposed through a stable command contract — OpenLamp State (OLS), a WLED-compatible JSON state patch (see OLS.md).
Part of the OpenLamp family:
| Layer | Repo | Role |
|---|---|---|
| Engine (this repo) | openlamp/engine |
drivers + dispatcher + local API + daemon + CLI (Python reference) |
| Engine, JS port | engine-js | same contract on Node/tuyapi — for JS-first environments |
| Stream Deck frontend | lumideck | keys, dials, live status on an Elgato Stream Deck |
| MIDI frontend | openlamp-midi | stage control from physical MIDI controllers |
Install the lamp CLI (PyPI)
The one-shot command-line frontend is published as openlamp-lamp:
pip install openlamp-lamp
lamp rouge # set colour (keeps brightness)
lamp bri:60 # brightness 1-100
lamp veilleuse # a brightness preset (lueur/veilleuse/tamise/moyen/fort/max)
lamp on | off
It finds your lamp config (with local keys) from $LUMIDECK_LAMPS, else
~/.config/openlamp/tuya-lamps.json (template at the bottom of this README). If the
LumiDeck plugin/daemon is running, the CLI routes through its local API
(127.0.0.1:8377) for instant response; otherwise it drives the lamps directly.
Only the CLI (
lamp.py) ships on PyPI. The engine + headless daemon stay in this repo (they couple to a local file layout — a clean engine package is a follow-up).
What's inside
engine.py— the engine: one thread per lamp with a persistent connection (sub-200 ms commands), the OLS dispatcher, groups, snapshots, animations (cycle/flash/tempo), connect-time sync, a rainbow welcome sweep, and the local API on127.0.0.1:8377(/cmd,/status,/syntax, plus the optional WLED-compat/json/state). Frontend-agnostic: its only upward link is anon_changehook.daemon.py— headless host: runs the engine without any frontend app.run-headless.sh— one command to switch to CLI/MIDI-only mode.lamp-doctor.sh(macOS) — one-command diagnosis of "lamps unreachable", testing the three causes in order: Mac on the wrong Wi-Fi / router down / lamp powered off or radio-napping. Never trust the router's web panel (it renders from service-worker cache even with the router dead) — this script is the ground truth.lamp.py— the CLI (also Bome-callable):lamp.py vert,lamp.py bri:40… Routes through the local API when a host runs, drives lamps directly otherwise.com.benlab.lumideck-daemon.plist— launchd autostart for the daemon.OLS.md— the OpenLamp State contract.TUYA-KEYS.md— how to get your lamps' local keys (official Tuya cloud API, one-time).
One host at a time — the rule
Every host binds port 8377 (and a Tuya lamp additionally accepts only one local connection).
So run either the Stream Deck plugin (it embeds this engine in-process) or
daemon.py — never both. Deck sessions → plugin; CLI/MIDI-only sessions → daemon.
Why 8-bit values (0–255)
OLS uses 8-bit for brightness and per-channel color, for three reasons:
- WLED compatibility — WLED's JSON API is 8-bit; OLS is a compatible patch.
- It matches the hardware — RGB LEDs are driven 8 bits per channel (16.7 M colors); Tuya's internal 0–1000 scale adds no perceptible precision.
- It matches perception — ~1 % brightness steps are at the threshold of what the eye distinguishes; 256 levels cover that. MIDI frontends (7-bit, 0–127) scale up ×2 — plenty for stage cues.
Config
tuya-lamps.json sits next to lamp.py (never committed — it contains your local
keys). Template:
{
"lamps": [
{"name": "L1", "mac": "aa:bb:cc:dd:ee:ff", "device_id": "…", "local_key": "…",
"ips": {"192.168.1": "192.168.1.50"}}
],
"groups": {"front": ["L1"]},
"sync": {"enabled": true, "state": {"on": true, "col": [0, 100, 200], "bri": 153}}
}
Test hardware (budget setup)
The engine and LumiDeck are developed and tested on this cheap, off-the-shelf rig — reproduce it for well under €40 for a two-lamp stereo stage:
- Bulbs — Athom WLED 7 W Color Bulb (E27, ESP32-C3, RGB + tunable white, WLED-preflashed, ~€13 each). WLED ships already flashed — put the bulb on Wi-Fi and it's auto-discovered. This is the reference bulb every timing figure is measured on (~45 ms/command, the latency-comp floor beatsync uses).
- Socket / holder — TobeBright E27 corded lamp holder with inline switch (up to 100 W, a few € on Amazon). Turns a bare bulb into a standalone plug-in stage lamp — no fixture required, just screw the bulb in and plug it to mains.
Publishing to PyPI (maintainer)
openlamp-lamp publishes via Trusted Publishing (OIDC) — no token in the repo.
.github/workflows/publish.yml builds + publishes on each GitHub Release. One-time:
- PyPI → Add a pending publisher (https://pypi.org → Publishing): project
openlamp-lamp, owneropenlamp, repoengine, workflowpublish.yml, environmentpypi. - GitHub → Settings → Environments → New →
pypi. - Bump
versioninpyproject.toml, commit, cut a GitHub Release (tagv0.1.0) → the workflow builds and publishes. Thenpip install openlamp-lampworks everywhere.
Credits
Made by @Beennnn (OpenLamp) with the help of Claude. WLED is the recommended, tested path (validated on Athom RGBCW bulbs, ~45 ms/command). Feedback: open an issue on lumideck.
Project details
Release history Release notifications | RSS feed
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file openlamp_lamp-0.1.0.tar.gz.
File metadata
- Download URL: openlamp_lamp-0.1.0.tar.gz
- Upload date:
- Size: 21.8 kB
- Tags: Source
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.12
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
0f2242d405fa6956239ddd06ce57b619b6fd100bdccebda2368b51c0d4cdfbb8
|
|
| MD5 |
17a6a7191f0eb2ca88ac0c467e36fa65
|
|
| BLAKE2b-256 |
70d436d698070d0ac13bf5a92a01dfcacbd1a4fb0b5c92e1d2fa76042aae4867
|
Provenance
The following attestation bundles were made for openlamp_lamp-0.1.0.tar.gz:
Publisher:
publish.yml on openlamp/engine
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
openlamp_lamp-0.1.0.tar.gz -
Subject digest:
0f2242d405fa6956239ddd06ce57b619b6fd100bdccebda2368b51c0d4cdfbb8 - Sigstore transparency entry: 2164763844
- Sigstore integration time:
-
Permalink:
openlamp/engine@eab1d5b5ef2b00438b25ca552809bb4acb307218 -
Branch / Tag:
refs/tags/v0.1.0 - Owner: https://github.com/openlamp
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
publish.yml@eab1d5b5ef2b00438b25ca552809bb4acb307218 -
Trigger Event:
release
-
Statement type:
File details
Details for the file openlamp_lamp-0.1.0-py3-none-any.whl.
File metadata
- Download URL: openlamp_lamp-0.1.0-py3-none-any.whl
- Upload date:
- Size: 18.2 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.12
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
1739694f11468a55b83578f9579a8776ee5f6ff5200f36699bcaccc8839b6c5e
|
|
| MD5 |
1f6cf1ce44d4feb8d7034abe4ea9b040
|
|
| BLAKE2b-256 |
9f22845c9aab352cac3f1c914e1b21ec0e91c8a0a2e0603b0ab0fe6a3ff218f7
|
Provenance
The following attestation bundles were made for openlamp_lamp-0.1.0-py3-none-any.whl:
Publisher:
publish.yml on openlamp/engine
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
openlamp_lamp-0.1.0-py3-none-any.whl -
Subject digest:
1739694f11468a55b83578f9579a8776ee5f6ff5200f36699bcaccc8839b6c5e - Sigstore transparency entry: 2164763867
- Sigstore integration time:
-
Permalink:
openlamp/engine@eab1d5b5ef2b00438b25ca552809bb4acb307218 -
Branch / Tag:
refs/tags/v0.1.0 - Owner: https://github.com/openlamp
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
publish.yml@eab1d5b5ef2b00438b25ca552809bb4acb307218 -
Trigger Event:
release
-
Statement type: