Yeelight Web Controller
A Python REST bridge that exposes Yeelight LAN-control bulbs through the same wire contract as a matter_webcontrol v0.25 logical bridge. A Matter server can register this service and federate Yeelight bulbs alongside its native Matter devices — no embedded scripts, no exec().
- REST — control bulbs by IP-derived ID, get / set Matter-style level (0-254) and mireds (153-500)
- Federation-ready —
/api/devicesand/api/metadata(api_version"2") matchLogicalBridgeClient - Discovery resilient — TCP probe fallback when SSDP multicast is blocked or bulbs stop broadcasting
Quick start
pip install yeelight-web-controller # or: pip install -e .
# Generate an API key (used by all clients via X-API-Key header)
export YEELIGHT_SRV_KEY=$(openssl rand -hex 32)
# Start the server (auto-probe LAN /24 if SSDP returns nothing)
yeelight-srv --host 0.0.0.0 --port 9800 --auto-probe
# In another terminal — talk to it
curl -H "X-API-Key: $YEELIGHT_SRV_KEY" http://127.0.0.1:9800/api/devices
Requires Python 3.12+. Bulbs must have LAN Control enabled in the Yeelight Classic app.
How it works
┌─────────────────────┐ ┌──────────────────────┐
│ matter-srv │ │ REST clients │
│ (federation peer) │ │ curl / app │
└──────────┬──────────┘ └──────────┬───────────┘
│ X-API-Key │ X-API-Key
▼ ▼
┌────────────────────────────────────────────────┐
│ yeelight-srv (FastAPI) │
│ ┌─────────────┐ ┌────────────────────┐ │
│ │ SSDP cache │ │ TCP/55443 probe │ │
│ └──────┬──────┘ └──────────┬─────────┘ │
└─────────┼──────────────────────┼───────────────┘
▼ ▼
yeelight bulbs /24 subnet scan
(port 55443) (fallback)
- Bulbs are identified by
capabilities.idfrom SSDP, oryeelight_<ip>when SSDP is unavailable. Both forms are stable across server restarts viacache.json. /api/devicesand/api/metadatamirror the schema thatmatter_webcontrol'sLogicalBridgeClientconsumes (states.{on_off, brightness_raw, color_temp_mireds},names: list[str],bridge.api_version: "2").- Authentication is a single
X-API-Keyheader. Same key used by federation peers when they register this server via/api/bridge?…&api_key=…on the matter side.
CLI options
| Flag | Default | Description |
|---|---|---|
--port |
9800 |
REST port |
--host |
127.0.0.1 |
Bind address. Use 0.0.0.0 to expose on LAN (api-key strongly recommended) |
--api-key |
$YEELIGHT_SRV_KEY |
Required header value. If unset and --host 0.0.0.0, a warning is logged |
--seed-ip |
(none) | Pre-register a bulb by IP at startup. Repeatable, or comma-separated. Also reads $YEELIGHT_SEED_IPS |
--probe-subnet |
(none) | TCP-scan this CIDR (e.g. 192.168.1.0/24) for bulbs at startup. Repeatable |
--auto-probe |
off | When SSDP returns no bulbs and the cache is empty, lazily TCP-scan the local /24 on first request |
REST API
All endpoints require X-API-Key: $YEELIGHT_SRV_KEY (when --api-key is set).
Devices are addressed by their id field (the SSDP capabilities.id, or yeelight_<ip> if seeded without SSDP). Aliases set via /api/name are display-only and not accepted as IDs.
Error mapping: 404 (device unknown), 400 (bad parameters), 401 (auth), 500 (other).
Read
| Method & Path | Description |
|---|---|
GET /api/devices |
Federation feed — every device with id, endpoint_id, states, names |
GET /api/lights |
Human view: kelvin + percent, includes IP and primary alias |
GET /api/level?id=… |
Read raw brightness (0-254). Add &level=N (or POST) to set |
GET /api/mired?id=… |
Read color temperature in mireds. Add &mireds=N (or POST) to set |
GET /api/metadata |
Declarative bridge info (bridge.api_version: "2", capabilities + states) |
Control
| Method & Path | Body / Params |
|---|---|
POST /api/set |
{"id":"…","brightness":0.0–1.0,"temperature":Kelvin} — both fields optional |
POST /api/level |
{"id":"…","level":0–254} |
POST /api/mired |
{"id":"…","mireds":153–500} (clamped to Matter spec) |
# 80 % warm white on a bulb
curl -H "X-API-Key: $YEELIGHT_SRV_KEY" \
-X POST -H "Content-Type: application/json" \
-d '{"id":"yeelight_192.168.1.7","brightness":0.8,"temperature":2700}' \
http://127.0.0.1:9800/api/set
/api/set brightness < 0.01 activates Yeelight Moonlight where the bulb supports it.
Management
| Method & Path | Params |
|---|---|
POST /api/name |
{"id":"…","name":"…"} — append alias (multiple per device) |
GET /api/name/remove?id=&name= |
Remove a single alias |
GET /api/refresh |
Re-probe every cached IP |
GET /api/seed?ips=192.168.1.7,192.168.1.236 |
Add IPs to cache without SSDP |
GET /api/probe?subnet=192.168.1.0/24 |
TCP-scan a CIDR (subnet optional → local /24) |
Federation with matter_webcontrol
Pair this server with a running matter-srv so Yeelight bulbs appear alongside native Matter devices:
# On the Yeelight bridge host (e.g. 10.0.0.20)
export YEELIGHT_SRV_KEY=keyY
yeelight-srv --host 0.0.0.0 --port 9800 --auto-probe
# On the matter-srv host — register this bridge as a logical peer
curl -H "X-API-Key: $MATTER_SRV_KEY" \
"http://127.0.0.1:8080/api/bridge?ip=10.0.0.20&port=9800&api_key=keyY"
# matter-srv now exposes the Yeelight bulbs through its own endpoints:
curl -H "X-API-Key: $MATTER_SRV_KEY" http://127.0.0.1:8080/api/lights
matter-srv's LogicalBridgeClient calls /api/devices, /api/level, /api/mired, and /api/set directly over plain HTTP — no script execution, no events blobs.
Tests
The test suite drives a real bulb. Skipped by default unless an IP is provided.
pip install -e '.[test]'
export YEELIGHT_TEST_IP=192.168.1.7 # required
export YEELIGHT_TEST_ID=0x000000002ce4355f # optional; auto-discovered if unset
export YEELIGHT_TEST_KEY=optional-secret # optional; used by the live-server auth test
pytest -v
Coverage: /api/devices, metadata v2 schema, level (set / get / clamp), mireds (set / get / clamp), /api/set float brightness, alias add / remove, X-API-Key auth, plus an end-to-end test that mirrors the HTTP calls LogicalBridgeClient issues.
Known issues
- SSDP discovery returns empty for long-running bulbs. Yeelight bulbs only emit SSDP
NOTIFYframes for a short window after power-on. After thatdiscover_bulbs()returns[]even though TCP/55443 still works. Use--auto-probe,--seed-ip, or/api/probeto bypass SSDP. - Bulbs occasionally drop off the LAN (~every 2 weeks). Symptom: the bulb stops answering on TCP/55443 and ignores commands. The only known recovery is a hardware power-cycle (cut mains for ~10 s, then restore). The Yeelight LAN protocol does not expose a reboot / reset command, so this cannot be triggered from the web interface — recovery requires either a smart plug upstream of the bulb or someone flipping the wall switch.
- One subnet per host.
--auto-probeand/api/probe(withoutsubnet=) scan only the /24 of each local interface. Multi-VLAN setups should pass--probe-subnetexplicitly per network.
Limitations
- Only color-temperature and dimmable LAN-control Yeelight bulbs are tested. RGB-only models are not exercised by the current control mappings.
- The Yeelight LAN protocol is unauthenticated on the wire —
--api-keyonly gates the REST front, not the bulb-side TCP. Anyone on the same L2 segment as a bulb can still control it directly on port 55443.
Metadata
Release files for yeelight-web-controller 0.5.0
For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.
Source distribution (sdist)
| File | Size | Uploaded | Provenance | Actions |
|---|---|---|---|---|
| yeelight_web_controller-0.5.0.tar.gz | 16.4 kB | With provenance | Details for yeelight_web_controller-0.5.0.tar.gz |
Built distribution (wheel)
| File | Interpreter | ABI | Platform | Actions Reset |
|---|---|---|---|---|
| yeelight_web_controller-0.5.0-py3-none-any.whl | Python 3 | none | any | Details for yeelight_web_controller-0.5.0-py3-none-any.whl |
Total release size: 29.1 kB
Release files / yeelight_web_controller-0.5.0.tar.gz
| Download URL | yeelight_web_controller-0.5.0.tar.gz |
|---|---|
| Size | 16.4 kB |
| Tags | Source |
|
SHA-256 checksum How to use checksums |
9e72d4148894918c087007749a775a32e71b8a8c14f1ad89a2b252ee67f12338
|
|
BLAKE2b-256 checksum How to use checksums |
cf919f2e39d144e542d7fd4c553e21e39e082885350b2329f8923bb62f2b6433
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
twine/6.1.0 CPython/3.13.12
|
Provenance
Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.
PyPI Publish Attestation
PyPI verified that this artifact, at this checksum, originated from the publisher listed below.
Signed by GitHub Actions, verified by PyPI on May 4, 2026.
Transparency logRelease files / yeelight_web_controller-0.5.0-py3-none-any.whl
| Download URL | yeelight_web_controller-0.5.0-py3-none-any.whl |
|---|---|
| Size | 12.6 kB |
| Tags | Python 3 |
|
SHA-256 checksum How to use checksums |
75a00b2159f7a9866e1865dabed3ea1f2753b43d14ba3eced5c45b2eb48c612d
|
|
BLAKE2b-256 checksum How to use checksums |
90245101dc24f674f8818bef8352de948236066d1a9e626ab3d3bc9de8e8e910
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
twine/6.1.0 CPython/3.13.12
|
Provenance
Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.
PyPI Publish Attestation
PyPI verified that this artifact, at this checksum, originated from the publisher listed below.
Signed by GitHub Actions, verified by PyPI on May 4, 2026.
Transparency logSecurity for yeelight-web-controller 0.5.0
Provenance
Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.
Full provenance
All 2 files in this release have been uploaded with provenance metadata.
Attestation for yeelight_web_controller-0.5.0.tar.gz
PyPI Publish Attestation
PyPI verified that this artifact, at this checksum, originated from the publisher listed below.
Signed by GitHub Actions, verified by PyPI on May 4, 2026.
Transparency logA note on package security
PyPI is a repository for distributing packages, not a service for auditing them. While third-party security researchers and our staff do their best to detect and remove malicious packages, PyPI cannot guarantee the quality, security or suitability of any given package.
To learn more about protecting yourself from malicious actors, visit our package security documentation.