# `signage` Signage Devices Author: Zhenyu Yang Last updated: Apr 24, 2026 Corresponding API docs: [Signage Device API](../../api/signage.md) ## `signage` Code Entry - `backend/apps/signage/models/__init__.py` - `backend/apps/signage/views/__init__.py` - `backend/apps/signage/serializers/__init__.py` - `backend/apps/signage/authentication.py` - `backend/apps/signage/tokens.py` ## `signage` Core Data ### `SignageDevice` - One-to-one with `Classroom`. - Key fields: - `device_code` - `device_label` - `signage_title` - `signage_subtitle` - `signage_config` - `device_token_hash` - `activation_code_hash` - `token_issued_at` - `activated_at` - `last_seen_at` The backend only stores hashes of the activation code and device token; plaintext values are never persisted. ## Two Interface Surfaces ### Device Side - `POST /signage/activate` - `GET /signage/device` - `GET /signage/device/schedule` The device side exclusively uses `Authorization: Bearer `; it does not use user JWTs. ### Admin Side - `GET /signage/classrooms/` - `GET /signage/classrooms//schedule` - `PATCH /signage/classrooms//config` - `POST /signage/classrooms//activation-code` - `POST /signage/classrooms//revoke-token` The admin side requires standard user authentication, and the current implementation only allows `superadmin`. ## Activation Flow 1. The admin side issues a one-time `activation_code` for a classroom. 2. The Android signage device scans the QR code to obtain the `classroom_id` and `activation_code`. 3. The device calls `POST /signage/activate`. 4. The backend validates: - The activation code exists and has not expired. - The classroom matches. - If the device already has a bound `device_code`, it must match. - The `device_code` must not conflict with another classroom's device. 5. On success, a long-lived device token is issued and the one-time activation code is cleared. ## Schedule Source - Signage schedules read only from `CourseOccurrence`. - Manual reservations are not merged. - The returned structure includes course name, course number, teacher name, class, class period, and time range. ## Configuration - Configurable fields: - `device_label` - `signage_title` - `signage_subtitle` - `announcements` - `announcements` are written to `signage_config["announcements"]`. ## Current Boundaries - Signage already has independent device authentication and an activation lifecycle. - Currently, signage is a display-only terminal; it does not write check-in records or call `face-service`.