Rename picture-targets to output-targets across entire codebase

Rename all Python modules, classes, API endpoints, config keys, frontend
fetch URLs, and Home Assistant integration URLs from picture-targets to
output-targets. Store loads both new and legacy JSON keys for backward
compatibility with existing data files.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
2026-03-09 10:55:36 +03:00
parent 5b4813368b
commit 353a1c2d85
37 changed files with 243 additions and 244 deletions

View File

@@ -16,7 +16,7 @@ class Device:
A device holds connection state and output settings.
Calibration, processing settings, and picture source assignments
now live on ColorStripSource and WledPictureTarget respectively.
now live on ColorStripSource and WledOutputTarget respectively.
"""
def __init__(

View File

@@ -1,10 +1,10 @@
"""Key colors picture target — extracts key colors from image rectangles."""
"""Key colors output target — extracts key colors from image rectangles."""
from dataclasses import dataclass, field
from datetime import datetime
from typing import List, Optional
from wled_controller.storage.picture_target import PictureTarget
from wled_controller.storage.output_target import OutputTarget
@dataclass
@@ -71,7 +71,7 @@ class KeyColorsSettings:
@dataclass
class KeyColorsPictureTarget(PictureTarget):
class KeyColorsOutputTarget(OutputTarget):
"""Key colors extractor target — extracts key colors from image rectangles."""
picture_source_id: str = ""
@@ -119,7 +119,7 @@ class KeyColorsPictureTarget(PictureTarget):
return d
@classmethod
def from_dict(cls, data: dict) -> "KeyColorsPictureTarget":
def from_dict(cls, data: dict) -> "KeyColorsOutputTarget":
settings_data = data.get("settings", {})
settings = KeyColorsSettings.from_dict(settings_data)

View File

@@ -1,4 +1,4 @@
"""Picture target base data model."""
"""Output target base data model."""
from dataclasses import dataclass
from datetime import datetime
@@ -6,8 +6,8 @@ from typing import Optional
@dataclass
class PictureTarget:
"""Base class for picture targets."""
class OutputTarget:
"""Base class for output targets."""
id: str
name: str
@@ -50,13 +50,13 @@ class PictureTarget:
}
@classmethod
def from_dict(cls, data: dict) -> "PictureTarget":
def from_dict(cls, data: dict) -> "OutputTarget":
"""Create from dictionary, dispatching to the correct subclass."""
target_type = data.get("target_type", "led")
if target_type == "led":
from wled_controller.storage.wled_picture_target import WledPictureTarget
return WledPictureTarget.from_dict(data)
from wled_controller.storage.wled_output_target import WledOutputTarget
return WledOutputTarget.from_dict(data)
if target_type == "key_colors":
from wled_controller.storage.key_colors_picture_target import KeyColorsPictureTarget
return KeyColorsPictureTarget.from_dict(data)
from wled_controller.storage.key_colors_output_target import KeyColorsOutputTarget
return KeyColorsOutputTarget.from_dict(data)
raise ValueError(f"Unknown target type: {target_type}")

View File

@@ -1,4 +1,4 @@
"""Picture target storage using JSON files."""
"""Output target storage using JSON files."""
import json
import uuid
@@ -6,11 +6,11 @@ from datetime import datetime
from pathlib import Path
from typing import Dict, List, Optional
from wled_controller.storage.picture_target import PictureTarget
from wled_controller.storage.wled_picture_target import WledPictureTarget
from wled_controller.storage.key_colors_picture_target import (
from wled_controller.storage.output_target import OutputTarget
from wled_controller.storage.wled_output_target import WledOutputTarget
from wled_controller.storage.key_colors_output_target import (
KeyColorsSettings,
KeyColorsPictureTarget,
KeyColorsOutputTarget,
)
from wled_controller.utils import atomic_write_json, get_logger
@@ -19,17 +19,17 @@ logger = get_logger(__name__)
DEFAULT_STATE_CHECK_INTERVAL = 30 # seconds
class PictureTargetStore:
"""Persistent storage for picture targets."""
class OutputTargetStore:
"""Persistent storage for output targets."""
def __init__(self, file_path: str):
"""Initialize picture target store.
"""Initialize output target store.
Args:
file_path: Path to targets JSON file
"""
self.file_path = Path(file_path)
self._targets: Dict[str, PictureTarget] = {}
self._targets: Dict[str, OutputTarget] = {}
self._load()
def _load(self) -> None:
@@ -41,52 +41,53 @@ class PictureTargetStore:
with open(self.file_path, "r", encoding="utf-8") as f:
data = json.load(f)
targets_data = data.get("picture_targets", {})
# Support both new "output_targets" and legacy "picture_targets" keys
targets_data = data.get("output_targets") or data.get("picture_targets", {})
loaded = 0
for target_id, target_dict in targets_data.items():
try:
target = PictureTarget.from_dict(target_dict)
target = OutputTarget.from_dict(target_dict)
self._targets[target_id] = target
loaded += 1
except Exception as e:
logger.error(f"Failed to load picture target {target_id}: {e}", exc_info=True)
logger.error(f"Failed to load output target {target_id}: {e}", exc_info=True)
if loaded > 0:
logger.info(f"Loaded {loaded} picture targets from storage")
logger.info(f"Loaded {loaded} output targets from storage")
except Exception as e:
logger.error(f"Failed to load picture targets from {self.file_path}: {e}")
logger.error(f"Failed to load output targets from {self.file_path}: {e}")
raise
logger.info(f"Picture target store initialized with {len(self._targets)} targets")
logger.info(f"Output target store initialized with {len(self._targets)} targets")
def _save(self) -> None:
"""Save all targets to file."""
try:
data = {
"version": "1.0.0",
"picture_targets": {
"output_targets": {
target_id: target.to_dict()
for target_id, target in self._targets.items()
},
}
atomic_write_json(self.file_path, data)
except Exception as e:
logger.error(f"Failed to save picture targets to {self.file_path}: {e}")
logger.error(f"Failed to save output targets to {self.file_path}: {e}")
raise
def get_all_targets(self) -> List[PictureTarget]:
"""Get all picture targets."""
def get_all_targets(self) -> List[OutputTarget]:
"""Get all output targets."""
return list(self._targets.values())
def get_target(self, target_id: str) -> PictureTarget:
def get_target(self, target_id: str) -> OutputTarget:
"""Get target by ID.
Raises:
ValueError: If target not found
"""
if target_id not in self._targets:
raise ValueError(f"Picture target not found: {target_id}")
raise ValueError(f"Output target not found: {target_id}")
return self._targets[target_id]
def create_target(
@@ -105,8 +106,8 @@ class PictureTargetStore:
key_colors_settings: Optional[KeyColorsSettings] = None,
description: Optional[str] = None,
picture_source_id: str = "",
) -> PictureTarget:
"""Create a new picture target.
) -> OutputTarget:
"""Create a new output target.
Raises:
ValueError: If validation fails
@@ -117,13 +118,13 @@ class PictureTargetStore:
# Check for duplicate name
for target in self._targets.values():
if target.name == name:
raise ValueError(f"Picture target with name '{name}' already exists")
raise ValueError(f"Output target with name '{name}' already exists")
target_id = f"pt_{uuid.uuid4().hex[:8]}"
now = datetime.utcnow()
if target_type == "led":
target: PictureTarget = WledPictureTarget(
target: OutputTarget = WledOutputTarget(
id=target_id,
name=name,
target_type="led",
@@ -141,7 +142,7 @@ class PictureTargetStore:
updated_at=now,
)
elif target_type == "key_colors":
target = KeyColorsPictureTarget(
target = KeyColorsOutputTarget(
id=target_id,
name=name,
target_type="key_colors",
@@ -157,7 +158,7 @@ class PictureTargetStore:
self._targets[target_id] = target
self._save()
logger.info(f"Created picture target: {name} ({target_id}, type={target_type})")
logger.info(f"Created output target: {name} ({target_id}, type={target_type})")
return target
def update_target(
@@ -175,14 +176,14 @@ class PictureTargetStore:
protocol: Optional[str] = None,
key_colors_settings: Optional[KeyColorsSettings] = None,
description: Optional[str] = None,
) -> PictureTarget:
"""Update a picture target.
) -> OutputTarget:
"""Update an output target.
Raises:
ValueError: If target not found or validation fails
"""
if target_id not in self._targets:
raise ValueError(f"Picture target not found: {target_id}")
raise ValueError(f"Output target not found: {target_id}")
target = self._targets[target_id]
@@ -190,7 +191,7 @@ class PictureTargetStore:
# Check for duplicate name (exclude self)
for other in self._targets.values():
if other.id != target_id and other.name == name:
raise ValueError(f"Picture target with name '{name}' already exists")
raise ValueError(f"Output target with name '{name}' already exists")
target.update_fields(
name=name,
@@ -210,42 +211,42 @@ class PictureTargetStore:
target.updated_at = datetime.utcnow()
self._save()
logger.info(f"Updated picture target: {target_id}")
logger.info(f"Updated output target: {target_id}")
return target
def delete_target(self, target_id: str) -> None:
"""Delete a picture target.
"""Delete an output target.
Raises:
ValueError: If target not found
"""
if target_id not in self._targets:
raise ValueError(f"Picture target not found: {target_id}")
raise ValueError(f"Output target not found: {target_id}")
del self._targets[target_id]
self._save()
logger.info(f"Deleted picture target: {target_id}")
logger.info(f"Deleted output target: {target_id}")
def get_targets_for_device(self, device_id: str) -> List[PictureTarget]:
def get_targets_for_device(self, device_id: str) -> List[OutputTarget]:
"""Get all targets that reference a specific device."""
return [
t for t in self._targets.values()
if isinstance(t, WledPictureTarget) and t.device_id == device_id
if isinstance(t, WledOutputTarget) and t.device_id == device_id
]
def get_targets_referencing_source(self, source_id: str) -> List[str]:
"""Return names of KC targets that reference a picture source."""
return [
target.name for target in self._targets.values()
if isinstance(target, KeyColorsPictureTarget) and target.picture_source_id == source_id
if isinstance(target, KeyColorsOutputTarget) and target.picture_source_id == source_id
]
def get_targets_referencing_css(self, css_id: str) -> List[str]:
"""Return names of LED targets that reference a color strip source."""
return [
target.name for target in self._targets.values()
if isinstance(target, WledPictureTarget)
if isinstance(target, WledOutputTarget)
and target.color_strip_source_id == css_id
]

View File

@@ -4,7 +4,7 @@ from dataclasses import dataclass, field
from datetime import datetime
from typing import List, Optional
from wled_controller.storage.key_colors_picture_target import KeyColorRectangle
from wled_controller.storage.key_colors_output_target import KeyColorRectangle
@dataclass

View File

@@ -6,7 +6,7 @@ from datetime import datetime
from pathlib import Path
from typing import Dict, List, Optional
from wled_controller.storage.key_colors_picture_target import KeyColorRectangle
from wled_controller.storage.key_colors_output_target import KeyColorRectangle
from wled_controller.storage.pattern_template import PatternTemplate
from wled_controller.utils import atomic_write_json, get_logger
@@ -203,11 +203,11 @@ class PatternTemplateStore:
logger.info(f"Deleted pattern template: {template_id}")
def get_targets_referencing(self, template_id: str, picture_target_store) -> List[str]:
def get_targets_referencing(self, template_id: str, output_target_store) -> List[str]:
"""Return names of KC targets that reference this template."""
from wled_controller.storage.key_colors_picture_target import KeyColorsPictureTarget
from wled_controller.storage.key_colors_output_target import KeyColorsOutputTarget
return [
target.name for target in picture_target_store.get_all_targets()
if isinstance(target, KeyColorsPictureTarget) and target.settings.pattern_template_id == template_id
target.name for target in output_target_store.get_all_targets()
if isinstance(target, KeyColorsOutputTarget) and target.settings.pattern_template_id == template_id
]

View File

@@ -1,17 +1,17 @@
"""LED picture target — sends color strip sources to an LED device."""
"""LED output target — sends color strip sources to an LED device."""
from dataclasses import dataclass
from datetime import datetime
from typing import Optional
from wled_controller.storage.picture_target import PictureTarget
from wled_controller.storage.output_target import OutputTarget
DEFAULT_STATE_CHECK_INTERVAL = 30 # seconds
@dataclass
class WledPictureTarget(PictureTarget):
"""LED picture target — pairs an LED device with a ColorStripSource."""
class WledOutputTarget(OutputTarget):
"""LED output target — pairs an LED device with a ColorStripSource."""
device_id: str = ""
color_strip_source_id: str = ""
@@ -104,7 +104,7 @@ class WledPictureTarget(PictureTarget):
return d
@classmethod
def from_dict(cls, data: dict) -> "WledPictureTarget":
def from_dict(cls, data: dict) -> "WledOutputTarget":
"""Create from dictionary."""
return cls(
id=data["id"],