Skip to content

feat: CLI for 0xB6 extended effects, segments, scribble, and timers#542

Draft
asxzy wants to merge 8 commits into
lightinglibs:masterfrom
asxzy:feat/add-0xb6-cli
Draft

feat: CLI for 0xB6 extended effects, segments, scribble, and timers#542
asxzy wants to merge 8 commits into
lightinglibs:masterfrom
asxzy:feat/add-0xb6-cli

Conversation

@asxzy

@asxzy asxzy commented Jun 30, 2026

Copy link
Copy Markdown
Contributor

Merge order: #538#539#540#541 → this. Draft until predecessors land.

Exposes everything in PR3/PR4 through fluxled.py, reusing the existing -C/--custom
scaffold.

CLI surface

  • Extended effects: -C <pattern> <speed> "<colors>" — pattern is a name (incl.
    multi-word, e.g. "rainbow bridge"); options --density 0-100, --direction l2r|r2l,
    --colorchange (= variant 1), --variant 0|1|2.
  • Segments: -C segments <speed> "<up to 20 colors>".
  • Scribble: -C scribble <effect> "<per-led-spec>"
    • <effect>: a name (static|flowing|twinkling|stars_wink|accumulate) or raw id 0-8.
    • per-LED spec tokens: <color>, <n>x<color>, w<level>, off, each with an optional
      blink suffix …/mode[/speed] (e.g. 40xred/fast/80); global tokens
      density= speed= dir= blink= blinkspeed= enter=.
    • The CLI reconciles the spec to the device's led_count (pads with off / truncates, with
      a warning).
  • Timers/clock: -t, -T, --getclock, --setclock; --listpresets shows the 0xB6
    effect names; help text updated for all of the above.

Notes for review

  • Color/white/effect parsing reuses utils.color_object_to_tuple; effect→id and
    blink-name→enum maps live next to the parser. The w<level> white token uses a
    digits-only regex so color names starting with w (e.g. white, wheat) still parse as
    colors. The per-token blink suffix uses / (unambiguous — colors/hex/r,g,b contain no
    /).
  • Numeric effect ids are passed through as int to async_set_scribble (which accepts
    ScribbleEffect | int), so ids 3/4/6/7 work without a wrapping ScribbleEffect(...).

Testing

  • ~694 lines of CLI tests: arg parsing for every option (extended pattern names + multi-word,
    segments, scribble effect names + numeric + range errors, per-LED blink grammar,
    enter=, --variant validation, run-length expansion, count reconcile pad/truncate).
  • pytest green, ruff clean, no new mypy errors.

On-device verification (device 192.168.1.78): the entire on-device test pass was
driven through this CLI (power, color/white, extended effects, segments, scribble, timers,
clock all issued via flux_led …). The CLI-completeness options specifically:

Check Command Result
Numeric effect id (unnamed) -C scribble 4 "20x255,0,0 20x0,255,0 20x0,0,255 20x255,255,0" ✅ animates (id 4, no UI name)
Per-LED blink (per-token) -C scribble static "40x255,0,0/fast/80 40x0,255,0" ✅ first half red-fast-blink, second half green-steady
enter=false -C scribble static "80x0,0,255 enter=false" ✅ renders blue, skips E1 23 init
--variant 2 -C "rainbow bridge" 60 "255,0,0 0,255,0 0,0,255" --variant 2 ✅ applies (option byte 2)

CLI coverage matrix: every device/API capability is reachable from the CLI; the only
remaining item is informational (led_count is not printed in -i).

@asxzy asxzy force-pushed the feat/add-0xb6-cli branch from b6b8b0f to 82ac90e Compare June 30, 2026 16:06
@codecov

codecov Bot commented Jun 30, 2026

Copy link
Copy Markdown

Codecov Report

❌ Patch coverage is 70.10309% with 261 lines in your changes missing coverage. Please review.

Files with missing lines Patch % Lines
flux_led/fluxled.py 41.80% 183 Missing and 30 partials ⚠️
flux_led/base_device.py 85.32% 8 Missing and 8 partials ⚠️
flux_led/timer.py 90.66% 6 Missing and 8 partials ⚠️
flux_led/aiodevice.py 69.56% 6 Missing and 1 partial ⚠️
flux_led/protocol.py 96.05% 6 Missing ⚠️
flux_led/device.py 66.66% 5 Missing ⚠️
Files with missing lines Coverage Δ
flux_led/const.py 100.00% <100.00%> (ø)
flux_led/models_db.py 98.90% <ø> (ø)
flux_led/pattern.py 100.00% <100.00%> (ø)
flux_led/device.py 74.75% <66.66%> (+2.26%) ⬆️
flux_led/protocol.py 98.64% <96.05%> (-0.38%) ⬇️
flux_led/aiodevice.py 95.02% <69.56%> (-1.26%) ⬇️
flux_led/timer.py 81.77% <90.66%> (+20.13%) ⬆️
flux_led/base_device.py 94.31% <85.32%> (-1.34%) ⬇️
flux_led/fluxled.py 32.66% <41.80%> (+32.66%) ⬆️

