from datetime import datetime from pydantic import BaseModel, Field # --- Device schemas --- class DeviceRegisterRequest(BaseModel): device_id: str = Field(..., min_length=1, max_length=50) hostname: str = Field(..., min_length=1, max_length=100) ip_address: str = Field(..., min_length=1, max_length=45) firmware_version: str | None = None has_nfc: bool = True has_scale: bool = True tare_offset: int = 0 calibration_factor: float = 1.0 nfc_reader_type: str | None = None nfc_connection: str | None = None backend_url: str | None = None has_backlight: bool = False class DeviceResponse(BaseModel): id: int device_id: str hostname: str ip_address: str firmware_version: str | None = None has_nfc: bool has_scale: bool tare_offset: int calibration_factor: float nfc_reader_type: str | None = None nfc_connection: str | None = None backend_url: str | None = None display_brightness: int = 100 display_blank_timeout: int = 0 has_backlight: bool = False last_calibrated_at: datetime | None = None last_seen: datetime | None = None pending_command: str | None = None nfc_ok: bool scale_ok: bool uptime_s: int update_status: str | None = None update_message: str | None = None online: bool = False ssh_public_key: str | None = None created_at: datetime updated_at: datetime class Config: from_attributes = True class HeartbeatRequest(BaseModel): nfc_ok: bool = False scale_ok: bool = False uptime_s: int = 0 firmware_version: str | None = None ip_address: str | None = None nfc_reader_type: str | None = None nfc_connection: str | None = None backend_url: str | None = None class HeartbeatResponse(BaseModel): pending_command: str | None = None pending_write_payload: dict | None = None pending_system_payload: dict | None = None tare_offset: int calibration_factor: float display_brightness: int = 100 display_blank_timeout: int = 0 # --- NFC schemas --- class TagScannedRequest(BaseModel): device_id: str tag_uid: str tray_uuid: str | None = None sak: int | None = None tag_type: str | None = None raw_blocks: dict | None = None class TagRemovedRequest(BaseModel): device_id: str tag_uid: str # --- Scale schemas --- class ScaleReadingRequest(BaseModel): device_id: str weight_grams: float stable: bool = False raw_adc: int | None = None class UpdateSpoolWeightRequest(BaseModel): spool_id: int weight_grams: float # --- Calibration schemas --- class SetTareRequest(BaseModel): tare_offset: int class SetCalibrationFactorRequest(BaseModel): known_weight_grams: float = Field(..., gt=0) raw_adc: int tare_raw_adc: int | None = None class CalibrationResponse(BaseModel): tare_offset: int calibration_factor: float # --- Display schemas --- class WriteTagRequest(BaseModel): device_id: str spool_id: int class WriteTagResultRequest(BaseModel): device_id: str spool_id: int tag_uid: str success: bool message: str | None = None class DisplaySettingsRequest(BaseModel): brightness: int = Field(ge=0, le=100) blank_timeout: int = Field(ge=0) class SystemConfigRequest(BaseModel): backend_url: str = Field(..., min_length=1, max_length=255) api_key: str | None = Field(default=None, max_length=255) class SystemCommandResultRequest(BaseModel): command: str success: bool message: str | None = None # --- Diagnostics schemas --- class DiagnosticResultRequest(BaseModel): diagnostic: str # 'nfc', 'scale', or 'read_tag' success: bool output: str exit_code: int