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.
80 lines
2.9 KiB
Python
80 lines
2.9 KiB
Python
"""Pydantic schemas for pattern template API."""
|
|
|
|
from datetime import datetime
|
|
from typing import List, Optional
|
|
|
|
from pydantic import BaseModel, Field
|
|
|
|
from .output_targets import KeyColorRectangleSchema
|
|
|
|
|
|
class PatternTemplateCreate(BaseModel):
|
|
"""Request to create a pattern template."""
|
|
|
|
name: str = Field(description="Template name", min_length=1, max_length=100)
|
|
rectangles: List[KeyColorRectangleSchema] = Field(
|
|
default_factory=list, description="List of named rectangles"
|
|
)
|
|
description: Optional[str] = Field(None, description="Template description", max_length=500)
|
|
tags: List[str] = Field(default_factory=list, description="User-defined tags")
|
|
icon: Optional[str] = Field(
|
|
None,
|
|
max_length=64,
|
|
description="Icon id from the curated icon library. Pass empty string to clear.",
|
|
)
|
|
icon_color: Optional[str] = Field(
|
|
None,
|
|
max_length=32,
|
|
description="Optional CSS color override for the icon. Empty/null inherits the channel accent.",
|
|
)
|
|
|
|
|
|
class PatternTemplateUpdate(BaseModel):
|
|
"""Request to update a pattern template."""
|
|
|
|
name: Optional[str] = Field(None, description="Template name", min_length=1, max_length=100)
|
|
rectangles: Optional[List[KeyColorRectangleSchema]] = Field(
|
|
None, description="List of named rectangles"
|
|
)
|
|
description: Optional[str] = Field(None, description="Template description", max_length=500)
|
|
tags: Optional[List[str]] = None
|
|
icon: Optional[str] = Field(
|
|
None,
|
|
max_length=64,
|
|
description="Icon id from the curated icon library. Pass empty string to clear.",
|
|
)
|
|
icon_color: Optional[str] = Field(
|
|
None,
|
|
max_length=32,
|
|
description="Optional CSS color override for the icon. Empty/null inherits the channel accent.",
|
|
)
|
|
|
|
|
|
class PatternTemplateResponse(BaseModel):
|
|
"""Pattern template response."""
|
|
|
|
id: str = Field(description="Template ID")
|
|
name: str = Field(description="Template name")
|
|
rectangles: List[KeyColorRectangleSchema] = Field(description="List of named rectangles")
|
|
tags: List[str] = Field(default_factory=list, description="User-defined tags")
|
|
created_at: datetime = Field(description="Creation timestamp")
|
|
updated_at: datetime = Field(description="Last update timestamp")
|
|
description: Optional[str] = Field(None, description="Template description")
|
|
icon: Optional[str] = Field(
|
|
None,
|
|
max_length=64,
|
|
description="Icon id from the curated icon library. Pass empty string to clear.",
|
|
)
|
|
icon_color: Optional[str] = Field(
|
|
None,
|
|
max_length=32,
|
|
description="Optional CSS color override for the icon. Empty/null inherits the channel accent.",
|
|
)
|
|
|
|
|
|
class PatternTemplateListResponse(BaseModel):
|
|
"""List of pattern templates."""
|
|
|
|
templates: List[PatternTemplateResponse] = Field(description="List of pattern templates")
|
|
count: int = Field(description="Number of templates")
|