diff --git a/server/src/wled_controller/api/routes/value_sources.py b/server/src/wled_controller/api/routes/value_sources.py index 7553238..874eda3 100644 --- a/server/src/wled_controller/api/routes/value_sources.py +++ b/server/src/wled_controller/api/routes/value_sources.py @@ -43,7 +43,6 @@ def _to_response(source: ValueSource) -> ValueSourceResponse: mode=d.get("mode"), sensitivity=d.get("sensitivity"), smoothing=d.get("smoothing"), - adaptive_mode=d.get("adaptive_mode"), schedule=d.get("schedule"), picture_source_id=d.get("picture_source_id"), scene_behavior=d.get("scene_behavior"), @@ -56,7 +55,7 @@ def _to_response(source: ValueSource) -> ValueSourceResponse: @router.get("/api/v1/value-sources", response_model=ValueSourceListResponse, tags=["Value Sources"]) async def list_value_sources( _auth: AuthRequired, - source_type: Optional[str] = Query(None, description="Filter by source_type: static, animated, or audio"), + source_type: Optional[str] = Query(None, description="Filter by source_type: static, animated, audio, adaptive_time, or adaptive_scene"), store: ValueSourceStore = Depends(get_value_source_store), ): """List all value sources, optionally filtered by type.""" @@ -90,7 +89,6 @@ async def create_value_source( sensitivity=data.sensitivity, smoothing=data.smoothing, description=data.description, - adaptive_mode=data.adaptive_mode, schedule=data.schedule, picture_source_id=data.picture_source_id, scene_behavior=data.scene_behavior, @@ -137,7 +135,6 @@ async def update_value_source( sensitivity=data.sensitivity, smoothing=data.smoothing, description=data.description, - adaptive_mode=data.adaptive_mode, schedule=data.schedule, picture_source_id=data.picture_source_id, scene_behavior=data.scene_behavior, diff --git a/server/src/wled_controller/api/schemas/value_sources.py b/server/src/wled_controller/api/schemas/value_sources.py index c07ebc9..389f223 100644 --- a/server/src/wled_controller/api/schemas/value_sources.py +++ b/server/src/wled_controller/api/schemas/value_sources.py @@ -10,7 +10,7 @@ class ValueSourceCreate(BaseModel): """Request to create a value source.""" name: str = Field(description="Source name", min_length=1, max_length=100) - source_type: Literal["static", "animated", "audio", "adaptive"] = Field(description="Source type") + source_type: Literal["static", "animated", "audio", "adaptive_time", "adaptive_scene"] = Field(description="Source type") # static fields value: Optional[float] = Field(None, description="Constant value (0.0-1.0)", ge=0.0, le=1.0) # animated fields @@ -24,7 +24,6 @@ class ValueSourceCreate(BaseModel): sensitivity: Optional[float] = Field(None, description="Gain multiplier (0.1-5.0)", ge=0.1, le=5.0) smoothing: Optional[float] = Field(None, description="Temporal smoothing (0.0-1.0)", ge=0.0, le=1.0) # adaptive fields - adaptive_mode: Optional[str] = Field(None, description="Adaptive mode: time_of_day|scene") schedule: Optional[list] = Field(None, description="Time-of-day schedule: [{time: 'HH:MM', value: 0.0-1.0}]") picture_source_id: Optional[str] = Field(None, description="Picture source ID for scene mode") scene_behavior: Optional[str] = Field(None, description="Scene behavior: complement|match") @@ -48,7 +47,6 @@ class ValueSourceUpdate(BaseModel): sensitivity: Optional[float] = Field(None, description="Gain multiplier (0.1-5.0)", ge=0.1, le=5.0) smoothing: Optional[float] = Field(None, description="Temporal smoothing (0.0-1.0)", ge=0.0, le=1.0) # adaptive fields - adaptive_mode: Optional[str] = Field(None, description="Adaptive mode: time_of_day|scene") schedule: Optional[list] = Field(None, description="Time-of-day schedule") picture_source_id: Optional[str] = Field(None, description="Picture source ID for scene mode") scene_behavior: Optional[str] = Field(None, description="Scene behavior: complement|match") @@ -60,7 +58,7 @@ class ValueSourceResponse(BaseModel): id: str = Field(description="Source ID") name: str = Field(description="Source name") - source_type: str = Field(description="Source type: static, animated, audio, or adaptive") + source_type: str = Field(description="Source type: static, animated, audio, adaptive_time, or adaptive_scene") value: Optional[float] = Field(None, description="Static value") waveform: Optional[str] = Field(None, description="Waveform type") speed: Optional[float] = Field(None, description="Cycles per minute") @@ -70,7 +68,6 @@ class ValueSourceResponse(BaseModel): mode: Optional[str] = Field(None, description="Audio mode") sensitivity: Optional[float] = Field(None, description="Gain multiplier") smoothing: Optional[float] = Field(None, description="Temporal smoothing") - adaptive_mode: Optional[str] = Field(None, description="Adaptive mode") schedule: Optional[list] = Field(None, description="Time-of-day schedule") picture_source_id: Optional[str] = Field(None, description="Picture source ID") scene_behavior: Optional[str] = Field(None, description="Scene behavior") diff --git a/server/src/wled_controller/core/processing/value_stream.py b/server/src/wled_controller/core/processing/value_stream.py index 051e5d7..2a4f0f4 100644 --- a/server/src/wled_controller/core/processing/value_stream.py +++ b/server/src/wled_controller/core/processing/value_stream.py @@ -7,8 +7,8 @@ on demand via ``get_value()``. Five concrete types: AnimatedValueStream — evaluates a periodic waveform (sine/triangle/square/sawtooth) AudioValueStream — polls audio analysis for RMS/peak/beat, applies sensitivity and temporal smoothing - TimeOfDayValueStream — interpolates brightness along a 24h schedule - SceneValueStream — derives brightness from a picture source's frame luminance + TimeOfDayValueStream — interpolates brightness along a 24h schedule (adaptive_time) + SceneValueStream — derives brightness from a picture source's frame luminance (adaptive_scene) ValueStreams are cheap (trivial math or single poll), so they compute inline in the caller's processing loop — no background threads required. @@ -362,7 +362,7 @@ class TimeOfDayValueStream(ValueStream): def update_source(self, source: "ValueSource") -> None: from wled_controller.storage.value_source import AdaptiveValueSource - if isinstance(source, AdaptiveValueSource) and source.adaptive_mode == "time_of_day": + if isinstance(source, AdaptiveValueSource) and source.source_type == "adaptive_time": self._parse_schedule(source.schedule) self._min = source.min_value self._max = source.max_value @@ -463,7 +463,7 @@ class SceneValueStream(ValueStream): def update_source(self, source: "ValueSource") -> None: from wled_controller.storage.value_source import AdaptiveValueSource - if not isinstance(source, AdaptiveValueSource) or source.adaptive_mode != "scene": + if not isinstance(source, AdaptiveValueSource) or source.source_type != "adaptive_scene": return self._behavior = source.scene_behavior @@ -603,7 +603,7 @@ class ValueStreamManager: ) if isinstance(source, AdaptiveValueSource): - if source.adaptive_mode == "scene": + if source.source_type == "adaptive_scene": return SceneValueStream( picture_source_id=source.picture_source_id, scene_behavior=source.scene_behavior, @@ -613,7 +613,6 @@ class ValueStreamManager: max_value=source.max_value, live_stream_manager=self._live_stream_manager, ) - # Default: time_of_day return TimeOfDayValueStream( schedule=source.schedule, min_value=source.min_value, diff --git a/server/src/wled_controller/static/js/app.js b/server/src/wled_controller/static/js/app.js index 920f4c8..23f5b18 100644 --- a/server/src/wled_controller/static/js/app.js +++ b/server/src/wled_controller/static/js/app.js @@ -112,7 +112,7 @@ import { import { showValueSourceModal, closeValueSourceModal, saveValueSource, editValueSource, deleteValueSource, onValueSourceTypeChange, - onAdaptiveModeChange, addSchedulePoint, + addSchedulePoint, } from './features/value-sources.js'; // Layer 5: calibration @@ -331,7 +331,6 @@ Object.assign(window, { editValueSource, deleteValueSource, onValueSourceTypeChange, - onAdaptiveModeChange, addSchedulePoint, // calibration 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 cecbe35..ed70838 100644 --- a/server/src/wled_controller/static/js/features/value-sources.js +++ b/server/src/wled_controller/static/js/features/value-sources.js @@ -1,9 +1,10 @@ /** - * Value Sources — CRUD for scalar value sources (static, animated, audio, adaptive). + * Value Sources — CRUD for scalar value sources (static, animated, audio, adaptive_time, adaptive_scene). * * Value sources produce a float 0.0-1.0 used for dynamic brightness control - * on LED targets. Four subtypes: static (constant), animated (waveform), - * audio (audio-reactive), adaptive (time-of-day schedule or scene brightness). + * on LED targets. Five subtypes: static (constant), animated (waveform), + * audio (audio-reactive), adaptive_time (time-of-day schedule), + * adaptive_scene (scene brightness analysis). * * Card rendering is handled by streams.js (Value tab). * This module manages the editor modal and API operations. @@ -49,10 +50,11 @@ export async function showValueSourceModal(editData) { document.getElementById('value-source-mode').value = editData.mode || 'rms'; _setSlider('value-source-sensitivity', editData.sensitivity ?? 1.0); _setSlider('value-source-smoothing', editData.smoothing ?? 0.3); - } else if (editData.source_type === 'adaptive') { - document.getElementById('value-source-adaptive-mode').value = editData.adaptive_mode || 'time_of_day'; - onAdaptiveModeChange(); + } else if (editData.source_type === 'adaptive_time') { _populateScheduleUI(editData.schedule); + _setSlider('value-source-adaptive-min-value', editData.min_value ?? 0); + _setSlider('value-source-adaptive-max-value', editData.max_value ?? 1); + } else if (editData.source_type === 'adaptive_scene') { _populatePictureSourceDropdown(editData.picture_source_id || ''); document.getElementById('value-source-scene-behavior').value = editData.scene_behavior || 'complement'; _setSlider('value-source-scene-sensitivity', editData.sensitivity ?? 1.0); @@ -75,7 +77,6 @@ export async function showValueSourceModal(editData) { _setSlider('value-source-sensitivity', 1.0); _setSlider('value-source-smoothing', 0.3); // Adaptive defaults - document.getElementById('value-source-adaptive-mode').value = 'time_of_day'; _populateScheduleUI([]); _populatePictureSourceDropdown(''); document.getElementById('value-source-scene-behavior').value = 'complement'; @@ -97,7 +98,10 @@ export function onValueSourceTypeChange() { document.getElementById('value-source-static-section').style.display = type === 'static' ? '' : 'none'; document.getElementById('value-source-animated-section').style.display = type === 'animated' ? '' : 'none'; document.getElementById('value-source-audio-section').style.display = type === 'audio' ? '' : 'none'; - document.getElementById('value-source-adaptive-section').style.display = type === 'adaptive' ? '' : 'none'; + document.getElementById('value-source-adaptive-time-section').style.display = type === 'adaptive_time' ? '' : 'none'; + document.getElementById('value-source-adaptive-scene-section').style.display = type === 'adaptive_scene' ? '' : 'none'; + document.getElementById('value-source-adaptive-range-section').style.display = + (type === 'adaptive_time' || type === 'adaptive_scene') ? '' : 'none'; // Populate audio dropdown when switching to audio type if (type === 'audio') { @@ -107,19 +111,12 @@ export function onValueSourceTypeChange() { } } - // Initialize adaptive sub-sections - if (type === 'adaptive') { - onAdaptiveModeChange(); + // Populate picture source dropdown when switching to scene type + if (type === 'adaptive_scene') { _populatePictureSourceDropdown(''); } } -export function onAdaptiveModeChange() { - const mode = document.getElementById('value-source-adaptive-mode').value; - document.getElementById('value-source-tod-section').style.display = mode === 'time_of_day' ? '' : 'none'; - document.getElementById('value-source-scene-section').style.display = mode === 'scene' ? '' : 'none'; -} - // ── Save ────────────────────────────────────────────────────── export async function saveValueSource() { @@ -149,23 +146,22 @@ export async function saveValueSource() { payload.mode = document.getElementById('value-source-mode').value; payload.sensitivity = parseFloat(document.getElementById('value-source-sensitivity').value); payload.smoothing = parseFloat(document.getElementById('value-source-smoothing').value); - } else if (sourceType === 'adaptive') { - payload.adaptive_mode = document.getElementById('value-source-adaptive-mode').value; + } else if (sourceType === 'adaptive_time') { + payload.schedule = _getScheduleFromUI(); + if (payload.schedule.length < 2) { + errorEl.textContent = t('value_source.error.schedule_min'); + errorEl.style.display = ''; + return; + } + payload.min_value = parseFloat(document.getElementById('value-source-adaptive-min-value').value); + payload.max_value = parseFloat(document.getElementById('value-source-adaptive-max-value').value); + } else if (sourceType === 'adaptive_scene') { + payload.picture_source_id = document.getElementById('value-source-picture-source').value; + payload.scene_behavior = document.getElementById('value-source-scene-behavior').value; + payload.sensitivity = parseFloat(document.getElementById('value-source-scene-sensitivity').value); + payload.smoothing = parseFloat(document.getElementById('value-source-scene-smoothing').value); payload.min_value = parseFloat(document.getElementById('value-source-adaptive-min-value').value); payload.max_value = parseFloat(document.getElementById('value-source-adaptive-max-value').value); - if (payload.adaptive_mode === 'time_of_day') { - payload.schedule = _getScheduleFromUI(); - if (payload.schedule.length < 2) { - errorEl.textContent = t('value_source.error.schedule_min'); - errorEl.style.display = ''; - return; - } - } else if (payload.adaptive_mode === 'scene') { - payload.picture_source_id = document.getElementById('value-source-picture-source').value; - payload.scene_behavior = document.getElementById('value-source-scene-behavior').value; - payload.sensitivity = parseFloat(document.getElementById('value-source-scene-sensitivity').value); - payload.smoothing = parseFloat(document.getElementById('value-source-scene-smoothing').value); - } } try { @@ -224,7 +220,7 @@ export async function deleteValueSource(sourceId) { // ── Card rendering (used by streams.js) ─────────────────────── export function createValueSourceCard(src) { - const typeIcons = { static: '📊', animated: '🔄', audio: '🎵', adaptive: '🌤️' }; + const typeIcons = { static: '📊', animated: '🔄', audio: '🎵', adaptive_time: '🕐', adaptive_scene: '🌤️' }; const icon = typeIcons[src.source_type] || '🎚️'; let propsHtml = ''; @@ -245,23 +241,19 @@ export function createValueSourceCard(src) { ${escapeHtml(audioName)} ${modeLabel.toUpperCase()} `; - } else if (src.source_type === 'adaptive') { - if (src.adaptive_mode === 'scene') { - const ps = _cachedStreams.find(s => s.id === src.picture_source_id); - const psName = ps ? ps.name : (src.picture_source_id || '-'); - propsHtml = ` - ${t('value_source.adaptive_mode.scene')} - ${escapeHtml(psName)} - ${src.scene_behavior || 'complement'} - `; - } else { - const pts = (src.schedule || []).length; - propsHtml = ` - ${t('value_source.adaptive_mode.time_of_day')} - ${pts} ${t('value_source.schedule.points')} - ${src.min_value ?? 0}–${src.max_value ?? 1} - `; - } + } else if (src.source_type === 'adaptive_time') { + const pts = (src.schedule || []).length; + propsHtml = ` + ${pts} ${t('value_source.schedule.points')} + ${src.min_value ?? 0}–${src.max_value ?? 1} + `; + } 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 || '-'); + propsHtml = ` + ${escapeHtml(psName)} + ${src.scene_behavior || 'complement'} + `; } return ` diff --git a/server/src/wled_controller/static/locales/en.json b/server/src/wled_controller/static/locales/en.json index 318fa22..22b253d 100644 --- a/server/src/wled_controller/static/locales/en.json +++ b/server/src/wled_controller/static/locales/en.json @@ -774,11 +774,12 @@ "value_source.name.placeholder": "Brightness Pulse", "value_source.name.hint": "A descriptive name for this value source", "value_source.type": "Type:", - "value_source.type.hint": "Static outputs a constant value. Animated cycles through a waveform. Audio reacts to sound input. Adaptive adjusts based on time of day or scene brightness.", + "value_source.type.hint": "Static outputs a constant value. Animated cycles through a waveform. Audio reacts to sound input. Adaptive types adjust brightness automatically based on time of day or scene content.", "value_source.type.static": "Static", "value_source.type.animated": "Animated", "value_source.type.audio": "Audio", - "value_source.type.adaptive": "Adaptive", + "value_source.type.adaptive_time": "Adaptive (Time of Day)", + "value_source.type.adaptive_scene": "Adaptive (Scene)", "value_source.value": "Value:", "value_source.value.hint": "Constant output value (0.0 = off, 1.0 = full brightness)", "value_source.waveform": "Waveform:", @@ -804,10 +805,6 @@ "value_source.sensitivity.hint": "Gain multiplier for the audio signal (higher = more reactive)", "value_source.smoothing": "Smoothing:", "value_source.smoothing.hint": "Temporal smoothing (0 = instant response, 1 = very smooth/slow)", - "value_source.adaptive_mode": "Adaptive Mode:", - "value_source.adaptive_mode.hint": "Time of Day adjusts brightness on a daily schedule. Scene analyzes picture brightness in real time.", - "value_source.adaptive_mode.time_of_day": "Time of Day", - "value_source.adaptive_mode.scene": "Scene Brightness", "value_source.schedule": "Schedule:", "value_source.schedule.hint": "Define at least 2 time points. Brightness interpolates linearly between them, wrapping at midnight.", "value_source.schedule.add": "+ Add Point", diff --git a/server/src/wled_controller/static/locales/ru.json b/server/src/wled_controller/static/locales/ru.json index 5bc23c3..15c602a 100644 --- a/server/src/wled_controller/static/locales/ru.json +++ b/server/src/wled_controller/static/locales/ru.json @@ -774,11 +774,12 @@ "value_source.name.placeholder": "Пульс яркости", "value_source.name.hint": "Описательное имя для этого источника значений", "value_source.type": "Тип:", - "value_source.type.hint": "Статический выдаёт постоянное значение. Анимированный циклически меняет форму волны. Аудио реагирует на звук. Адаптивный подстраивается под время суток или яркость сцены.", + "value_source.type.hint": "Статический выдаёт постоянное значение. Анимированный циклически меняет форму волны. Аудио реагирует на звук. Адаптивные типы автоматически подстраивают яркость по времени суток или содержимому сцены.", "value_source.type.static": "Статический", "value_source.type.animated": "Анимированный", "value_source.type.audio": "Аудио", - "value_source.type.adaptive": "Адаптивный", + "value_source.type.adaptive_time": "Адаптивный (Время суток)", + "value_source.type.adaptive_scene": "Адаптивный (Сцена)", "value_source.value": "Значение:", "value_source.value.hint": "Постоянное выходное значение (0.0 = выкл, 1.0 = полная яркость)", "value_source.waveform": "Форма волны:", @@ -804,10 +805,6 @@ "value_source.sensitivity.hint": "Множитель усиления аудиосигнала (выше = более реактивный)", "value_source.smoothing": "Сглаживание:", "value_source.smoothing.hint": "Временное сглаживание (0 = мгновенный отклик, 1 = очень плавный/медленный)", - "value_source.adaptive_mode": "Адаптивный режим:", - "value_source.adaptive_mode.hint": "Время суток регулирует яркость по дневному расписанию. Сцена анализирует яркость изображения в реальном времени.", - "value_source.adaptive_mode.time_of_day": "Время суток", - "value_source.adaptive_mode.scene": "Яркость сцены", "value_source.schedule": "Расписание:", "value_source.schedule.hint": "Определите минимум 2 временные точки. Яркость линейно интерполируется между ними, с переходом через полночь.", "value_source.schedule.add": "+ Добавить точку", diff --git a/server/src/wled_controller/storage/value_source.py b/server/src/wled_controller/storage/value_source.py index 153c024..965c263 100644 --- a/server/src/wled_controller/storage/value_source.py +++ b/server/src/wled_controller/storage/value_source.py @@ -1,11 +1,13 @@ """Value source data model with inheritance-based source types. A ValueSource produces a scalar float (0.0–1.0) that can drive target -parameters like brightness. Four types: +parameters like brightness. Five types: StaticValueSource — constant float value AnimatedValueSource — periodic waveform (sine, triangle, square, sawtooth) AudioValueSource — audio-reactive scalar (RMS, peak, beat detection) - AdaptiveValueSource — adapts to external conditions (time of day, scene brightness) + AdaptiveValueSource — adapts to external conditions: + adaptive_time — interpolates brightness along a 24-hour schedule + adaptive_scene — derives brightness from a picture source's frame luminance """ from dataclasses import dataclass, field @@ -19,7 +21,7 @@ class ValueSource: id: str name: str - source_type: str # "static" | "animated" | "audio" + source_type: str # "static" | "animated" | "audio" | "adaptive_time" | "adaptive_scene" created_at: datetime updated_at: datetime description: Optional[str] = None @@ -43,7 +45,6 @@ class ValueSource: "mode": None, "sensitivity": None, "smoothing": None, - "adaptive_mode": None, "schedule": None, "picture_source_id": None, "scene_behavior": None, @@ -92,12 +93,19 @@ class ValueSource: smoothing=float(data.get("smoothing") or 0.3), ) - if source_type == "adaptive": + if source_type == "adaptive_time": return AdaptiveValueSource( - id=sid, name=name, source_type="adaptive", + id=sid, name=name, source_type="adaptive_time", created_at=created_at, updated_at=updated_at, description=description, - adaptive_mode=data.get("adaptive_mode") or "time_of_day", schedule=data.get("schedule") or [], + min_value=float(data.get("min_value") or 0.0), + max_value=float(data["max_value"]) if data.get("max_value") is not None else 1.0, + ) + + if source_type == "adaptive_scene": + return AdaptiveValueSource( + id=sid, name=name, source_type="adaptive_scene", + created_at=created_at, updated_at=updated_at, description=description, picture_source_id=data.get("picture_source_id") or "", scene_behavior=data.get("scene_behavior") or "complement", sensitivity=float(data.get("sensitivity") or 1.0), @@ -177,12 +185,11 @@ class AudioValueSource(ValueSource): class AdaptiveValueSource(ValueSource): """Value source that adapts to external conditions. - Two sub-modes: - time_of_day — interpolates brightness along a 24-hour schedule - scene — derives brightness from a picture source's frame luminance + source_type determines the sub-mode: + adaptive_time — interpolates brightness along a 24-hour schedule + adaptive_scene — derives brightness from a picture source's frame luminance """ - adaptive_mode: str = "time_of_day" # "time_of_day" | "scene" schedule: List[dict] = field(default_factory=list) # [{time: "HH:MM", value: 0.0-1.0}] picture_source_id: str = "" # for scene mode scene_behavior: str = "complement" # "complement" | "match" @@ -193,7 +200,6 @@ class AdaptiveValueSource(ValueSource): def to_dict(self) -> dict: d = super().to_dict() - d["adaptive_mode"] = self.adaptive_mode d["schedule"] = self.schedule d["picture_source_id"] = self.picture_source_id d["scene_behavior"] = self.scene_behavior diff --git a/server/src/wled_controller/storage/value_source_store.py b/server/src/wled_controller/storage/value_source_store.py index b833010..9acb494 100644 --- a/server/src/wled_controller/storage/value_source_store.py +++ b/server/src/wled_controller/storage/value_source_store.py @@ -102,7 +102,6 @@ class ValueSourceStore: sensitivity: Optional[float] = None, smoothing: Optional[float] = None, description: Optional[str] = None, - adaptive_mode: Optional[str] = None, schedule: Optional[list] = None, picture_source_id: Optional[str] = None, scene_behavior: Optional[str] = None, @@ -110,7 +109,7 @@ class ValueSourceStore: if not name or not name.strip(): raise ValueError("Name is required") - if source_type not in ("static", "animated", "audio", "adaptive"): + if source_type not in ("static", "animated", "audio", "adaptive_time", "adaptive_scene"): raise ValueError(f"Invalid source type: {source_type}") for source in self._sources.values(): @@ -144,16 +143,21 @@ class ValueSourceStore: sensitivity=sensitivity if sensitivity is not None else 1.0, smoothing=smoothing if smoothing is not None else 0.3, ) - elif source_type == "adaptive": - am = adaptive_mode or "time_of_day" + elif source_type == "adaptive_time": schedule_data = schedule or [] - if am == "time_of_day" and len(schedule_data) < 2: + if len(schedule_data) < 2: raise ValueError("Time of day schedule requires at least 2 points") source = AdaptiveValueSource( - id=sid, name=name, source_type="adaptive", + id=sid, name=name, source_type="adaptive_time", created_at=now, updated_at=now, description=description, - adaptive_mode=am, schedule=schedule_data, + min_value=min_value if min_value is not None else 0.0, + max_value=max_value if max_value is not None else 1.0, + ) + elif source_type == "adaptive_scene": + source = AdaptiveValueSource( + id=sid, name=name, source_type="adaptive_scene", + created_at=now, updated_at=now, description=description, picture_source_id=picture_source_id or "", scene_behavior=scene_behavior or "complement", sensitivity=sensitivity if sensitivity is not None else 1.0, @@ -182,7 +186,6 @@ class ValueSourceStore: sensitivity: Optional[float] = None, smoothing: Optional[float] = None, description: Optional[str] = None, - adaptive_mode: Optional[str] = None, schedule: Optional[list] = None, picture_source_id: Optional[str] = None, scene_behavior: Optional[str] = None, @@ -223,10 +226,8 @@ class ValueSourceStore: if smoothing is not None: source.smoothing = smoothing elif isinstance(source, AdaptiveValueSource): - if adaptive_mode is not None: - source.adaptive_mode = adaptive_mode if schedule is not None: - if source.adaptive_mode == "time_of_day" and len(schedule) < 2: + if source.source_type == "adaptive_time" and len(schedule) < 2: raise ValueError("Time of day schedule requires at least 2 points") source.schedule = schedule if picture_source_id is not None: diff --git a/server/src/wled_controller/templates/modals/value-source-editor.html b/server/src/wled_controller/templates/modals/value-source-editor.html index dc6a382..2085d2e 100644 --- a/server/src/wled_controller/templates/modals/value-source-editor.html +++ b/server/src/wled_controller/templates/modals/value-source-editor.html @@ -32,7 +32,8 @@ - + + @@ -161,87 +162,73 @@ - -