Add a page for grabbing the lights right now (#24) #135

Merged
claude-bot merged 1 commit from feat/24-remote into main 2026-09-05 01:58:51 +00:00
Contributor

Closes #24. All four acceptance criteria met.

A remote at /remote: colour swatches, a custom picker, brightness, effect, speed and intensity — all pushing immediately — plus a one-tap Back to schedule. Laid out for a phone first, because the plausible use is standing in the garden looking at the tree, not sitting at a desk.

It drives the existing quick-push endpoint

Not a second push path. That's what makes it safe to hand to someone at a party: quick push saves the controller's state, applies the new one, and hands the night back at the next scheduler event, so nothing the page does can leave the lights somewhere the schedule won't recover from.

A second path would have had to reimplement that, and the version that forgot to would be indistinguishable from the working one until the evening it mattered.

Admin-gated, matching the endpoint — viewers are limited to bounded previews everywhere else, and this would be an unbounded one.

What changed on the backend

Quick push gained brightness_pct, effect_id, effect_speed, effect_intensity. They're meaningful only alongside a colour: a scheme carries its own effect and brightness, and letting the request override those would mean two sources of truth for one push.

All four default to the previous behaviour, so the toolbar's existing quick push produces a byte-identical payload. There are tests pinning that specifically, because solid_color_state is shared with the no-event ambient state and the scheduler's fallbacks — a "harmless" default change here would quietly alter both.

They're bounded at the API rather than clamped. A slider that sends 300 is a bug in the caller, and swallowing it hides that.

Interaction details that took a decision

  • Tapping a swatch sets and pushes in one action. Requiring a second tap on Apply is one tap too many for something you're doing while looking at the lights.
  • Sliders push on release, not on drag. Same reasoning applied to a control that would otherwise send sixty requests a second.

A real bug found while reading the push path

POST /wled/push — the "Push now" button — built its scheme id directly instead of going through the resolver #74 added. On an interpolated event the button sent the phase anchor while the scheduler sent the blended day.

That's the same drift #93 fixed once already, and #74 added a third asker of the question without my noticing. It now goes through resolve_scheme_for_date and has a test that fails with the old lookup restored:

assert 'interp_event_2026_mid' == 'interp_event_2026@2026-12-05'

Verification

21 new backend tests. Backend ruff/mypy clean, 780 tests passing. Frontend in node:22: tsc -b clean, eslint clean, vitest passing, npm run build succeeds.

🤖 Generated with Claude Code

Closes #24. All four acceptance criteria met. A remote at `/remote`: colour swatches, a custom picker, brightness, effect, speed and intensity — all pushing immediately — plus a one-tap **Back to schedule**. Laid out for a phone first, because the plausible use is standing in the garden looking at the tree, not sitting at a desk. ## It drives the existing quick-push endpoint Not a second push path. That's what makes it safe to hand to someone at a party: quick push saves the controller's state, applies the new one, and hands the night back at the next scheduler event, so **nothing the page does can leave the lights somewhere the schedule won't recover from**. A second path would have had to reimplement that, and the version that forgot to would be indistinguishable from the working one until the evening it mattered. Admin-gated, matching the endpoint — viewers are limited to bounded previews everywhere else, and this would be an unbounded one. ## What changed on the backend Quick push gained `brightness_pct`, `effect_id`, `effect_speed`, `effect_intensity`. They're meaningful only alongside a **colour**: a scheme carries its own effect and brightness, and letting the request override those would mean two sources of truth for one push. All four default to the previous behaviour, so the toolbar's existing quick push produces a **byte-identical payload**. There are tests pinning that specifically, because `solid_color_state` is shared with the no-event ambient state and the scheduler's fallbacks — a "harmless" default change here would quietly alter both. They're **bounded at the API rather than clamped**. A slider that sends 300 is a bug in the caller, and swallowing it hides that. ## Interaction details that took a decision - **Tapping a swatch sets and pushes in one action.** Requiring a second tap on Apply is one tap too many for something you're doing while looking at the lights. - **Sliders push on release, not on drag.** Same reasoning applied to a control that would otherwise send sixty requests a second. ## A real bug found while reading the push path `POST /wled/push` — the **"Push now"** button — built its scheme id directly instead of going through the resolver #74 added. On an interpolated event the button sent the **phase anchor** while the scheduler sent the **blended day**. That's the same drift #93 fixed once already, and #74 added a third asker of the question without my noticing. It now goes through `resolve_scheme_for_date` and has a test that fails with the old lookup restored: ``` assert 'interp_event_2026_mid' == 'interp_event_2026@2026-12-05' ``` ## Verification 21 new backend tests. Backend `ruff`/`mypy` clean, **780 tests passing**. Frontend in `node:22`: `tsc -b` clean, `eslint` clean, vitest passing, `npm run build` succeeds. 🤖 Generated with [Claude Code](https://claude.com/claude-code)
Add a page for grabbing the lights right now (#24)
All checks were successful
CI / Dockerfile lint (pull_request) Successful in 9s
CI / Pre-commit hooks (pull_request) Successful in 52s
CI / Alembic migration check (pull_request) Successful in 1m1s
CI / Python lint & type-check (pull_request) Successful in 1m7s
CI / Frontend lint, test & build (pull_request) Successful in 1m45s
CI / Python tests (pull_request) Successful in 4m49s
CI / Docker build, health smoke & E2E (pull_request) Successful in 2m0s
cb0a18d1dc
A remote at /remote: colour swatches, a custom picker, brightness,
effect, speed and intensity, all pushing immediately, plus a one-tap
Back to schedule. Laid out for a phone first, because the plausible use
is standing in the garden looking at the tree, not sitting at a desk.

It drives the existing quick-push endpoint rather than inventing a second
push path. That is what makes it safe to hand to someone at a party:
quick push saves the controller's state, applies the new one, and hands
the night back at the next scheduler event, so nothing the page does can
leave the lights somewhere the schedule will not recover from. A second
path would have had to reimplement that, and the version that forgot to
would be indistinguishable until the evening it mattered.

Quick push gained brightness_pct, effect_id, effect_speed and
effect_intensity. They are meaningful only alongside a colour: a scheme
carries its own effect and brightness, and letting the request override
those would mean two sources of truth for one push. All four default to
the previous behaviour, so the toolbar's existing quick push produces a
byte-identical payload -- there are tests pinning that, because a
"harmless" default change here would quietly alter the no-event ambient
state and the scheduler's fallbacks, which share the same builder.

They are bounded at the API rather than clamped. A slider that sends 300
is a bug in the caller and swallowing it hides that.

Tapping a swatch sets and pushes in one action. Requiring a second tap on
Apply is one tap too many for something you are doing while looking at
the lights; the sliders push on release rather than on every drag, which
is the same reasoning applied to a control that would otherwise send
sixty requests a second.

Also fixed here, found while reading the push path: POST /wled/push --
the "Push now" button -- built its scheme id directly instead of going
through the resolver #74 added. On an interpolated event the button sent
the phase anchor while the scheduler sent the blended day. That is the
same drift #93 fixed once already, and #74 added a third asker of the
question without my noticing. It now has a test that fails with the old
lookup restored.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
rbrooks force-pushed feat/24-remote from cb0a18d1dc
All checks were successful
CI / Dockerfile lint (pull_request) Successful in 9s
CI / Pre-commit hooks (pull_request) Successful in 52s
CI / Alembic migration check (pull_request) Successful in 1m1s
CI / Python lint & type-check (pull_request) Successful in 1m7s
CI / Frontend lint, test & build (pull_request) Successful in 1m45s
CI / Python tests (pull_request) Successful in 4m49s
CI / Docker build, health smoke & E2E (pull_request) Successful in 2m0s
to c9a29b027f
All checks were successful
CI / Dockerfile lint (pull_request) Successful in 7s
CI / Pre-commit hooks (pull_request) Successful in 58s
CI / Alembic migration check (pull_request) Successful in 59s
CI / Python lint & type-check (pull_request) Successful in 1m25s
CI / Frontend lint, test & build (pull_request) Successful in 1m56s
CI / Python tests (pull_request) Successful in 5m1s
CI / Docker build, health smoke & E2E (pull_request) Successful in 1m57s
2026-09-05 01:40:53 +00:00
Compare
claude-bot deleted branch feat/24-remote 2026-09-05 01:58:51 +00:00
Sign in to join this conversation.
No description provided.