Files
Claude af530cfe60 docs(03-01): complete protocol encoders plan — SUMMARY, STATE, ROADMAP updated
- 03-01-SUMMARY.md: WLED encoders, animation cmd, UDP sender, 27 passing tests
- STATE.md: advanced to plan 2/3, progress 80%, decisions recorded
- ROADMAP.md: phase 3 plan progress updated (1/3 complete)
- REQUIREMENTS.md: marked UDP-01/02/03, ANI-03/04 complete
- pyproject.toml: pytest added as dev dependency
- uv.lock: updated lockfile
2026-04-06 21:43:59 +00:00

7.5 KiB

phase, plan, subsystem, tags, requires, provides, affects, tech-stack, key-files, key-decisions, duration, completed
phase plan subsystem tags requires provides affects tech-stack key-files key-decisions duration completed
03-communication-protocol 01 protocol
udp
wled
protocol
animation
led
struct
asyncio
tdd
phase provides
02-audio-engine FastAPI app with lifespan pattern and app.state for shared resources
WLED-compatible DRGB/DRGBW/DNRGB UDP frame encoders
Custom 0xAC animation command packet encoder/decoder (16 bytes)
UDPSender async transport wrapper (single shared socket)
SK6812Device.encode_animation_cmd — real 16-byte packets, not empty stub
WS2801Device.encode_animation_cmd — real 16-byte packets, not empty stub
lightsync/protocol/ (new package)
lightsync/devices/sk6812.py
lightsync/devices/ws2801.py
added patterns
TDD RED-GREEN: failing tests committed first, implementation committed to pass them
Single shared asyncio DatagramTransport — one socket sends to N device addresses (no socket pool)
WLED DRGB/DRGBW: 2-byte header [type, timeout] + RGB/RGBW body per pixel
WLED DNRGB: 4-byte header [type, timeout, start_hi, start_lo] + RGB chunks per packet
struct.pack('>BBH') for big-endian DNRGB start index (critical per WLED spec)
struct.pack('>BBBBf') for 0xAC animation command header + float32 speed
_NullProtocol swallows OS UDP errors — fire-and-forget is correct for LED delivery
created modified
lightsync/protocol/__init__.py
lightsync/protocol/drgb.py
lightsync/protocol/animation_cmd.py
lightsync/protocol/udp_sender.py
tests/__init__.py
tests/test_protocol.py
lightsync/devices/sk6812.py
lightsync/devices/ws2801.py
TDD approach: tests committed as RED before implementation — forces verification of test coverage
0xAC animation command marker chosen well above WLED 0x01-0x04 range — no firmware collision
UDPSender uses asyncio DatagramTransport over asyncudp library — no dependency needed, stdlib suffices
Device stubs delegate to encode_animation_cmd via import alias _encode_cmd — clean, no code duplication
pytest added as dev dependency via uv add --dev pytest — was missing from pyproject.toml
~4min 2026-04-06

Phase 3 Plan 01: Protocol Encoders Summary

WLED-compatible UDP protocol layer implemented — DRGB/DRGBW/DNRGB frame encoders, custom 0xAC animation command packet with full roundtrip, async UDPSender transport, and SK6812/WS2801 device stubs replaced with real 16-byte packets — all tested with 27 passing tests

Performance

  • Duration: ~4 min
  • Started: 2026-04-06T21:38:29Z
  • Completed: 2026-04-06T21:42:37Z
  • Tasks: 2 (Task 1 TDD, Task 2 auto)
  • Tests: 27 passing

Accomplishments

Task 1: Protocol encoders — DRGB/DRGBW/DNRGB + animation command (TDD)

RED phase: 27 failing tests committed covering all encoder behaviors including byte layout, MTU truncation, DNRGB multi-packet splitting, big-endian start index, animation roundtrip, flags byte, and defaults.

GREEN phase: Implemented lightsync/protocol/drgb.py with:

  • encode_drgb: 2-byte header + 3-byte-per-pixel body, truncates at 490 px (MTU)
  • encode_drgbw: 2-byte header + 4-byte-per-pixel body, truncates at 367 px (MTU)
  • encode_dnrgb_packets: splits at 489 px/packet with big-endian start index via struct.pack(">BBH")

