feat: value source card crosslinks + gradient_map test shows input value
Lint & Test / test (push) Successful in 1m23s
Lint & Test / test (push) Successful in 1m23s
Add navigateToCard crosslinks for ha_entity (→ HA source), gradient_map (→ input value source + gradient entity), and css_extract (→ color strip). Gradient map test now charts the input interpolation factor instead of output luminance, making the 0–1 chart meaningful.
This commit is contained in:
@@ -397,6 +397,8 @@ async def test_value_source_ws(
|
||||
msg["color"] = [int(r), int(g), int(b)]
|
||||
except NotImplementedError:
|
||||
pass
|
||||
if hasattr(stream, "get_input_value"):
|
||||
msg["input_value"] = round(stream.get_input_value(), 4)
|
||||
if hasattr(stream, "get_raw_value"):
|
||||
raw = stream.get_raw_value()
|
||||
if raw is not None:
|
||||
|
||||
@@ -939,6 +939,7 @@ class GradientMapValueStream(ValueStream):
|
||||
self._gradient_store = gradient_store
|
||||
self._inner_stream: Optional[ValueStream] = None
|
||||
self._stops: list = []
|
||||
self._input_value: float = 0.0
|
||||
self._resolve_gradient()
|
||||
|
||||
def _resolve_gradient(self) -> None:
|
||||
@@ -979,11 +980,16 @@ class GradientMapValueStream(ValueStream):
|
||||
r, g, b = self.get_color()
|
||||
return (0.299 * r + 0.587 * g + 0.114 * b) / 255.0
|
||||
|
||||
def get_input_value(self) -> float:
|
||||
"""Return the last input interpolation factor (0.0–1.0)."""
|
||||
return self._input_value
|
||||
|
||||
def get_color(self) -> tuple:
|
||||
if self._inner_stream is None:
|
||||
return (128, 128, 128)
|
||||
|
||||
t = max(0.0, min(1.0, self._inner_stream.get_value()))
|
||||
self._input_value = t
|
||||
stops = self._stops
|
||||
if not stops:
|
||||
return (128, 128, 128)
|
||||
|
||||
@@ -813,13 +813,16 @@ export function testValueSource(sourceId: any) {
|
||||
_testVsWs.onmessage = (event) => {
|
||||
try {
|
||||
const data = JSON.parse(event.data);
|
||||
_testVsLatest = data.value;
|
||||
_testVsHistory.push(data.value);
|
||||
// For color sources with an input value (e.g. gradient_map),
|
||||
// chart the input interpolation factor instead of luminance
|
||||
const chartValue = data.input_value !== undefined ? data.input_value : data.value;
|
||||
_testVsLatest = chartValue;
|
||||
_testVsHistory.push(chartValue);
|
||||
if (_testVsHistory.length > VS_HISTORY_SIZE) {
|
||||
_testVsHistory.shift();
|
||||
}
|
||||
if (data.value < _testVsMinObserved) _testVsMinObserved = data.value;
|
||||
if (data.value > _testVsMaxObserved) _testVsMaxObserved = data.value;
|
||||
if (chartValue < _testVsMinObserved) _testVsMinObserved = chartValue;
|
||||
if (chartValue > _testVsMaxObserved) _testVsMaxObserved = chartValue;
|
||||
if (data.raw_value !== undefined) {
|
||||
_testVsRawLatest = data.raw_value;
|
||||
if (data.raw_range) _testVsRawRange = data.raw_range;
|
||||
@@ -1134,8 +1137,11 @@ export function createValueSourceCard(src: ValueSource) {
|
||||
const haName = haSrc ? haSrc.name : ((src as any).ha_source_id || '-');
|
||||
const entityId = (src as any).entity_id || '';
|
||||
const attr = (src as any).attribute;
|
||||
const haBadge = haSrc
|
||||
? `<span class="stream-card-prop stream-card-link" title="${escapeHtml(t('value_source.ha_source'))}" onclick="event.stopPropagation(); navigateToCard('streams','home_assistant','ha-sources','data-id','${(src as any).ha_source_id}')">${ICON_HOME} ${escapeHtml(haName)}</span>`
|
||||
: `<span class="stream-card-prop">${ICON_HOME} ${escapeHtml(haName)}</span>`;
|
||||
propsHtml = `
|
||||
<span class="stream-card-prop">${ICON_HOME} ${escapeHtml(haName)}</span>
|
||||
${haBadge}
|
||||
<span class="stream-card-prop">${ICON_LINK} ${escapeHtml(entityId)}${attr ? '.' + escapeHtml(attr) : ''}</span>
|
||||
<span class="stream-card-prop">${ICON_MOVE_VERTICAL} ${(src as any).min_ha_value ?? 0}\u2013${(src as any).max_ha_value ?? 100}</span>
|
||||
`;
|
||||
@@ -1149,9 +1155,15 @@ export function createValueSourceCard(src: ValueSource) {
|
||||
const gradientCss = stops.length >= 2
|
||||
? `linear-gradient(to right, ${stops.map((s: any) => `rgb(${s.color.join(',')}) ${(s.position * 100).toFixed(0)}%`).join(', ')})`
|
||||
: '#333';
|
||||
const inputBadge = inputVs
|
||||
? `<span class="stream-card-prop stream-card-link" title="${escapeHtml(t('value_source.gradient_map.input'))}" onclick="event.stopPropagation(); navigateToCard('streams','value','value-sources','data-id','${(src as any).value_source_id}')">${ICON_LINK} ${escapeHtml(inputName)}</span>`
|
||||
: `<span class="stream-card-prop">${ICON_LINK} ${escapeHtml(inputName)}</span>`;
|
||||
const gradBadge = grad
|
||||
? `<span class="stream-card-prop stream-card-link" title="${escapeHtml(t('value_source.gradient_map.gradient'))}" onclick="event.stopPropagation(); navigateToCard('streams','gradients','gradients','data-id','${(src as any).gradient_id}')">${ICON_RAINBOW} ${escapeHtml(gradName)}</span>`
|
||||
: `<span class="stream-card-prop">${ICON_RAINBOW} ${escapeHtml(gradName)}</span>`;
|
||||
propsHtml = `
|
||||
<span class="stream-card-prop">${ICON_LINK} ${escapeHtml(inputName)}</span>
|
||||
<span class="stream-card-prop">${ICON_RAINBOW} ${escapeHtml(gradName)}</span>
|
||||
${inputBadge}
|
||||
${gradBadge}
|
||||
<div style="height:8px;border-radius:4px;margin:4px 0;background:${gradientCss};"></div>
|
||||
`;
|
||||
} else if (src.source_type === 'css_extract') {
|
||||
@@ -1160,8 +1172,11 @@ export function createValueSourceCard(src: ValueSource) {
|
||||
const ledStart = (src as any).led_start ?? 0;
|
||||
const ledEnd = (src as any).led_end ?? -1;
|
||||
const rangeLabel = ledEnd < 0 ? `${ledStart}\u2013all` : `${ledStart}\u2013${ledEnd}`;
|
||||
const cssBadge = cssSrc
|
||||
? `<span class="stream-card-prop stream-card-link" title="${escapeHtml(t('value_source.css_extract.source'))}" onclick="event.stopPropagation(); navigateToCard('streams','color_strip','color-strips','data-css-id','${(src as any).color_strip_source_id}')">${ICON_DROPLETS} ${escapeHtml(cssName)}</span>`
|
||||
: `<span class="stream-card-prop">${ICON_DROPLETS} ${escapeHtml(cssName)}</span>`;
|
||||
propsHtml = `
|
||||
<span class="stream-card-prop">${ICON_DROPLETS} ${escapeHtml(cssName)}</span>
|
||||
${cssBadge}
|
||||
<span class="stream-card-prop">${ICON_MOVE_VERTICAL} LED ${rangeLabel}</span>
|
||||
`;
|
||||
}
|
||||
|
||||
@@ -1463,6 +1463,9 @@
|
||||
"value_source.type.gradient_map.desc": "Maps numeric value through a color gradient",
|
||||
"value_source.type.css_extract": "Strip Extract",
|
||||
"value_source.type.css_extract.desc": "Extracts color from a color strip source",
|
||||
"value_source.gradient_map.input": "Input Value Source",
|
||||
"value_source.gradient_map.gradient": "Gradient",
|
||||
"value_source.css_extract.source": "Color Strip Source",
|
||||
"value_source.ha_source": "HA Connection:",
|
||||
"value_source.ha_source.hint": "Home Assistant connection to read entities from",
|
||||
"value_source.entity_id": "Entity:",
|
||||
|
||||
Reference in New Issue
Block a user