""" Device models — SIP phones, softphones, cell phones. Devices register with the gateway and can receive transferred calls. """ from datetime import datetime from enum import Enum from typing import Optional from pydantic import BaseModel, Field class DeviceType(str, Enum): """Types of devices that can connect to the gateway.""" SIP_PHONE = "sip_phone" # Hardware SIP phone SOFTPHONE = "softphone" # Software SIP client CELL = "cell" # Cell phone (reached via PSTN trunk) TABLET = "tablet" # Tablet with SIP client WEBRTC = "webrtc" # Browser-based WebRTC client class DeviceBase(BaseModel): """Shared device fields.""" name: str # "Office SIP Phone" type: DeviceType extension: Optional[int] = None # 221-299, auto-assigned if omitted sip_uri: Optional[str] = None # sip:robert@gateway.helu.ca phone_number: Optional[str] = None # For PSTN devices (E.164) priority: int = 10 # Routing priority (lower = higher priority) capabilities: list[str] = Field(default_factory=lambda: ["voice"]) class Device(DeviceBase): """Full device model.""" id: str is_online: bool = False last_seen: Optional[datetime] = None created_at: Optional[datetime] = None updated_at: Optional[datetime] = None @property def can_receive_call(self) -> bool: """Can this device receive a call right now?""" if self.type in (DeviceType.SIP_PHONE, DeviceType.SOFTPHONE, DeviceType.WEBRTC): return self.is_online and self.sip_uri is not None if self.type == DeviceType.CELL: return self.phone_number is not None return False class DeviceCreate(DeviceBase): """Request model for registering a new device.""" pass class DeviceUpdate(BaseModel): """Request model for updating a device.""" name: Optional[str] = None type: Optional[DeviceType] = None extension: Optional[int] = None sip_uri: Optional[str] = None phone_number: Optional[str] = None priority: Optional[int] = None capabilities: Optional[list[str]] = None class DeviceStatus(BaseModel): """Lightweight device status for list views.""" id: str name: str type: DeviceType is_online: bool last_seen: Optional[datetime] = None can_receive_call: bool