No description
  • Python 69.5%
  • TypeScript 29.6%
  • Dockerfile 0.4%
  • JavaScript 0.3%
Find a file
Repository files (latest commit first)
Filename Latest commit message Latest commit date
renovate-bot b3cf02830f
All checks were successful
CI / Pre-commit hooks (push) Successful in 57s
CI / Python lint & type-check (push) Successful in 1m20s
CI / Dockerfile lint (push) Successful in 10s
CI / Alembic migration check (push) Successful in 42s
CI / Frontend lint, test & build (push) Successful in 2m30s
CI / Python tests (push) Successful in 8m20s
CI / Docker build, health smoke & E2E (push) Successful in 5m4s
Merge pull request 'chore(deps): update node.js to 5ec23b7' (#196) from renovate/node-22-slim into main
2026-11-09 00:44:36 +00:00
.forgejo/workflows Install the image's dependencies from uv.lock, so it can run its own jobs (#173) (#174) 2026-09-26 11:06:15 +00:00
backend Show what the lights used and cost this season (#153), and keep Settings edits until saved (#181) (#180) 2026-09-26 15:41:07 +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 Show what the lights used and cost this season (#153), and keep Settings edits until saved (#181) (#180) 2026-09-26 15:41:07 +00:00
frontend chore(deps): update dependency typescript-eslint to v8.71.2 2026-10-19 06:09:47 +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 Install Iris on a phone and get its alerts there (#154) (#178) 2026-09-26 11:28:27 +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 Show what the lights used and cost this season (#153), and keep Settings edits until saved (#181) (#180) 2026-09-26 15:41:07 +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 Pass the whole .env to the container, and refuse the default session secret (#147) (#160) 2026-09-26 05:32:31 +00:00
Dockerfile Merge pull request 'chore(deps): update node.js to 5ec23b7' (#196) from renovate/node-22-slim into main 2026-11-09 00:44:36 +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