Add brightness control to Key Colors targets with HAOS integration

Adds brightness (0.0-1.0) to KeyColorsSettings, applies scaling in the
KC processing pipeline after smoothing, exposes a slider on the WebUI
target card, and creates a HA number entity for KC target brightness.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
2026-02-19 02:26:46 +03:00
parent 10e426be13
commit 46be9922bd
8 changed files with 131 additions and 11 deletions

View File

@@ -58,6 +58,7 @@ import {
createKCTargetCard, testKCTarget, toggleKCTestAutoRefresh,
showKCEditor, closeKCEditorModal, forceCloseKCEditorModal, saveKCEditor,
deleteKCTarget, disconnectAllKCWebSockets,
updateKCBrightnessLabel, saveKCBrightness,
} from './features/kc-targets.js';
import {
createPatternTemplateCard,
@@ -210,6 +211,8 @@ Object.assign(window, {
saveKCEditor,
deleteKCTarget,
disconnectAllKCWebSockets,
updateKCBrightnessLabel,
saveKCBrightness,
// pattern-templates
createPatternTemplateCard,

View File

@@ -39,6 +39,8 @@ export function createKCTargetCard(target, sourceMap, patternTemplateMap) {
const kcSettings = target.key_colors_settings || {};
const isProcessing = state.processing || false;
const brightness = kcSettings.brightness ?? 1.0;
const brightnessInt = Math.round(brightness * 255);
const source = sourceMap[target.picture_source_id];
const sourceName = source ? source.name : (target.picture_source_id || 'No source');
@@ -74,11 +76,18 @@ export function createKCTargetCard(target, sourceMap, patternTemplateMap) {
<span class="stream-card-prop" title="${t('kc.pattern_template')}">📄 ${escapeHtml(patternName)}</span>
<span class="stream-card-prop">▭ ${rectCount} rect${rectCount !== 1 ? 's' : ''}</span>
</div>
<div class="brightness-control" data-kc-brightness-wrap="${target.id}">
<input type="range" class="brightness-slider" min="0" max="255"
value="${brightnessInt}" data-kc-brightness="${target.id}"
oninput="updateKCBrightnessLabel('${target.id}', this.value)"
onchange="saveKCBrightness('${target.id}', this.value)"
title="${Math.round(brightness * 100)}%">
</div>
<div id="kc-swatches-${target.id}" class="kc-color-swatches">
${swatchesHtml}
</div>
${isProcessing ? `
<div class="card-content">
<div id="kc-swatches-${target.id}" class="kc-color-swatches">
${swatchesHtml}
</div>
${isProcessing ? `
<div class="metrics-grid">
<div class="metric">
<div class="metric-label">${t('device.metrics.actual_fps')}</div>
@@ -127,8 +136,8 @@ export function createKCTargetCard(target, sourceMap, patternTemplateMap) {
</div>
</div>
` : ''}
` : ''}
</div>
` : ''}
<div class="card-actions">
${isProcessing ? `
<button class="btn btn-icon btn-danger" onclick="stopTargetProcessing('${target.id}')" title="${t('targets.button.stop')}">
@@ -521,6 +530,26 @@ export async function deleteKCTarget(targetId) {
}
}
// ===== KC BRIGHTNESS =====
export function updateKCBrightnessLabel(targetId, value) {
const slider = document.querySelector(`[data-kc-brightness="${targetId}"]`);
if (slider) slider.title = Math.round(parseInt(value) / 255 * 100) + '%';
}
export async function saveKCBrightness(targetId, value) {
const brightness = parseInt(value) / 255;
try {
await fetch(`${API_BASE}/picture-targets/${targetId}`, {
method: 'PUT',
headers: getHeaders(),
body: JSON.stringify({ key_colors_settings: { brightness } }),
});
} catch (err) {
console.error('Failed to save KC brightness:', err);
}
}
// ===== KEY COLORS WEBSOCKET =====
export function connectKCWebSocket(targetId) {