Let someone try Iris without owning a controller (#63) #137

Merged
claude-bot merged 1 commit from feat/63-demo into main 2026-09-05 01:47:37 +00:00
Contributor

Closes #63 for the three checkboxes I could do properly. README screenshots and the optional public instance are split to #136 — see the last section.

DEMO_MODE=true swaps the HTTP controller for an in-process simulator, so the whole workflow runs on a laptop: the year generates, the review queue fills, schemes preview as an animated virtual strip (#14), and pushes and preset sync succeed instead of failing at the first request.

Seeding

An empty database also gets three segments and a handful of approved schemes spread across the year, so the calendar has colour in it rather than being a blank grid someone has to imagine the rest of.

It seeds only an empty install. Existing segments and approvals are never touched — this is a flag someone can set on a database that already has their work in it, and overwriting a real segment layout because a flag was on would be unforgivable for a feature whose whole purpose is a first look. It also refuses to top up a partial layout: adding "Path" to someone's two-segment setup would be stranger than doing nothing, and would mark their approved schemes stale for the privilege.

It deliberately leaves the rest as candidates. The review queue with things actually in it is the feature being demonstrated; pre-approving the year would hide it.

Subclassing was the right call, and proved it immediately

The simulator subclasses WLEDController rather than reimplementing the interface, so every consumer works unchanged and mypy checks the substitution is total.

A test comparing the two surfaces found get_info un-overridden — it fell through to an HTTP call against host demo, producing a DNS error that would have read like a network problem rather than a missing method. Nothing in the class called it (probe_status was overridden too), so the hole was invisible until something compared them.

It reports a real device's answers

The effect catalogue is the one captured verbatim from a 16.0.0 controller in #62, not invented names — the browser simulator maps effect IDs to animations, so a fabricated catalogue would make every demo preview show the wrong effect.

That capture moved from tests/fixtures/ to app/data/ so there's one copy, shipped, rather than a test fixture duplicated into the image. The compat suite now reads it from there.

Three of my event keys didn't exist

thanksgiving_us, christmas and new_years_eve are actually thanksgiving_week, christmas_season and nothing at all. A typo there is invisible — the seed approves nothing for that key and the calendar is a little greyer than intended. There's now a test asserting every showcase key is a real event.

Verified by booting it, not just unit tests

Ran the app in demo mode and drove the real endpoints:

STATUS      : configured=True reachable=True name='Iris Demo' fw=16.0.0 segs=3 effects=220
SEGMENTS    : [('Eaves', 0, 'themed'), ('Porch', 1, 'themed'), ('Path', 2, 'themed')]
SCHEMES     : 108 total, 16 approved
APPROVED EV : christmas_season, easter, halloween, hanukkah, pride_month,
              st_patricks_day, thanksgiving_week, valentines_day
PUSH NOW    : 200   QUICK PUSH : 200
SYNC PRESETS: 200 — 16 slots written and read back

Two things it is not

  • Nothing on your network lights up while it's on, and a startup warning says so.
  • It never fails, so it exercises none of the retry, fallback or unreachable-controller paths. It's a demo, not a test rig, and anyone using it to convince themselves those work would be misled. That's in the module docstring, the README and .env.example rather than left as a discovery.

What I split out (#136)

The README screenshots. I can generate them but I can't verify them — Playwright is already set up, so capturing PNGs is mechanical, but I can't look at the result and tell whether the layout held, whether the crop shows the right thing, or whether it looks like software someone would want to run. Committing images I can't see to a README whose job is first impressions is the one place where "it produced output" is furthest from "it works".

Demo mode makes that job easy for whoever does it, which is the useful half.

Verification

29 new tests. Backend ruff/mypy clean, 790 tests passing.

🤖 Generated with Claude Code

Closes #63 for the three checkboxes I could do properly. README screenshots and the optional public instance are split to **#136** — see the last section. `DEMO_MODE=true` swaps the HTTP controller for an in-process simulator, so the whole workflow runs on a laptop: the year generates, the review queue fills, schemes preview as an animated virtual strip (#14), and pushes and preset sync **succeed** instead of failing at the first request. ## Seeding An empty database also gets three segments and a handful of approved schemes spread across the year, so the calendar has colour in it rather than being a blank grid someone has to imagine the rest of. **It seeds only an empty install.** Existing segments and approvals are never touched — this is a flag someone can set on a database that already has their work in it, and overwriting a real segment layout because a flag was on would be unforgivable for a feature whose whole purpose is a first look. It also refuses to *top up* a partial layout: adding "Path" to someone's two-segment setup would be stranger than doing nothing, and would mark their approved schemes stale for the privilege. It deliberately leaves the rest as **candidates**. The review queue with things actually in it is the feature being demonstrated; pre-approving the year would hide it. ## Subclassing was the right call, and proved it immediately The simulator subclasses `WLEDController` rather than reimplementing the interface, so every consumer works unchanged and mypy checks the substitution is total. A test comparing the two surfaces found **`get_info` un-overridden** — it fell through to an HTTP call against host `demo`, producing a DNS error that would have read like a network problem rather than a missing method. Nothing in the class called it (`probe_status` was overridden too), so the hole was invisible until something compared them. ## It reports a real device's answers The effect catalogue is the one captured verbatim from a 16.0.0 controller in #62, not invented names — the browser simulator maps effect IDs to animations, so a fabricated catalogue would make **every demo preview show the wrong effect**. That capture moved from `tests/fixtures/` to `app/data/` so there's one copy, shipped, rather than a test fixture duplicated into the image. The compat suite now reads it from there. ## Three of my event keys didn't exist `thanksgiving_us`, `christmas` and `new_years_eve` are actually `thanksgiving_week`, `christmas_season` and *nothing at all*. A typo there is invisible — the seed approves nothing for that key and the calendar is a little greyer than intended. There's now a test asserting every showcase key is a real event. ## Verified by booting it, not just unit tests Ran the app in demo mode and drove the real endpoints: ``` STATUS : configured=True reachable=True name='Iris Demo' fw=16.0.0 segs=3 effects=220 SEGMENTS : [('Eaves', 0, 'themed'), ('Porch', 1, 'themed'), ('Path', 2, 'themed')] SCHEMES : 108 total, 16 approved APPROVED EV : christmas_season, easter, halloween, hanukkah, pride_month, st_patricks_day, thanksgiving_week, valentines_day PUSH NOW : 200 QUICK PUSH : 200 SYNC PRESETS: 200 — 16 slots written and read back ``` ## Two things it is not - **Nothing on your network lights up while it's on**, and a startup warning says so. - **It never fails**, so it exercises none of the retry, fallback or unreachable-controller paths. It's a demo, not a test rig, and anyone using it to convince themselves those work would be misled. That's in the module docstring, the README and `.env.example` rather than left as a discovery. ## What I split out (#136) The README screenshots. **I can generate them but I can't verify them** — Playwright is already set up, so capturing PNGs is mechanical, but I can't look at the result and tell whether the layout held, whether the crop shows the right thing, or whether it looks like software someone would want to run. Committing images I can't see to a README whose job is first impressions is the one place where "it produced output" is furthest from "it works". Demo mode makes that job easy for whoever does it, which is the useful half. ## Verification 29 new tests. Backend `ruff`/`mypy` clean, **790 tests passing**. 🤖 Generated with [Claude Code](https://claude.com/claude-code)
Let someone try Iris without owning a controller (#63)
All checks were successful
CI / Dockerfile lint (pull_request) Successful in 8s
CI / Alembic migration check (pull_request) Successful in 40s
CI / Pre-commit hooks (pull_request) Successful in 58s
CI / Python lint & type-check (pull_request) Successful in 1m23s
CI / Frontend lint, test & build (pull_request) Successful in 1m43s
CI / Python tests (pull_request) Successful in 4m55s
CI / Docker build, health smoke & E2E (pull_request) Successful in 2m18s
ebe764c173
DEMO_MODE=true swaps the HTTP controller for an in-process simulator, so
the whole workflow runs on a laptop: the year generates, the review queue
fills, schemes preview as an animated virtual strip (#14), and pushes and
preset sync succeed instead of failing at the first request.

An empty database also gets three segments and a handful of approved
schemes spread across the year, so the calendar has colour in it rather
than being a blank grid someone has to imagine the rest of. It seeds only
an empty install -- existing segments and approvals are never touched,
because this is a flag someone can set on a database that already has
their work in it, and overwriting a real segment layout because a flag
was on would be unforgivable for a feature whose whole purpose is a first
look. It deliberately leaves the rest as candidates: the review queue
with things actually in it is the feature being demonstrated, and
pre-approving the year would hide it.

The simulator subclasses the real controller rather than reimplementing
the interface. Every consumer then works unchanged and mypy checks the
substitution is total; a parallel implementation would drift the moment
the real one gained a method, and the drift would surface as demo mode
being subtly wrong rather than as a type error. That decision paid for
itself immediately: a test comparing the two surfaces found get_info
un-overridden, falling through to an HTTP call against host "demo" -- a
DNS error that would have read like a network problem rather than a
missing method.

It serves the effect catalogue captured from a real 16.0.0 device rather
than invented names, because the browser simulator maps effect IDs to
animations and a fabricated catalogue would make every demo preview show
the wrong effect. That capture moved from tests/fixtures/ to app/data/ so
there is one copy, shipped, rather than a test fixture duplicated into
the image; the compat suite now reads it from there.

Three of my eight showcase event keys did not exist -- thanksgiving_us,
christmas and new_years_eve are thanksgiving_week, christmas_season and
nothing at all. A typo there is invisible: the seed simply approves
nothing for that key and the calendar is a little greyer than intended.
There is now a test asserting every key is a real event.

Verified by booting the app in demo mode and driving the real endpoints:
status reachable as "Iris Demo" with 220 effects, three segments, 108
schemes with 16 approved across eight events, push and quick push
succeeding, and preset sync writing sixteen slots and verifying the
read-back.

Two things it is not. Nothing on your network lights up while it is on,
and a startup warning says so. And it never fails, so it exercises none
of the retry, fallback or unreachable-controller paths -- it is a demo,
not a test rig, and anyone using it to convince themselves those work
would be misled.

README screenshots and the optional public instance are split to #136. I
can generate a screenshot; I cannot look at it and tell whether it is a
good one, and the README's job is first impressions.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
claude-bot deleted branch feat/63-demo 2026-09-05 01:47:37 +00:00
Sign in to join this conversation.
No description provided.