No description
  • Python 72.5%
  • TypeScript 26.4%
  • Dockerfile 0.5%
  • JavaScript 0.3%
  • CSS 0.1%
  • Other 0.1%
Find a file
Repository files (latest commit first)
Filename Latest commit message Latest commit date
renovate-bot 7942577ede
All checks were successful
CI / Pre-commit hooks (push) Successful in 1m5s
CI / Dockerfile lint (push) Successful in 8s
CI / Alembic migration check (push) Successful in 46s
CI / Python lint & type-check (push) Successful in 1m30s
CI / Frontend lint, test & build (push) Successful in 2m15s
CI / Python tests (push) Successful in 5m18s
CI / Docker build, health smoke & E2E (push) Successful in 5m56s
Merge pull request 'chore(deps): update npm (non-major)' (#142) from renovate/npm-(non-major) into main
2026-09-07 00:25:48 +00:00
.forgejo/workflows Harden the container: non-root, read-only rootfs, pinned digests (#60) 2026-09-04 19:04:24 +00:00
backend Light the house for someone who has died (#80) (#138) 2026-09-05 03:57:18 +00:00
config Put meteor showers, full moons and aurora on the calendar (#20) (#127) 2026-09-04 23:55:57 +00:00
data M0: Project scaffolding 2026-05-15 13:30:04 -05:00
docs Say which deployment path the docs describe (#104, partial) (#139) 2026-09-05 03:45:40 +00:00
frontend Merge pull request 'chore(deps): update npm (non-major)' (#142) from renovate/npm-(non-major) into main 2026-09-07 00:25:48 +00:00
schemes Light the house for someone who has died (#80) (#138) 2026-09-05 03:57:18 +00:00
.dockerignore Harden the container: non-root, read-only rootfs, pinned digests (#60) 2026-09-04 19:04:24 +00:00
.editorconfig M0: Project scaffolding 2026-05-15 13:30:04 -05:00
.env.example Let someone try Iris without owning a controller (#63) (#137) 2026-09-05 01:47:36 +00:00
.gitattributes Harden the container: non-root, read-only rootfs, pinned digests (#60) 2026-09-04 19:04:24 +00:00
.gitignore Declare WLED firmware compatibility and pin it with fixtures (#62) 2026-09-04 18:33:51 +00:00
.pre-commit-config.yaml Run the pre-commit hooks in CI (#58) 2026-09-01 19:01:54 +00:00
CHANGELOG.md Finalise the CHANGELOG for v1.1.0 (#140) 2026-09-06 00:56:33 +00:00
CONTRIBUTING.md Run the pre-commit hooks in CI (#58) 2026-09-01 19:01:54 +00:00
docker-compose.registry.yml Say which deployment path the docs describe (#104, partial) (#139) 2026-09-05 03:45:40 +00:00
docker-compose.yml Let someone try Iris without owning a controller (#63) (#137) 2026-09-05 01:47:36 +00:00
Dockerfile Harden the container: non-root, read-only rootfs, pinned digests (#60) 2026-09-04 19:04:24 +00:00
LICENSE M0: Project scaffolding 2026-05-15 13:30:04 -05:00
README.md Let someone try Iris without owning a controller (#63) (#137) 2026-09-05 01:47:36 +00:00
renovate.json Harden the container: non-root, read-only rootfs, pinned digests (#60) 2026-09-04 19:04:24 +00:00
startup.sh Harden the container: non-root, read-only rootfs, pinned digests (#60) 2026-09-04 19:04:24 +00:00

Iris

A self-hosted WLED holiday lighting system. Maintains a year-round calendar of events, generates progressive lighting schemes, and pushes approved schemes to a WLED controller on a sunrise/sunset schedule.

Full specification: docs/iris-spec.md
Changelog: CHANGELOG.md · Planned work: Forgejo Issues


60-second quickstart

Prerequisites: Docker and Docker Compose.

# 1. Clone and configure
git clone https://git.rhoving.com/rbrooks/Iris-WLED.git
cd Iris-WLED
cp .env.example .env

# 2. Edit .env — at minimum set:
#    LATITUDE, LONGITUDE, TZ, and your OIDC credentials
#    (or set DISABLE_AUTH=true for local testing)
$EDITOR .env

# 3. Make the data directory writable by the container's non-root user
mkdir -p ./data && sudo chown -R 1000:1000 ./data

# 4. Start
docker compose up -d

# 5. Open http://localhost:7842

Step 3 is needed because the container runs as uid 1000 rather than root, and ./data is a host bind mount. If you would rather not chown, set IRIS_UID / IRIS_GID in .env to the directory's existing owner instead — see docs/setup.md.

The data directory (./data/) is created automatically on first run and mounted as a volume. The SQLite database lives at ./data/iris.db.

Trying it without a WLED controller

Set DEMO_MODE=true in .env and Iris runs against an in-process simulator instead of hardware. The whole workflow works — the year generates, the review queue fills, schemes preview as an animated virtual strip, pushes and preset sync succeed — so you can decide whether you want it before wiring anything up.

An empty database also gets a few segments and a handful of approved schemes, so the calendar has colour in it rather than being a blank grid. It seeds only an empty install: existing segments and approvals are never touched, so turning this on against a real database does nothing rather than something regrettable.

Two things to know. Nothing on your network lights up while it is on, and a warning at startup says so. And the simulator never fails, so it exercises none of the retry, fallback or unreachable-controller paths — it is a demo, not a test rig.

Changing the port

Set HOST_PORT in .env to use a different host port:

HOST_PORT=8042

The container always listens on 7842 internally; only the host-side port changes.


Configuration

All settings are documented in .env.example. WLED, location, schedule, AI provider, and backup values can be changed from the Settings page after first boot. Key bootstrap variables:

Variable Default Description
HOST_PORT 7842 Host port exposed by Docker
WLED_HOST (none) Startup default; editable in Settings
LATITUDE / LONGITUDE (required for solar mode) Startup default; editable in Settings
TZ UTC Startup default; editable in Settings
OIDC_ISSUER_URL (required) OIDC provider discovery URL
DISABLE_AUTH false Bypass auth for local testing

See .env.example for the full reference.


WLED firmware compatibility

Version Status
16.0.0 Verified on hardware
0.15.x Expected to work, untested
0.14.x Minimum supported
0.13.x and older Untested — Iris warns but still drives it

WLED's JSON API drifts between releases, so Iris pins its expectations with recorded responses from each version above and tests the translator against all of them. Note that WLED released 16.0.0 directly after 0.15.x, not 0.16.0 — so 16.x is the newest line, not an old one.

One consequence worth knowing: WLED effect ids are positional and get reassigned between releases (id 77 was "Meteor Smooth" on 0.14 and is "Copy Segment" on 16.0.0). Schemes store the id, so the effect picker reads the live list from your controller rather than a built-in table.

Full details, including what Iris reads and writes and how to add a version: docs/wled-compatibility.md.


Authentication

Iris uses standard OIDC. Any compliant provider works (Authentik, Keycloak, Google, etc.).
Set the OIDC_* variables in .env. The redirect URI must be registered in your provider:

https://<your-iris-host>/auth/callback

For local development with no IdP available, set DISABLE_AUTH=true. A warning banner will be displayed in the UI.


Development

See CONTRIBUTING.md for the full development setup.

Quick start:

# Backend
cd backend && uv sync --group dev
uv run uvicorn app.main:app --reload

# Frontend (separate terminal)
cd frontend && npm install && npm run dev

Status

v1.0.0 shipped on 2026-09-01 with the full feature set: events, schemes, WLED integration, scheduler, auth, review UI, AI suggestions, notifications, and import/export. Work now targets v1.1.0 — observability, previews, exports, remote control, and onboarding — tracked in Forgejo Issues under the v1.1.0 label; shipped work is recorded in the CHANGELOG.

Authoritative container images are published to the Forgejo registry at git.rhoving.com/rbrooks/iris-wled for linux/amd64.


License

MIT