Implemented lightsync/protocol/animation_cmd.py with:

  • ANIMATION_IDS and ANIMATION_NAMES dicts mapping 7 animation names to IDs 0-6
  • encode_animation_cmd: 16-byte packet (0xAC marker, version, type ID, flags, speed float32, primary/secondary color, white, density)
  • decode_animation_cmd: full inverse — roundtrips correctly for all 7 types

All 27 tests pass.

Task 2: UDP sender + device stub completion

Created lightsync/protocol/udp_sender.py:

  • _NullProtocol swallows OS-level send errors (UDP delivery is best-effort)
  • UDPSender opens single shared DatagramTransport at local_addr=("0.0.0.0", 0)
  • send(payload, ip, port) is synchronous fire-and-forget
  • start()/stop() async lifecycle methods for FastAPI lifespan integration
  • is_running property reflects transport state

Updated lightsync/devices/sk6812.py and ws2801.py:

  • Added from lightsync.protocol.animation_cmd import encode_animation_cmd as _encode_cmd
  • Replaced return b"" stub with return _encode_cmd(animation, params)
  • SK6812: encode_animation_cmd("solid_color", {"color":[255,0,0], "white":128})ac0100003f000000ff00000000008000 (16 bytes, starts 0xAC)
  • WS2801: encode_animation_cmd("chase", {"color":[0,255,0], "speed":0.7})ac0101003f33333300ff000000000000 (16 bytes, starts 0xAC)

Task Commits

  1. Task 1 RED: Failing protocol testsffc4ef6 (test)
  2. Task 1 GREEN: WLED encoders + animation cmd5e13da3 (feat)
  3. Task 2: UDP sender + device stubsf17e77f (feat)

Files Created/Modified

  • lightsync/protocol/__init__.py — empty package init
  • lightsync/protocol/drgb.py — DRGB/DRGBW/DNRGB encoders with WLED constants
  • lightsync/protocol/animation_cmd.py — 0xAC animation command encoder/decoder
  • lightsync/protocol/udp_sender.py — async UDP transport wrapper
  • tests/__init__.py — test package init
  • tests/test_protocol.py — 27 tests for protocol correctness
  • lightsync/devices/sk6812.py — stub replaced with real encoding
  • lightsync/devices/ws2801.py — stub replaced with real encoding

Decisions Made

  • TDD approach applied: Tests written and committed as RED before implementation, ensuring coverage is meaningful not retrofitted
  • 0xAC marker: Well above WLED 0x01-0x04 range — firmware can dispatch on first byte without ambiguity
  • No asyncudp library: Python stdlib asyncio DatagramTransport is sufficient and avoids an extra dependency
  • pytest added as dev dep: Was missing from pyproject.toml, added via uv add --dev pytest

Deviations from Plan

Auto-fixed Issues

1. [Rule 3 - Blocking] pytest not installed

  • Found during: TDD RED phase (running tests)
  • Issue: uv run python -m pytest failed with "No module named pytest"
  • Fix: uv add --dev pytest — added to pyproject.toml [project.optional-dependencies] dev section
  • Files modified: pyproject.toml (by uv)
  • Commit: (included in uv lockfile update before ffc4ef6)

Verification Results

  • python -m pytest tests/test_protocol.py -v — 27 passed in 0.04s
  • from lightsync.protocol.drgb import encode_drgb, encode_drgbw, encode_dnrgb_packets — imports OK
  • from lightsync.protocol.udp_sender import UDPSender — imports OK
  • SK6812Device.encode_animation_cmd returns 16-byte 0xAC packet — PASS
  • WS2801Device.encode_animation_cmd returns 16-byte 0xAC packet — PASS

Known Stubs

None — all planned stubs replaced with real implementations. The encode_animation_cmd stubs in SK6812Device and WS2801Device that returned b"" are now wired to the real protocol encoder.

Self-Check

Files created/modified:

  • lightsync/protocol/init.py — FOUND
  • lightsync/protocol/drgb.py — FOUND
  • lightsync/protocol/animation_cmd.py — FOUND
  • lightsync/protocol/udp_sender.py — FOUND
  • tests/init.py — FOUND
  • tests/test_protocol.py — FOUND
  • lightsync/devices/sk6812.py — FOUND
  • lightsync/devices/ws2801.py — FOUND

Commits:

Self-Check: PASSED


Phase: 03-communication-protocol Completed: 2026-04-06