""" Device model - WiFi/BLE receivers/scanners. """ from datetime import datetime from sqlalchemy import DateTime, ForeignKey, Integer, String from sqlalchemy.dialects.postgresql import INET, JSONB from sqlalchemy.orm import Mapped, mapped_column, relationship from app.models.base import Base class Device(Base): """ Device model - WiFi/BLE receivers/scanners. Uses simple_id for customer support (Receiver #1, #2, #3...) instead of MAC addresses. """ __tablename__ = "devices" id: Mapped[int] = mapped_column(primary_key=True) # Simple ID for customer support (auto-increment, never reused) simple_id: Mapped[int] = mapped_column(Integer, unique=True, nullable=False) # Hardware identifiers mac_address: Mapped[str] = mapped_column(String(17), unique=True, nullable=False) serial_number: Mapped[str | None] = mapped_column(String(100)) # Device info device_type: Mapped[str] = mapped_column( String(50), default="combo", nullable=False ) # wifi_scanner, ble_receiver, combo model: Mapped[str | None] = mapped_column(String(50)) firmware_version: Mapped[str | None] = mapped_column(String(50)) # Organization binding (NULL = unassigned) organization_id: Mapped[int | None] = mapped_column( ForeignKey("organizations.id", ondelete="SET NULL") ) # Status: offline, online, error status: Mapped[str] = mapped_column( String(20), default="offline", nullable=False ) last_seen_at: Mapped[datetime | None] = mapped_column(DateTime(timezone=True)) last_ip: Mapped[str | None] = mapped_column(INET) # Config (flexible JSON for different device types) config: Mapped[dict] = mapped_column(JSONB, default={}, nullable=False) # Notes notes: Mapped[str | None] = mapped_column(String) # Relationships organization: Mapped["Organization | None"] = relationship( "Organization", back_populates="devices" ) @property def display_name(self) -> str: """Display name: Receiver #5""" return f"Receiver #{self.simple_id}" def __repr__(self) -> str: return f""