New value source types: - ha_entity: reads numeric values from HA entity state/attribute, normalizes via min/max range, applies EMA smoothing. EntitySelect for HA connection and entity selection with live entity list fetching. - gradient_map: maps a float value source (0-1) through a gradient entity. EntitySelect for both input source and gradient with inline previews. - css_extract: extracts single color by averaging LED range from a color strip source. EntitySelect for source selection. Value source type picker: - Filter tabs (All / Numeric / Color) above the icon grid - showTypePicker extended with filterTabs + onFilterChange support Palette selectors converted to EntitySelect: - Effect palette, gradient preset, and audio palette selectors now use command-palette style EntitySelect with gradient strip previews Tab indicator fixes: - Icon now updates on tab switch (was passing no args to updateTabIndicator) - Visible with any background effect active, not just Noise Field - Noise Field is the default background effect for new users Dashboard section collapse fix: - Split header into clickable toggle (chevron+label) and non-clickable actions area — buttons no longer trigger collapse/expand Discriminated union fix (422 errors): - source_type/target_type now always included in update payloads for: CSS editor, LED target, HA light target, simple calibration, advanced calibration
11 KiB
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.0max_ha_value: float— raw HA value corresponding to output 1.0smoothing: float— EMA smoothing factor (0..1)return_type: "float"— always float
Backend
-
Storage model —
HAEntityValueSourcesubclass instorage/value_source.py- Fields:
ha_source_id,entity_id,attribute,min_ha_value,max_ha_value,smoothing - Register in
_VALUE_SOURCE_MAPas"ha_entity" to_dict()/from_dict()/_parse_common_fields()
- Fields:
-
Store — add
"ha_entity"case inValueSourceStore.create_source()andupdate_source()- Validate:
ha_source_idmust be non-empty,entity_idmust be non-empty
- Validate:
-
API schemas —
HAEntityValueSourceCreate,HAEntityValueSourceResponseinapi/schemas/value_sources.py- Add to
ValueSourceCreate/ValueSourceResponsediscriminated unions - Fields:
ha_source_id,entity_id,attribute(optional),min_ha_value,max_ha_value,smoothing
- Add to
-
API routes — add
HAEntityValueSource→ response builder in_RESPONSE_MAP -
Stream —
HAEntityValueStreamincore/processing/value_stream.pystart(): acquire HA runtime viaha_manager.acquire(ha_source_id)get_value(): readha_manager.get_state(ha_source_id, entity_id)→ extract state or attribute → clamp/normalize to [0,1] via min/max range → apply EMA smoothingstop(): release HA runtimeupdate_source(): hot-update parameters- Add to
ValueStreamManager._create_stream()
Frontend
-
TypeScript type —
HAEntityValueSourceinterface intypes.tssource_type: 'ha_entity',return_type: 'float'- Fields:
ha_source_id,entity_id,attribute,min_ha_value,max_ha_value,smoothing - Add to
ValueSourceTypeunion andValueSourceunion
-
Icon — add
ha_entity: _svg(P.home)to_valueSourceTypeIconsinicons.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_entitysection tovalue-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_entityhandler invalue-sources.tsonValueSourceTypeChange(): 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 existingColorStopmodel from color strip sources)easing: str— interpolation mode: "linear", "step" (reuse existing easing modes)return_type: "color"— always color
Backend
-
Storage model —
GradientMapValueSourcesubclass instorage/value_source.py- Fields:
value_source_id,stops(list of dicts withposition+color),easing - Register in
_VALUE_SOURCE_MAPas"gradient_map"
- Fields:
-
Store — add
"gradient_map"case increate_source()/update_source()- Validate: at least 2 stops,
value_source_idnon-empty
- Validate: at least 2 stops,
-
API schemas —
GradientMapValueSourceCreate,GradientMapValueSourceResponse- Reuse
ColorStopschema from color_strip_sources schemas (or define minimal version) - Add to discriminated unions
- Reuse
-
API routes — add to
_RESPONSE_MAP -
Stream —
GradientMapValueStreaminvalue_stream.pystart(): acquire the referenced value stream viaValueStreamManager.acquire(value_source_id)get_value(): return BT.601 luminance of current colorget_color(): callinner_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 streamupdate_source(): hot-update stops/easing, re-acquire if value_source_id changed
Frontend
-
TypeScript type —
GradientMapValueSourceinterfacesource_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_mapsection- 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 —
CSSExtractValueSourcesubclass instorage/value_source.py- Fields:
color_strip_source_id,led_start,led_end - Register as
"css_extract"
- Fields:
-
Store — add
"css_extract"case increate_source()/update_source()- Validate:
color_strip_source_idnon-empty
- Validate:
-
API schemas —
CSSExtractValueSourceCreate,CSSExtractValueSourceResponse- Add to discriminated unions
-
API routes — add to
_RESPONSE_MAP -
Stream —
CSSExtractValueStreaminvalue_stream.pystart(): acquire color strip stream viaColorStripStreamManager.acquire(color_strip_source_id, led_count=needed)get_color(): read strip colors → average the specified LED range → return single RGB tupleget_value(): BT.601 luminance of extracted colorstop(): release color strip streamupdate_source(): hot-update range, re-acquire if source changed- Note: Needs access to
ColorStripStreamManager— may need to inject it intoValueStreamManageror pass via constructor
Frontend
-
TypeScript type —
CSSExtractValueSourceinterfacesource_type: 'css_extract',return_type: 'color'- Fields:
color_strip_source_id,led_start,led_end
-
Icon — add
css_extract: _svg(P.eyedropper)(orP.pipetteif available, elseP.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_extractsection- 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
groupssupport to IconSelect (items grouped by category with filter tabs) - Option B: Filter
VS_TYPE_KEYSbefore 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
- Option A: Add
-
i18n — add keys:
value_source.filter.all: "All"value_source.filter.float: "Float"value_source.filter.color: "Color"
Implementation Order
- Feature 4 (filter) — smallest, unblocks better UX for the growing type list
- Feature 1 (ha_entity) — standalone float type, no cross-dependencies
- Feature 3 (css_extract) — needs ColorStripStreamManager injection
- Feature 2 (gradient_map) — needs float VS reference + gradient UI
Cross-Cutting Concerns
- All new types need entries in
_VALUE_SOURCE_MAP(backend) andVS_TYPE_KEYS(frontend) - All new types need
_RESPONSE_MAPentries 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(andru.json,zh.json— can defer translations) ValueSourceStorereferential 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