from __future__ import annotations from typing import Optional from pydantic import BaseModel, Field, field_validator class TriggerConfig(BaseModel): name: str = Field(..., min_length=1, max_length=80) type: str = Field(..., pattern=r"^GPIO\d+$") music_file: str = Field(..., min_length=1, max_length=255) start_seconds: Optional[float] = Field(default=0.0, ge=0) end_seconds: Optional[float] = Field(default=None, ge=0) volume: int = Field(default=80, ge=0, le=100) @field_validator("end_seconds") @classmethod def validate_time_window(cls, end_seconds: Optional[float], info): start_seconds = info.data.get("start_seconds", 0.0) if end_seconds is not None and end_seconds <= start_seconds: raise ValueError("end_seconds must be greater than start_seconds") return end_seconds class TriggerPatch(BaseModel): name: Optional[str] = Field(default=None, min_length=1, max_length=80) type: Optional[str] = Field(default=None, pattern=r"^GPIO\d+$") music_file: Optional[str] = Field(default=None, min_length=1, max_length=255) start_seconds: Optional[float] = Field(default=None, ge=0) end_seconds: Optional[float] = Field(default=None, ge=0) volume: Optional[int] = Field(default=None, ge=0, le=100) class ForcePlayRequest(BaseModel): trigger_id: Optional[str] = Field(default=None, min_length=1, max_length=80) music_file: Optional[str] = Field(default=None, min_length=1, max_length=255) start_seconds: float = Field(default=0.0, ge=0) end_seconds: Optional[float] = Field(default=None, ge=0) @field_validator("end_seconds") @classmethod def validate_end_seconds(cls, end_seconds: Optional[float], info): start_seconds = info.data.get("start_seconds", 0.0) if end_seconds is not None and end_seconds <= start_seconds: raise ValueError("end_seconds must be greater than start_seconds") return end_seconds @field_validator("music_file") @classmethod def validate_play_source(cls, music_file: Optional[str], info): trigger_id = info.data.get("trigger_id") if not music_file and not trigger_id: raise ValueError("trigger_id or music_file must be provided") return music_file