Add LED skip start/end, rename standby_interval to keepalive_interval, remove migrations
LED skip: set first N and last M LEDs to black on a target. Color sources (static, gradient, effect, color cycle) render across only the active (non-skipped) LEDs. Processor pads with blacks before sending to device. Rename standby_interval → keepalive_interval across all Python, API schemas, and JS. from_dict falls back to old key for existing configs. Remove legacy migration functions (_migrate_devices_to_targets, _migrate_targets_to_color_strips) and legacy fields from target model. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -54,105 +54,6 @@ processor_manager = ProcessorManager(
|
||||
)
|
||||
|
||||
|
||||
def _migrate_devices_to_targets():
|
||||
"""One-time migration: create picture targets from legacy device settings.
|
||||
|
||||
If the target store is empty and any device has legacy picture_source_id
|
||||
or settings in raw JSON, migrate them to WledPictureTargets.
|
||||
"""
|
||||
if picture_target_store.count() > 0:
|
||||
return # Already have targets, skip migration
|
||||
|
||||
raw = device_store.load_raw()
|
||||
devices_raw = raw.get("devices", {})
|
||||
if not devices_raw:
|
||||
return
|
||||
|
||||
migrated = 0
|
||||
for device_id, device_data in devices_raw.items():
|
||||
legacy_source_id = device_data.get("picture_source_id", "")
|
||||
|
||||
if not legacy_source_id:
|
||||
continue
|
||||
|
||||
device_name = device_data.get("name", device_id)
|
||||
target_name = f"{device_name} Target"
|
||||
|
||||
try:
|
||||
target = picture_target_store.create_target(
|
||||
name=target_name,
|
||||
target_type="wled",
|
||||
device_id=device_id,
|
||||
description=f"Auto-migrated from device {device_name}",
|
||||
)
|
||||
migrated += 1
|
||||
logger.info(f"Migrated device {device_id} -> target {target.id}")
|
||||
except Exception as e:
|
||||
logger.error(f"Failed to migrate device {device_id} to target: {e}")
|
||||
|
||||
if migrated > 0:
|
||||
logger.info(f"Migration complete: created {migrated} picture target(s) from legacy device settings")
|
||||
|
||||
|
||||
def _migrate_targets_to_color_strips():
|
||||
"""One-time migration: create ColorStripSources from legacy WledPictureTarget data.
|
||||
|
||||
For each WledPictureTarget that has a legacy _legacy_picture_source_id (from old JSON)
|
||||
but no color_strip_source_id, create a ColorStripSource and link it.
|
||||
"""
|
||||
from wled_controller.storage.wled_picture_target import WledPictureTarget
|
||||
from wled_controller.core.capture.calibration import create_default_calibration
|
||||
|
||||
migrated = 0
|
||||
for target in picture_target_store.get_all_targets():
|
||||
if not isinstance(target, WledPictureTarget):
|
||||
continue
|
||||
if target.color_strip_source_id:
|
||||
continue # already migrated
|
||||
if not target._legacy_picture_source_id:
|
||||
continue # no legacy source to migrate
|
||||
|
||||
legacy_settings = target._legacy_settings or {}
|
||||
|
||||
# Try to get calibration from device (old location)
|
||||
device = device_store.get_device(target.device_id) if target.device_id else None
|
||||
calibration = getattr(device, "_legacy_calibration", None) if device else None
|
||||
if calibration is None:
|
||||
calibration = create_default_calibration(0)
|
||||
|
||||
css_name = f"{target.name} Strip"
|
||||
# Ensure unique name
|
||||
existing_names = {s.name for s in color_strip_store.get_all_sources()}
|
||||
if css_name in existing_names:
|
||||
css_name = f"{target.name} Strip (migrated)"
|
||||
|
||||
try:
|
||||
css = color_strip_store.create_source(
|
||||
name=css_name,
|
||||
source_type="picture",
|
||||
picture_source_id=target._legacy_picture_source_id,
|
||||
fps=legacy_settings.get("fps", 30),
|
||||
brightness=legacy_settings.get("brightness", 1.0),
|
||||
smoothing=legacy_settings.get("smoothing", 0.3),
|
||||
interpolation_mode=legacy_settings.get("interpolation_mode", "average"),
|
||||
calibration=calibration,
|
||||
)
|
||||
|
||||
# Update target to reference the new CSS
|
||||
target.color_strip_source_id = css.id
|
||||
target.standby_interval = legacy_settings.get("standby_interval", 1.0)
|
||||
target.state_check_interval = legacy_settings.get("state_check_interval", 30)
|
||||
picture_target_store._save()
|
||||
|
||||
migrated += 1
|
||||
logger.info(f"Migrated target {target.id} -> CSS {css.id} ({css_name})")
|
||||
except Exception as e:
|
||||
logger.error(f"Failed to migrate target {target.id} to CSS: {e}")
|
||||
|
||||
if migrated > 0:
|
||||
logger.info(f"CSS migration complete: created {migrated} color strip source(s) from legacy targets")
|
||||
|
||||
|
||||
@asynccontextmanager
|
||||
async def lifespan(app: FastAPI):
|
||||
"""Application lifespan manager.
|
||||
@@ -182,10 +83,6 @@ async def lifespan(app: FastAPI):
|
||||
logger.info(f"Authorized clients: {client_labels}")
|
||||
logger.info("All API requests require valid Bearer token authentication")
|
||||
|
||||
# Run migrations
|
||||
_migrate_devices_to_targets()
|
||||
_migrate_targets_to_color_strips()
|
||||
|
||||
# Create profile engine (needs processor_manager)
|
||||
profile_engine = ProfileEngine(profile_store, processor_manager)
|
||||
|
||||
|
||||
Reference in New Issue
Block a user