diff --git a/CLAUDE.md b/CLAUDE.md
index 948809d..5a94b42 100644
--- a/CLAUDE.md
+++ b/CLAUDE.md
@@ -156,6 +156,7 @@ When creating or modifying entity cards (devices, targets, CSS sources, streams,
- Clone (📋) and Edit (✏️) icon buttons in `.template-card-actions`
- Delete (✕) button as `.card-remove-btn`
- Property badges in `.stream-card-props` with emoji icons
+- **Crosslinks**: When a card references another entity (audio source, picture source, capture template, PP template, etc.), make the property badge a clickable link using the `stream-card-link` CSS class and an `onclick` handler calling `navigateToCard(tab, subTab, sectionKey, cardAttr, cardValue)`. Only add the link when the referenced entity is found (to avoid broken navigation). Example: `🎵 Name`
### Modal footer buttons
diff --git a/server/src/wled_controller/core/processing/value_stream.py b/server/src/wled_controller/core/processing/value_stream.py
index 63d4086..998166e 100644
--- a/server/src/wled_controller/core/processing/value_stream.py
+++ b/server/src/wled_controller/core/processing/value_stream.py
@@ -449,7 +449,7 @@ class SceneValueStream(ValueStream):
self._max = max_value
self._live_stream_manager = live_stream_manager
self._live_stream = None
- self._prev_value = 0.5 # neutral start
+ self._prev_value = None # None = no frame seen yet; skip smoothing on first frame
def start(self) -> None:
if self._live_stream_manager and self._picture_source_id:
@@ -471,15 +471,15 @@ class SceneValueStream(ValueStream):
except Exception as e:
logger.warning(f"SceneValueStream failed to release live stream: {e}")
self._live_stream = None
- self._prev_value = 0.5
+ self._prev_value = None
def get_value(self) -> float:
if self._live_stream is None:
- return self._prev_value
+ return self._prev_value if self._prev_value is not None else 0.0
frame = self._live_stream.get_latest_frame()
if frame is None:
- return self._prev_value
+ return self._prev_value if self._prev_value is not None else 0.0
# Fast luminance: subsample to ~64x64 via numpy stride (zero-copy view)
img = frame.image
@@ -500,8 +500,11 @@ class SceneValueStream(ValueStream):
if self._behavior == "complement":
raw = 1.0 - raw
- # Temporal smoothing (EMA)
- smoothed = self._smoothing * self._prev_value + (1.0 - self._smoothing) * raw
+ # Temporal smoothing (EMA) — skip on first frame (no history to blend with)
+ if self._prev_value is None:
+ smoothed = raw
+ else:
+ smoothed = self._smoothing * self._prev_value + (1.0 - self._smoothing) * raw
self._prev_value = smoothed
# Map to output range
diff --git a/server/src/wled_controller/static/js/features/value-sources.js b/server/src/wled_controller/static/js/features/value-sources.js
index c3d209b..674d4ec 100644
--- a/server/src/wled_controller/static/js/features/value-sources.js
+++ b/server/src/wled_controller/static/js/features/value-sources.js
@@ -482,9 +482,13 @@ export function createValueSourceCard(src) {
} else if (src.source_type === 'audio') {
const audioSrc = _cachedAudioSources.find(a => a.id === src.audio_source_id);
const audioName = audioSrc ? audioSrc.name : (src.audio_source_id || '-');
+ const audioSection = audioSrc ? (audioSrc.source_type === 'mono' ? 'audio-mono' : 'audio-multi') : 'audio-multi';
const modeLabel = src.mode || 'rms';
+ const audioBadge = audioSrc
+ ? `🎵 ${escapeHtml(audioName)}`
+ : `🎵 ${escapeHtml(audioName)}`;
propsHtml = `
- 🎵 ${escapeHtml(audioName)}
+ ${audioBadge}
📈 ${modeLabel.toUpperCase()}
↕️ ${src.min_value ?? 0}–${src.max_value ?? 1}
`;
@@ -497,8 +501,16 @@ export function createValueSourceCard(src) {
} else if (src.source_type === 'adaptive_scene') {
const ps = _cachedStreams.find(s => s.id === src.picture_source_id);
const psName = ps ? ps.name : (src.picture_source_id || '-');
+ let psSubTab = 'raw', psSection = 'raw-streams';
+ if (ps) {
+ if (ps.stream_type === 'static_image') { psSubTab = 'static_image'; psSection = 'static-streams'; }
+ else if (ps.stream_type === 'processed') { psSubTab = 'processed'; psSection = 'proc-streams'; }
+ }
+ const psBadge = ps
+ ? `🖥️ ${escapeHtml(psName)}`
+ : `🖥️ ${escapeHtml(psName)}`;
propsHtml = `
- 🖥️ ${escapeHtml(psName)}
+ ${psBadge}
🔄 ${src.scene_behavior || 'complement'}
`;
}