Skip to main content

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 on 127.0.0.1:8377 (/cmd, /status, /syntax, plus the optional WLED-compat /json/state). Frontend-agnostic: its only upward link is an on_change hook.
  • 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:

  1. WLED compatibility — WLED's JSON API is 8-bit; OLS is a compatible patch.
  2. 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.
  3. 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:

  1. PyPI → Add a pending publisher (https://pypi.orgPublishing): project openlamp-lamp, owner openlamp, repo engine, workflow publish.yml, environment pypi.
  2. GitHub → Settings → Environments → Newpypi.
  3. Bump version in pyproject.toml, commit, cut a GitHub Release (tag v0.1.0) → the workflow builds and publishes. Then pip install openlamp-lamp works 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


Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

openlamp_lamp-0.1.0.tar.gz (21.8 kB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

openlamp_lamp-0.1.0-py3-none-any.whl (18.2 kB view details)

Uploaded Python 3

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

Hashes for openlamp_lamp-0.1.0.tar.gz
Algorithm Hash digest
SHA256 0f2242d405fa6956239ddd06ce57b619b6fd100bdccebda2368b51c0d4cdfbb8
MD5 17a6a7191f0eb2ca88ac0c467e36fa65
BLAKE2b-256 70d436d698070d0ac13bf5a92a01dfcacbd1a4fb0b5c92e1d2fa76042aae4867

See more details on using hashes here.

Provenance

The following attestation bundles were made for openlamp_lamp-0.1.0.tar.gz:

Publisher: publish.yml on openlamp/engine

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

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

Hashes for openlamp_lamp-0.1.0-py3-none-any.whl
Algorithm Hash digest
SHA256 1739694f11468a55b83578f9579a8776ee5f6ff5200f36699bcaccc8839b6c5e
MD5 1f6cf1ce44d4feb8d7034abe4ea9b040
BLAKE2b-256 9f22845c9aab352cac3f1c914e1b21ec0e91c8a0a2e0603b0ab0fe6a3ff218f7

See more details on using hashes here.

Provenance

The following attestation bundles were made for openlamp_lamp-0.1.0-py3-none-any.whl:

Publisher: publish.yml on openlamp/engine

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Pingdom Monitoring Sentry Error logging StatusPage Status page