Files
ledgrab/plans/game-integration/phase-5-value-source.md
T
alexei.dolgolyov 492bdb95e3 feat: game integration system
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
2026-03-31 13:17:52 +03:00

70 lines
4.3 KiB
Markdown

# Phase 5: GameEventValueSource
**Status:** ✅ Complete
**Parent plan:** [PLAN.md](./PLAN.md)
**Domain:** backend
## Objective
Create a new ValueSource type that exposes game metrics (health, ammo, mana, etc.) as 0.0-1.0 scalar values. These can be bound to any existing effect parameter (brightness, speed, color position) via the BindableFloat/BindableColor system.
## Tasks
- [x] Task 1: Create `GameEventValueSource` dataclass in `storage/value_source.py`
- Fields: game_integration_id (str), event_type (str — from standard vocabulary), min_game_value (float, default 0.0), max_game_value (float, default 100.0), smoothing (float, 0.0-1.0, EMA alpha), default_value (float, 0.5), timeout (float, seconds before reverting to default)
- source_type = "game_event"
- Implement to_dict/from_dict
- [x] Task 2: Register "game_event" in `_VALUE_SOURCE_MAP` in value_source.py
- [x] Task 3: Create `GameEventValueStream` runtime resolver (`core/value_sources/game_event_value_source.py`)
- Subscribe to EventBus for the configured event_type
- Normalize incoming value using min/max mapping → 0.0-1.0
- Apply EMA smoothing if configured
- Track last_event_time for timeout detection
- `get_value()` → returns current normalized value (or default if timed out)
- `get_color()` → returns None (game event value source only provides scalars)
- Thread-safe (EventBus callback + get_value from render thread)
- Cleanup: unsubscribe from EventBus on release
- [x] Task 4: Register in value source manager / factory
- Add case for "game_event" source_type in the value stream creation logic
- Inject EventBus reference
- [x] Task 5: Write tests for GameEventValueSource (serialization, from_dict, defaults)
- [x] Task 6: Write tests for GameEventValueStream (normalization, smoothing, timeout, thread safety)
## Files to Modify/Create
- `server/src/wled_controller/storage/value_source.py` — add GameEventValueSource + register
- `server/src/wled_controller/core/value_sources/game_event_value_source.py` — runtime resolver
- `server/src/wled_controller/core/processing/value_stream.py` — register factory case + event_bus param
- `server/tests/core/test_game_event_value_source.py` — tests
## Acceptance Criteria
- "game_event" value source type serializes/deserializes correctly
- Runtime resolver normalizes game values to 0.0-1.0 using min/max
- EMA smoothing works correctly (smooth transitions, not jumpy)
- Timeout reverts to default_value when no events received
- Can be bound to BindableFloat properties on any ColorStripSource
- All tests pass
## Notes
- EMA formula: `smoothed = alpha * new_value + (1 - alpha) * smoothed` where alpha = 1 - smoothing
- Timeout uses monotonic clock — compare current time vs last_event_time
- Best suited for continuous event types (health, mana, ammo) — triggers (kill, death) are less useful here
- ⚠️ Big Bang: value_source.py is a shared file — coordinate with Phase 4 if running in parallel
## Review Checklist
- [x] All tasks completed
- [x] Code follows project conventions
- [x] No unintended side effects
- [x] Tests pass (24/24)
## Handoff to Next Phase
### What was implemented
- **GameEventValueSource** dataclass in `storage/value_source.py` with all required fields (game_integration_id, event_type, min/max mapping, smoothing, default_value, timeout)
- **GameEventValueStream** in new module `core/value_sources/game_event_value_source.py` — subscribes to GameEventBus, normalizes values, applies EMA smoothing, handles timeout with monotonic clock, thread-safe via threading.Lock
- **ValueStreamManager** updated with `event_bus` parameter and factory case for `GameEventValueSource` in `core/processing/value_stream.py`
- **24 tests** covering serialization, normalization (7 cases), smoothing (3 cases), timeout (4 cases), lifecycle (4 cases), thread safety, and hot-update
### Integration notes for downstream phases
- `ValueStreamManager.__init__` now accepts an optional `event_bus: GameEventBus` parameter — Phase 8 (wiring) needs to pass the EventBus instance when constructing ValueStreamManager in `dependencies.py`
- New module `core/value_sources/` created — contains `__init__.py` and `game_event_value_source.py`
- No changes to API schemas or routes — Phase 7 (frontend) will need to add "game_event" to the value source editor UI