Add adaptive brightness value source with time-of-day and scene modes

New "adaptive" value source type that automatically adjusts brightness
based on external conditions. Two sub-modes: time-of-day (schedule-based
interpolation with midnight wrap) and scene brightness (frame luminance
analysis via numpy BT.601 subsampling with EMA smoothing).

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
2026-02-24 15:14:30 +03:00
parent 48651f0a4e
commit d339dd3f90
11 changed files with 643 additions and 19 deletions

View File

@@ -1,15 +1,16 @@
"""Value source data model with inheritance-based source types.
A ValueSource produces a scalar float (0.01.0) that can drive target
parameters like brightness. Three types:
parameters like brightness. Four 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)
"""
from dataclasses import dataclass
from dataclasses import dataclass, field
from datetime import datetime
from typing import Optional
from typing import List, Optional
@dataclass
@@ -42,6 +43,10 @@ class ValueSource:
"mode": None,
"sensitivity": None,
"smoothing": None,
"adaptive_mode": None,
"schedule": None,
"picture_source_id": None,
"scene_behavior": None,
}
@staticmethod
@@ -87,6 +92,20 @@ class ValueSource:
smoothing=float(data.get("smoothing") or 0.3),
)
if source_type == "adaptive":
return AdaptiveValueSource(
id=sid, name=name, source_type="adaptive",
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 [],
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),
smoothing=float(data.get("smoothing") or 0.3),
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,
)
# Default: "static" type
return StaticValueSource(
id=sid, name=name, source_type="static",
@@ -152,3 +171,34 @@ class AudioValueSource(ValueSource):
d["sensitivity"] = self.sensitivity
d["smoothing"] = self.smoothing
return d
@dataclass
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
"""
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"
sensitivity: float = 1.0 # gain multiplier (0.1-5.0)
smoothing: float = 0.3 # temporal smoothing (0.0-1.0)
min_value: float = 0.0 # output range min
max_value: float = 1.0 # output range max
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
d["sensitivity"] = self.sensitivity
d["smoothing"] = self.smoothing
d["min_value"] = self.min_value
d["max_value"] = self.max_value
return d