feat(01-01): create package structure, Pydantic models, and device abstractions
- pyproject.toml with FastAPI, uvicorn, Pydantic v2, aiofiles, structlog deps - DeviceConfig Pydantic model with StripType, UUID, validation constraints - ShowModel with AudioRef, TrackModel, CueModel, AnalysisBlock, schema_version=1 - BaseDevice ABC with encode_frame, encode_animation_cmd, bytes_per_pixel - SK6812Device (4 bytes/pixel) and WS2801Device (3 bytes/pixel) implementations
This commit is contained in:
4
lightsync/models/__init__.py
Normal file
4
lightsync/models/__init__.py
Normal file
@@ -0,0 +1,4 @@
|
||||
from lightsync.models.device import DeviceConfig
|
||||
from lightsync.models.show import ShowModel
|
||||
|
||||
__all__ = ["DeviceConfig", "ShowModel"]
|
||||
17
lightsync/models/device.py
Normal file
17
lightsync/models/device.py
Normal file
@@ -0,0 +1,17 @@
|
||||
from uuid import UUID, uuid4
|
||||
from typing import Literal
|
||||
from pydantic import BaseModel, Field
|
||||
|
||||
StripType = Literal["sk6812", "ws2801", "generic"]
|
||||
|
||||
|
||||
class DeviceConfig(BaseModel):
|
||||
id: UUID = Field(default_factory=uuid4)
|
||||
name: str
|
||||
strip_type: StripType
|
||||
led_count: int = Field(gt=0, le=1000)
|
||||
ip: str
|
||||
port: int = Field(ge=1, le=65535)
|
||||
enabled: bool = True
|
||||
|
||||
model_config = {"populate_by_name": True}
|
||||
46
lightsync/models/show.py
Normal file
46
lightsync/models/show.py
Normal file
@@ -0,0 +1,46 @@
|
||||
from uuid import UUID, uuid4
|
||||
from datetime import datetime, timezone
|
||||
from typing import Any, Literal
|
||||
from pydantic import BaseModel, Field
|
||||
|
||||
from lightsync.models.device import DeviceConfig
|
||||
|
||||
|
||||
class AudioRef(BaseModel):
|
||||
source_type: Literal["file", "youtube"] = "file"
|
||||
path: str | None = None
|
||||
yt_url: str | None = None
|
||||
duration_seconds: float | None = None
|
||||
|
||||
|
||||
class CueModel(BaseModel):
|
||||
id: UUID = Field(default_factory=uuid4)
|
||||
timestamp: float
|
||||
mode: Literal["animation", "frame_sequence"] = "animation"
|
||||
animation: str | None = None
|
||||
params: dict[str, Any] = Field(default_factory=dict)
|
||||
|
||||
|
||||
class TrackModel(BaseModel):
|
||||
device_id: UUID
|
||||
cues: list[CueModel] = Field(default_factory=list)
|
||||
|
||||
|
||||
class AnalysisBlock(BaseModel):
|
||||
analysed_at: datetime | None = None
|
||||
tempo_bpm: float | None = None
|
||||
beat_times: list[float] = Field(default_factory=list)
|
||||
onset_times: list[float] = Field(default_factory=list)
|
||||
|
||||
|
||||
class ShowModel(BaseModel):
|
||||
schema_version: int = 1
|
||||
id: UUID = Field(default_factory=uuid4)
|
||||
name: str
|
||||
created_at: datetime = Field(default_factory=lambda: datetime.now(timezone.utc))
|
||||
updated_at: datetime = Field(default_factory=lambda: datetime.now(timezone.utc))
|
||||
audio: AudioRef = Field(default_factory=AudioRef)
|
||||
devices: list[DeviceConfig] = Field(default_factory=list)
|
||||
analysis: AnalysisBlock = Field(default_factory=AnalysisBlock)
|
||||
tracks: list[TrackModel] = Field(default_factory=list)
|
||||
ai_sequences: list[Any] = Field(default_factory=list)
|
||||
Reference in New Issue
Block a user