| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778 |
- """
- Device model - WiFi/BLE receivers/scanners.
- """
- from datetime import datetime
- from sqlalchemy import DateTime, ForeignKey, Integer, String, text
- 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,
- server_default=text("nextval('device_simple_id_seq')"),
- )
- # 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)
- # Device credentials (for API access)
- device_token: Mapped[str | None] = mapped_column(String(512), unique=True)
- device_password: Mapped[str | None] = mapped_column(String(32))
- # 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"<Device {self.id}: {self.display_name} ({self.mac_address})>"
|