... and 2 files with indirect coverage changes

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.

@asxzy asxzy force-pushed the feat/add-0xb6-cli branch from 82ac90e to 13e487c Compare June 30, 2026 16:33
asxzy added 8 commits June 30, 2026 10:39
Introduces a dedicated protocol class for the Surplife 0xB6 device, switches the
model to it, and adds synchronous-path support.

- protocol.py: PROTOCOL_LEDENET_EXTENDED_CUSTOM, LEDENET_EXTENDED_STATE_RESPONSE_LEN,
  and ProtocolLEDENETExtendedCustom (name, 21-byte state length, extended-only
  is_valid_state_response, white-off-aware extended_state_to_state, and
  named_raw_state that converts extended -> standard for the sync path)
- device.py: read the full 21-byte extended state and accept it while probing
- base_device.py: register the protocol and add supports_extended_custom_effects
- models_db.py: point the 0xB6 model at PROTOCOL_LEDENET_EXTENDED_CUSTOM

Stacked on the reuse PR.
- const.py: ExtendedCustomEffectPattern / Direction / Option enums
- pattern.py: EXTENDED_CUSTOM_EFFECT_ID_NAME / NAME_ID maps
- protocol.py: construct_extended_custom_effect (0xE1 0x21),
  construct_custom_segment_colors (0xE1 0x22), construct_levels_change and the
  HSV(+W) byte helpers on ProtocolLEDENETExtendedCustom
- base_device.py: extended_custom_effect_pattern_list, _generate_* helpers and
  pattern-name lookup in _named_effect
- aiodevice.py / device.py: async_set_* and set* entry points

Stacked on the dedicated-protocol PR.
The 0xB6 device uses a distinct timer wire format (e0 05/06 wrapped commands,
variable-length 6-slot list, per-slot set):

- const.py: TIMER_ACTION_* and TIMER_EFFECT_* constants
- protocol.py: MSG_TIMERS_EXTENDED and the timer methods on
  ProtocolLEDENETExtendedCustom
- timer.py: LedTimerExtended dataclass (to_bytes/from_bytes, __str__)
- aiodevice.py: timer types widened, async_set_timers sends 0xB6 timers one at a
  time, new async_set_timer

Stacked on the effect-APIs PR.
- fluxled.py: processCustomArgs reworked to a dict supporting extended custom
  effect and segment modes; new --density / --direction / --colorchange options;
  updated --listpresets; -C and --settimer dispatch; processSetTimerArgsExtended

Stacked on the timers PR.
- Add processCustomArgs 'scribble' branch (before speed=int(args[1])) with
  _process_scribble_args helper and _parse_scribble_led_token
- Parse effect names (static/flowing/twinkling/stars_wink/accumulate)
- Expand run-length syntax: NxCOLOR, w<level>, off, bare color
- Parse optional key=value trailing tokens: speed, density, dir, blink, blinkspeed
- Bake global blink into each ScribbleLED at parse time
- Add dispatch block (mode=="scribble") calling bulb.async_set_scribble
- Update --custom help text and showUsageExamples/--listpresets with scribble example
- Add 10 CLI parsing tests covering run-length, white, off, malformed, kv args
…ffect id, per-LED blink, enter_mode, extended variant)

- Gap 1: accept numeric effect id 0-8 in -C scribble <effect>; named effects still work; out-of-range/garbage -> parser.error
- Gap 2: per-LED blink suffix syntax <value>[/blinkmode[/blinkspeed]]; per-token blink overrides global blink=/blinkspeed= defaults
- Gap 3: enter=true|false spec token controls async_set_scribble enter_mode; default true
- Gap 4: --variant 0|1|2 optparse option overrides --colorchange for ExtendedCustomEffect option byte; threaded through processCustomArgs and parseArgs call site
- Dispatch: pass raw int effect id (not ScribbleEffect()) so unnamed ids 3/4/6/7 don't raise ValueError; pass enter_mode from parsed dict
- 23 new TDD tests covering all four gaps (parser-level); all 287 tests pass
@asxzy asxzy force-pushed the feat/add-0xb6-cli branch from 13e487c to e52583a Compare June 30, 2026 16:46
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.

1 participant