492bdb95e3
Receive real-time events from games (CS2, Dota 2, LoL, etc.) and drive LED effects through the existing color strip and value source pipelines. Core: - GameEventBus (thread-safe pub/sub) with standardized 23-type event vocabulary - GameAdapter ABC + AdapterRegistry + MappingAdapter (YAML-driven) - Built-in adapters: CS2 GSI, Dota 2 GSI, LoL Live Client, Generic Webhook - Community YAML adapters: Minecraft, Valorant, Rocket League - GameEventColorStripStream with 5 effects (flash/pulse/sweep/color_shift/breathing) - GameEventValueSource with EMA smoothing and timeout - 4 built-in effect presets (FPS Combat, MOBA Health, Racing, Generic Alert) - Auto-setup for Valve GSI games (Steam path detection, cfg file writing) - Demo capture engine exposed to non-demo mode Frontend: - Game tab in Streams tree navigation with integration cards - Game integration editor modal with adapter picker, config fields, event mappings - game_event source type in CSS and ValueSource editors - Setup instructions overlay (markdown rendered) - Live event monitor and connection test API: - Full CRUD for game integrations - Event ingestion endpoint (adapter-level auth) - Adapter metadata, presets, auto-setup, status/diagnostics endpoints
5.1 KiB
5.1 KiB
Phase 4: GameEventColorStripStream
Status: ✅ Complete Parent plan: PLAN.md Domain: backend
Objective
Create a new ColorStripSource/Stream type that renders LED effects in response to game events. Follows the NotificationColorStripStream pattern — event-driven with a 30 FPS render loop and double-buffered output.
Tasks
- Task 1: Create
GameEventColorStripSourcedataclass instorage/color_strip_source.py- Fields: game_integration_id (str), idle_color (BindableColor), event_mappings (list of EventMapping dicts — override/supplement config-level mappings)
- source_type = "game_event"
- Implement to_dict/from_dict/create_from_kwargs/apply_update
- sharable = False (each target gets its own stream)
- Task 2: Register "game_event" in
_SOURCE_TYPE_MAPin color_strip_source.py - Task 3: Create
GameEventColorStripStream(core/processing/game_event_stream.py)- Constructor: parse event_mappings into lookup dict, subscribe to EventBus
_on_game_event(event)— callback from EventBus, enqueue effect (thread-safe via deque)- Effect types: flash, pulse, sweep, color_shift, breathing
- Priority-based layering — higher priority effects override lower (same as notification)
- 30 FPS background render thread with frame_time sleep
- Double-buffered output under threading.Lock
- Idle: output idle_color when no active effects
get_latest_colors()→ returns current rendered frame (np.ndarray)start()/stop()for lifecycle- Cleanup: unsubscribe from EventBus on stop
- Task 4: Register in
_SIMPLE_STREAM_MAPincolor_strip_stream_manager.py - Task 5: Wire EventBus injection — stream needs access to the singleton EventBus
- Added
game_event_busparameter to ColorStripStreamManager constructor - Injection via
set_event_bus()using same hasattr pattern as asset_store
- Added
- Task 6: Write tests for GameEventColorStripSource (serialization, factory, update)
- Task 7: Write tests for GameEventColorStripStream (event → effect rendering, priority, idle state, lifecycle)
Files to Modify/Create
server/src/wled_controller/storage/color_strip_source.py— add GameEventColorStripSource + registerserver/src/wled_controller/core/processing/game_event_stream.py— new stream classserver/src/wled_controller/core/processing/color_strip_stream_manager.py— register in _SIMPLE_STREAM_MAP, inject EventBusserver/src/wled_controller/core/processing/processor_manager.py— pass EventBus to stream manager (if needed)server/tests/core/test_game_event_css.py— source + stream tests
Acceptance Criteria
- "game_event" source type serializes/deserializes correctly
- Stream subscribes to EventBus and renders effects when events arrive
- Multiple simultaneous effects layer by priority
- Idle state outputs the configured idle_color
- Stream cleans up subscriptions on stop
- All tests pass
Notes
- Reuse effect rendering logic from NotificationColorStripStream where possible (flash, pulse, sweep are identical)
- Consider extracting shared effect rendering into a utility if duplication is significant
- The stream needs the EventBus singleton — simplest injection is via the stream manager's dependencies
- ⚠️ Big Bang: color_strip_source.py and stream_manager.py are shared files — coordinate with Phase 5 if running in parallel
Review Checklist
- All tasks completed
- Code follows project conventions
- No unintended side effects
- Tests pass (28/28)
Handoff to Next Phase
What was built
GameEventColorStripSourcedataclass instorage/color_strip_source.pywith full serialization, factory, and update supportGameEventColorStripStreamincore/processing/game_event_stream.py— event-driven stream with 5 effects (flash, pulse, sweep, color_shift, breathing), 30 FPS render loop, double-buffered output, priority-based effect layering- EventBus injection via
game_event_busparameter onColorStripStreamManagerconstructor +set_event_bus()method on the stream
Integration points for later phases
- Phase 7 (frontend): The
source_type = "game_event"needs a UI editor. Fields:game_integration_id(EntitySelect),idle_color(BindableColor picker),event_mappings(list of EventMapping dicts with effect/color/duration/intensity/priority),led_count - Phase 8 (wiring):
processor_manager.pyneeds to pass theGameEventBussingleton toColorStripStreamManagervia the newgame_event_bus=constructor parameter. The bus is created in Phase 1'sinit_dependencies().
Files modified
server/src/wled_controller/storage/color_strip_source.py— addedGameEventColorStripSourceclass + registered in_SOURCE_TYPE_MAPserver/src/wled_controller/core/processing/game_event_stream.py— new fileserver/src/wled_controller/core/processing/color_strip_stream_manager.py— added import,_SIMPLE_STREAM_MAPentry,game_event_busconstructor param, injection hookserver/tests/core/test_game_event_css.py— 28 tests covering source serialization, stream lifecycle, rendering, effects, auto-size, and hot-update