0f5850ef80
Extends the icon-plate work from devices and output targets to every
remaining card type — 18 new entities, 20 in total. Users can now pick
a curated icon (with optional colour override) for any card on any tab,
and the picker reuses the same modal, recent-strip, search, and
category tabs introduced for the device picker.
Foundation:
- icon-picker.ts — replace the hardcoded 2-entry adapter record with a
Map<EntityType, EntityTypeAdapter> and expose
registerIconEntityType() + makeSimpleIconAdapter() so each feature
module owns its own adapter (~6 lines per type).
- bodyExtras hook on adapters, keyed off id, lets discriminated routes
(output-targets target_type, picture-sources stream_type, audio /
value / color-strip-sources source_type) accept icon-only PUTs.
- core/card-icon.ts — new makeCardIconFields(type, id, entity) helper
spreads iconHtml / iconColor / iconAttrs into a mod-card head in one
line.
- _onDocumentClick now accepts any registered type instead of a
hardcoded device/target check.
Backend (purely additive — no migrations needed thanks to JSON-blob
storage):
- 18 dataclasses gained icon: str = "" + icon_color: str = "" with
emit-when-truthy serialisation and "" defaults on load.
- All matching Create / Update / Response Pydantic schemas gained the
fields with the standard Optional[str] + max_length=64/32 +
description set.
- All routes' response builders use
getattr(entity, "icon", "") or "" so existing rows render unchanged.
- ValueSource and CSS handle icon/icon_color on the base class so all
source-type subclasses inherit them automatically.
Frontend wiring (12 modules):
- streams.ts — picture sources, capture templates, PP templates,
CSPT, audio sources, audio templates, gradients (built-in
gradients keep no plate).
- automations, scene-presets, sync-clocks, weather-sources,
value-sources, mqtt-sources, home-assistant-sources,
game-integration, audio-processing-templates, assets,
color-strips/cards.
- pattern-templates skipped — uses the legacy wrapCard({content,
actions}) string API, separate migration.
Dashboard cards now also display the chosen icon:
- Targets already had it (with device inheritance for LED targets).
- Sync clocks, automations, and scene presets gained the same plate
via a shared _dashboardIconPlate helper that mirrors the mod-card
layout (mod-head--with-icon class flips on when present).
i18n: 20 new device.icon.entity.<type> labels in en/ru/zh.
Verification:
- ruff check src/ tests/ — clean.
- npx tsc --noEmit — clean.
- npm run build — 2.6 MB bundle.
- pytest tests/ --no-cov — 949 passed (no regressions).
Pending: manual smoke test on each card type — open picker, save, and
confirm the channel-color preview matches the live card.
75 lines
2.2 KiB
Python
75 lines
2.2 KiB
Python
"""Capture template data model."""
|
|
|
|
from dataclasses import dataclass, field
|
|
from datetime import datetime, timezone
|
|
from typing import Any, Dict, List, Optional
|
|
|
|
|
|
@dataclass
|
|
class CaptureTemplate:
|
|
"""Represents a screen capture template configuration."""
|
|
|
|
id: str
|
|
name: str
|
|
engine_type: str
|
|
engine_config: Dict[str, Any]
|
|
created_at: datetime
|
|
updated_at: datetime
|
|
description: Optional[str] = None
|
|
tags: List[str] = field(default_factory=list)
|
|
icon: str = ""
|
|
icon_color: str = ""
|
|
|
|
def to_dict(self) -> dict:
|
|
"""Convert template to dictionary.
|
|
|
|
Returns:
|
|
Dictionary representation
|
|
"""
|
|
d = {
|
|
"id": self.id,
|
|
"name": self.name,
|
|
"engine_type": self.engine_type,
|
|
"engine_config": self.engine_config,
|
|
"created_at": self.created_at.isoformat(),
|
|
"updated_at": self.updated_at.isoformat(),
|
|
"description": self.description,
|
|
"tags": self.tags,
|
|
}
|
|
if self.icon:
|
|
d["icon"] = self.icon
|
|
if self.icon_color:
|
|
d["icon_color"] = self.icon_color
|
|
return d
|
|
|
|
@classmethod
|
|
def from_dict(cls, data: dict) -> "CaptureTemplate":
|
|
"""Create template from dictionary.
|
|
|
|
Args:
|
|
data: Dictionary with template data
|
|
|
|
Returns:
|
|
CaptureTemplate instance
|
|
"""
|
|
return cls(
|
|
id=data["id"],
|
|
name=data["name"],
|
|
engine_type=data["engine_type"],
|
|
engine_config=data.get("engine_config", {}),
|
|
created_at=(
|
|
datetime.fromisoformat(data["created_at"])
|
|
if isinstance(data.get("created_at"), str)
|
|
else data.get("created_at", datetime.now(timezone.utc))
|
|
),
|
|
updated_at=(
|
|
datetime.fromisoformat(data["updated_at"])
|
|
if isinstance(data.get("updated_at"), str)
|
|
else data.get("updated_at", datetime.now(timezone.utc))
|
|
),
|
|
description=data.get("description"),
|
|
tags=data.get("tags", []),
|
|
icon=data.get("icon", ""),
|
|
icon_color=data.get("icon_color", ""),
|
|
)
|