Skip to content

Color-correction (gamma) read-back - #230

Merged
fughilli merged 1 commit into
mainfrom
claude/gamma-color-correction-readback
Oct 4, 2026
Merged

fughilli merged 1 commit into
mainfrom
claude/gamma-color-correction-readback

Conversation

@fughilli

Copy link
Copy Markdown
Owner

Color-correction (gamma) read-back

Today the app can set color correction (set_color_correction, ack'd only via welcome) but there is no read-back, so the Color Correction page can't show the device's current gamma, and a gamma HITL journey can only assert the device ack'd — not that it applied the value it was set to.

This adds a get_color_correction request + color_correction_state reply, mirroring the existing get_hardware_config / hardware_config_state pattern.

Protocol (shared/protocol/proto/ledmapper.proto)

  • GetColorCorrection {} — client request (get_color_correction = 35).
  • ColorCorrectionState — server reply (color_correction_state = 20): resolved per-channel gamma_r/g/b + lum_r/g/b. The device always has a concrete profile (the WS2812B default after a reboot), so the state carries resolved values, never a profile name.
  • Regenerated the checked-in TS bindings (web/src/gen/ledmapper_pb.ts) via buf/protobuf-es; micropb (firmware) and Python bindings are generated at build time.

Firmware (firmware/player/src/lib.rs)

  • CMsg::GetColorCorrection → color_correction_state(), reporting whatever the last set_color_correction stored (the same (gamma, luminance) the LUTs are built from).
  • The handler lives in the shared session core, so both esp32c6 and esp32c3 get the arm for free — both build clean (-c opt). No C++/board change needed (the reply serializes through the generated ServerMessage encoder like hardware_config_state).
  • Updated the two exhaustive matches in firmware/player/tests/phone_client_frames.rs (reply_allowed + arm_name).

Web

  • client.getColorCorrection() (web/src/net/client.ts) mirrors getHardwareConfig.
  • The Color Correction page (web/src/ui/screens/colorCorrection.ts) now hydrates its curves from the device on open — adopting the device's reported gamma/luminance as the clean baseline (not a dirty edit, so nothing is pushed back and the save button stays idle), and falling back to the locally-remembered profile on older firmware without the read-back. Stale "no device read-back" comments updated.
  • Flat shapes added in web/src/net/proto.ts; driver harness case added in web/src/driver/harness.ts.

Tests

  • color_correction_roundtrip in firmware/player/tests/session.rs: set_color_correction → get_color_correction echoes the gamma + luminance (incl. a partial gamma-only override falling back to the WS2812B luminance), mirroring hardware_config_roundtrip.
  • getColorCorrection round-trips the device's applied gamma in web/tests/client.test.ts (wire round-trip; float32-exact values since the proto fields are float).

Journey hook (not implemented here)

The pi/hitl/phone/ journey files belong to the iOS agent / #227, and the gamma_config journey lands after #227. With this read-back in place, that future journey can:

await harness.setColorCorrection({ gamma: [2.0, 2.0, 2.0], commit: true });
const st = await harness.getColorCorrection();
assert st.gammaR == 2.0  // "device APPLIED gamma = X", not just that it ack'd

i.e. assert color_correction_state echoes the gamma it was set to, instead of only checking the welcome ack.

Mock device: mock_device.py support for the new get was not added — pi/hitl/phone/mock_device.py sits under the #227-owned directory, so it's left to that agent to avoid a collision. (The Pi pydantic server handler.py already returns unknown_type for unimplemented arms, same as it does for set_color_correction today.)

Verification

  • bazel test //firmware/player:session_test //firmware/player:phone_client_frames_test //web:web_ts_typecheck_test //web:unit_tests //web:client_test — green.
  • bazel build -c opt //firmware/player_app:esp32c6_netstack and :esp32c3_netstack — ok.

Notes for reviewers

🤖 Generated with Claude Code

Today the app can SET color correction (set_color_correction, ack'd only
via welcome) but there is no read-back, so the Color Correction page can't
show the device's current gamma and a gamma HITL journey can only assert the
device ack'd -- not that it applied the value it was set to.

Add a get_color_correction request + color_correction_state reply, mirroring
the get_hardware_config / hardware_config_state pattern:

- Proto: GetColorCorrection {} and ColorCorrectionState (resolved per-channel
  gamma_r/g/b + lum_r/g/b). The device always has a concrete profile (WS2812B
  after a reboot), so the state carries resolved values, never a profile name.
  Regenerated the checked-in TS bindings (web/src/gen); micropb + protobuf-es
  bindings are build-time.
- Firmware: CMsg::GetColorCorrection -> color_correction_state(), reporting
  whatever the last set_color_correction stored (the shared session core, so
  esp32c6 and esp32c3 both get the arm -- both build clean). Updated the two
  exhaustive matches in phone_client_frames.rs.
- Web: client.getColorCorrection() (mirrors getHardwareConfig) + the Color
  Correction page now hydrates its curves from the device on open (adopting
  the device's reported values as the clean baseline, not a dirty edit) and
  falls back to the locally-remembered profile on older firmware. Wired the
  driver harness too.
- Tests: set->get round-trip unit test in session.rs (echoes gamma +
  luminance, incl. a partial gamma-only override) mirroring
  hardware_config_roundtrip, and a getColorCorrection wire round-trip in
  web/tests/client.test.ts.

Journey hook (not implemented here -- the pi/hitl/phone journey files belong
to the iOS agent / #227, and gamma_config lands after #227): the future
gamma_config journey can now setColorCorrection({gamma}) then
getColorCorrection() and assert color_correction_state echoes the set gamma
("device applied gamma = X"), instead of only checking the welcome ack. Mock
device support for the new get was NOT added: mock_device.py lives under
pi/hitl/phone/ (owned by #227), so it is left to that agent to avoid a
collision.

Verified: bazel test //firmware/player:session_test
//firmware/player:phone_client_frames_test //web:web_ts_typecheck_test
//web:unit_tests //web:client_test (green); bazel build -c opt
//firmware/player_app:esp32c6_netstack and :esp32c3_netstack (ok).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
@github-actions

Copy link
Copy Markdown
Contributor
PR Preview Action v1.8.1

QR code for preview link

🚀 View preview at
https://fughilli.github.io/splanc/pr-preview/pr-230/

Built to branch gh-pages at 2026-09-30 17:40 UTC.
Preview will be ready when the GitHub Pages deployment is complete.

@fughilli
fughilli merged commit ef9fefd into main Oct 4, 2026
11 checks passed

This branch was successfully deployed

1 active deployment
HITL — 996d0f5d Deployed Sep 30, 2026 by fughilli via hitl_tests #698
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants