# New Value Source Types + Filter Support ## Feature 1: HA Value Source (`ha_entity`) A value source that reads numeric values from a Home Assistant entity's state or attribute. Allows binding any scalar property in the system to a live HA sensor/entity value. ### Configuration - `ha_source_id: str` — HA connection entity (EntitySelect picker) - `entity_id: str` — HA entity (EntitySelect picker, populated from `/api/v1/home-assistant/sources/{id}/entities`) - `attribute: str` — optional attribute name (text input or dropdown populated from entity attributes) - `min_ha_value: float` — raw HA value corresponding to output 0.0 - `max_ha_value: float` — raw HA value corresponding to output 1.0 - `smoothing: float` — EMA smoothing factor (0..1) - `return_type: "float"` — always float ### Backend - [ ] **Storage model** — `HAEntityValueSource` subclass in `storage/value_source.py` - Fields: `ha_source_id`, `entity_id`, `attribute`, `min_ha_value`, `max_ha_value`, `smoothing` - Register in `_VALUE_SOURCE_MAP` as `"ha_entity"` - `to_dict()` / `from_dict()` / `_parse_common_fields()` - [ ] **Store** — add `"ha_entity"` case in `ValueSourceStore.create_source()` and `update_source()` - Validate: `ha_source_id` must be non-empty, `entity_id` must be non-empty - [ ] **API schemas** — `HAEntityValueSourceCreate`, `HAEntityValueSourceResponse` in `api/schemas/value_sources.py` - Add to `ValueSourceCreate` / `ValueSourceResponse` discriminated unions - Fields: `ha_source_id`, `entity_id`, `attribute` (optional), `min_ha_value`, `max_ha_value`, `smoothing` - [ ] **API routes** — add `HAEntityValueSource` → response builder in `_RESPONSE_MAP` - [ ] **Stream** — `HAEntityValueStream` in `core/processing/value_stream.py` - `start()`: acquire HA runtime via `ha_manager.acquire(ha_source_id)` - `get_value()`: read `ha_manager.get_state(ha_source_id, entity_id)` → extract state or attribute → clamp/normalize to [0,1] via min/max range → apply EMA smoothing - `stop()`: release HA runtime - `update_source()`: hot-update parameters - Add to `ValueStreamManager._create_stream()` ### Frontend - [ ] **TypeScript type** — `HAEntityValueSource` interface in `types.ts` - `source_type: 'ha_entity'`, `return_type: 'float'` - Fields: `ha_source_id`, `entity_id`, `attribute`, `min_ha_value`, `max_ha_value`, `smoothing` - Add to `ValueSourceType` union and `ValueSource` union - [ ] **Icon** — add `ha_entity: _svg(P.home)` to `_valueSourceTypeIcons` in `icons.ts` - [ ] **i18n** — add keys in `en.json`: - `value_source.type.ha_entity`: "Home Assistant Entity" - `value_source.type.ha_entity.desc`: "Reads value from a Home Assistant sensor or entity attribute" - `value_source.ha_source`: "HA Connection:" - `value_source.entity_id`: "Entity:" - `value_source.attribute`: "Attribute (optional):" - `value_source.min_ha_value`: "Min HA Value:" - `value_source.max_ha_value`: "Max HA Value:" - [ ] **Editor modal** — add `ha_entity` section to `value-source-editor.html` - HA connection selector (EntitySelect from HA sources cache) - Entity selector (EntitySelect populated from HA entities endpoint) - Attribute text input (optional) - Min/Max HA value range inputs - Smoothing slider - [ ] **Editor logic** — add `ha_entity` handler in `value-sources.ts` - `onValueSourceTypeChange()`: show/hide ha_entity section - `_typeHandlers['ha_entity']`: load/reset/getPayload - EntitySelect for HA source + EntitySelect for entity (refreshes when HA source changes) - Auto-name: "{entity_friendly_name}" or "{entity_id}" - [ ] **Card renderer** — show HA source link + entity ID + attribute (if set) + range - [ ] **VS_TYPE_KEYS** — add `'ha_entity'` to the array --- ## Feature 2: Lerp Color Value Source (`gradient_map`) A color value source that maps a numeric value source's output through a color gradient. Given a float value source (0..1), interpolates the color at that position in a user-defined gradient. ### Configuration - `value_source_id: str` — reference to a float-returning value source (EntitySelect) - `stops: List[ColorStop]` — gradient color stops `[{position: float, color: [R,G,B]}]` (reuse existing `ColorStop` model from color strip sources) - `easing: str` — interpolation mode: "linear", "step" (reuse existing easing modes) - `return_type: "color"` — always color ### Backend - [ ] **Storage model** — `GradientMapValueSource` subclass in `storage/value_source.py` - Fields: `value_source_id`, `stops` (list of dicts with `position` + `color`), `easing` - Register in `_VALUE_SOURCE_MAP` as `"gradient_map"` - [ ] **Store** — add `"gradient_map"` case in `create_source()` / `update_source()` - Validate: at least 2 stops, `value_source_id` non-empty - [ ] **API schemas** — `GradientMapValueSourceCreate`, `GradientMapValueSourceResponse` - Reuse `ColorStop` schema from color_strip_sources schemas (or define minimal version) - Add to discriminated unions - [ ] **API routes** — add to `_RESPONSE_MAP` - [ ] **Stream** — `GradientMapValueStream` in `value_stream.py` - `start()`: acquire the referenced value stream via `ValueStreamManager.acquire(value_source_id)` - `get_value()`: return BT.601 luminance of current color - `get_color()`: call `inner_stream.get_value()` → interpolate through gradient stops → return RGB tuple - Reuse `_compute_gradient_colors()` logic from color_strip_stream.py (or a shared helper for single-point interpolation) - `stop()`: release inner value stream - `update_source()`: hot-update stops/easing, re-acquire if value_source_id changed ### Frontend - [ ] **TypeScript type** — `GradientMapValueSource` interface - `source_type: 'gradient_map'`, `return_type: 'color'` - Fields: `value_source_id`, `stops: ColorStop[]`, `easing` - [ ] **Icon** — add `gradient_map: _svg(P.rainbow)` to `_valueSourceTypeIcons` - [ ] **i18n** — add keys: - `value_source.type.gradient_map`: "Gradient Map" - `value_source.type.gradient_map.desc`: "Maps a numeric value through a color gradient" - `value_source.input_source`: "Input Value Source:" - `value_source.gradient_stops`: "Gradient:" - `value_source.easing`: "Interpolation:" - [ ] **Editor modal** — add `gradient_map` section - Value source selector (EntitySelect from float value sources) - Gradient stop editor (reuse gradient stop UI from CSS editor if possible, or build minimal version: list of position + color picker rows) - Easing selector (IconSelect: linear, step) - Live gradient preview bar (CSS linear-gradient from stops) - [ ] **Editor logic** — `_typeHandlers['gradient_map']`: load/reset/getPayload - [ ] **Card renderer** — CSS gradient preview bar + input source link + stop count - [ ] **VS_TYPE_KEYS** — add `'gradient_map'` --- ## Feature 3: CSS Extraction Color Value Source (`css_extract`) A color value source that extracts a single color from a color strip source by averaging a range of LEDs. Useful for deriving a single color signal from an existing color strip. ### Configuration - `color_strip_source_id: str` — reference to a color strip source (EntitySelect) - `led_start: int` — start of LED range (0-based, optional, default 0) - `led_end: int` — end of LED range (exclusive, optional, default -1 = whole strip) - `return_type: "color"` — always color ### Backend - [ ] **Storage model** — `CSSExtractValueSource` subclass in `storage/value_source.py` - Fields: `color_strip_source_id`, `led_start`, `led_end` - Register as `"css_extract"` - [ ] **Store** — add `"css_extract"` case in `create_source()` / `update_source()` - Validate: `color_strip_source_id` non-empty - [ ] **API schemas** — `CSSExtractValueSourceCreate`, `CSSExtractValueSourceResponse` - Add to discriminated unions - [ ] **API routes** — add to `_RESPONSE_MAP` - [ ] **Stream** — `CSSExtractValueStream` in `value_stream.py` - `start()`: acquire color strip stream via `ColorStripStreamManager.acquire(color_strip_source_id, led_count=needed)` - `get_color()`: read strip colors → average the specified LED range → return single RGB tuple - `get_value()`: BT.601 luminance of extracted color - `stop()`: release color strip stream - `update_source()`: hot-update range, re-acquire if source changed - **Note**: Needs access to `ColorStripStreamManager` — may need to inject it into `ValueStreamManager` or pass via constructor ### Frontend - [ ] **TypeScript type** — `CSSExtractValueSource` interface - `source_type: 'css_extract'`, `return_type: 'color'` - Fields: `color_strip_source_id`, `led_start`, `led_end` - [ ] **Icon** — add `css_extract: _svg(P.eyedropper)` (or `P.pipette` if available, else `P.palette`) - [ ] **i18n** — add keys: - `value_source.type.css_extract`: "Strip Color Extract" - `value_source.type.css_extract.desc`: "Extracts a single color from a color strip source" - `value_source.color_strip_source`: "Color Strip Source:" - `value_source.led_start`: "LED Start:" - `value_source.led_end`: "LED End (-1 = all):" - [ ] **Editor modal** — add `css_extract` section - Color strip source selector (EntitySelect from color strip sources cache) - LED start/end numeric inputs - Optional: live color preview swatch - [ ] **Editor logic** — `_typeHandlers['css_extract']`: load/reset/getPayload - [ ] **Card renderer** — color strip source link + LED range badge - [ ] **VS_TYPE_KEYS** — add `'css_extract'` --- ## Feature 4: Value Source Type Filter in Icon Grid Add a filter/category system to the value source type IconSelect so users can filter by return type or category. ### Implementation - [ ] **Add filter tabs** above the value source type icon grid in the editor modal - "All" (default) — show all types - "Float" — show float-returning types: static, animated, audio, adaptive_time, adaptive_scene, daylight, ha_entity - "Color" — show color-returning types: static_color, animated_color, adaptive_time_color, gradient_map, css_extract - [ ] **IconSelect enhancement** — either: - Option A: Add `groups` support to IconSelect (items grouped by category with filter tabs) - Option B: Filter `VS_TYPE_KEYS` before building items, with toggle buttons above the grid - Decision: Option B is simpler and follows existing patterns — add filter buttons that rebuild the icon grid - [ ] **i18n** — add keys: - `value_source.filter.all`: "All" - `value_source.filter.float`: "Float" - `value_source.filter.color`: "Color" --- ## Implementation Order 1. **Feature 4** (filter) — smallest, unblocks better UX for the growing type list 2. **Feature 1** (ha_entity) — standalone float type, no cross-dependencies 3. **Feature 3** (css_extract) — needs ColorStripStreamManager injection 4. **Feature 2** (gradient_map) — needs float VS reference + gradient UI ## Cross-Cutting Concerns - All new types need entries in `_VALUE_SOURCE_MAP` (backend) and `VS_TYPE_KEYS` (frontend) - All new types need `_RESPONSE_MAP` entries in routes - All new types need `ValueStreamManager._create_stream()` factory case - All new types need icon in `_valueSourceTypeIcons` - All new types need i18n keys in `en.json` (and `ru.json`, `zh.json` — can defer translations) - `ValueSourceStore` referential integrity check on delete should verify new references (ha_entity → ha_source, gradient_map → value_source, css_extract → color_strip_source) - Graph editor: new edge types for ha_entity → HA source node, gradient_map → value source node, css_extract → color strip node