Signage device API

1. Activate a signage device (public, one-time activation code)

The current Android signage device obtains an activation_code by scanning a QR code, then exchanges it for a long-lived device token through this endpoint.

POST /api/v1/signage/activate
Content-Type: application/json

Request body:

  • activation_code: one-time activation code.

  • classroom_id: classroom ID carried in the QR code; used for activation validation.

  • device_code: device code generated locally by Android and uploaded to the backend to identify the physical device. If the classroom already has a recorded device code, this value must match it.

  • device_name: optional device name.

  • app_version: optional signage app version.

  • install_id: optional device installation identifier.

Response:

{
  "code": 0,
  "message": "success",
  "data": {
    "token": "device_token_here",
    "device": {
      "id": 1,
      "device_code": "8d0e1cb5-6f74-4d95-b861-3a0c2af18413",
      "device_label": "5A203 Signage",
      "building": "5A",
      "room_number": "203",
      "capacity": 60,
      "status": "available",
      "signage_title": "5A203",
      "signage_subtitle": "Digital Signage",
      "signage_config": {
        "announcements": [
          {
            "label": "Notice",
            "text": "Please keep the classroom clean.",
            "type": "warn"
          }
        ]
      },
      "activated_at": "2026-04-08T10:00:00+08:00",
      "token_issued_at": "2026-04-08T10:00:00+08:00",
      "last_seen_at": "2026-04-08T10:05:00+08:00",
      "activation_code_issued_at": null,
      "activation_code_expires_at": null,
      "is_bound": true
    }
  }
}

Notes:

  • Activation codes are single-use and have an expiration time.

  • The backend stores only the token hash. The plaintext token is returned only once, after successful activation.

  • Devices must use Authorization: Bearer <token> to access signage APIs afterward.

2. Current signage device information (device authentication)

GET /api/v1/signage/device
Authorization: Bearer <device_token>

Returns the signage configuration for the classroom bound to the current token. The signage device does not need to pass classroom_id.

Response:

{
  "code": 0,
  "message": "success",
  "data": {
    "id": 1,
    "device_code": "SIG-ABC123456789",
    "device_label": "5A203 Signage",
    "building": "5A",
    "room_number": "203",
    "capacity": 60,
    "status": "available",
    "signage_title": "5A203",
    "signage_subtitle": "Digital Signage",
    "signage_config": {
      "announcements": [
        {
          "label": "Info",
          "text": "Device is connected.",
          "type": "info"
        }
      ]
    },
    "activated_at": "2026-04-08T10:00:00+08:00",
    "token_issued_at": "2026-04-08T10:00:00+08:00",
    "last_seen_at": "2026-04-08T10:05:00+08:00",
    "activation_code_issued_at": null,
    "activation_code_expires_at": null,
    "is_bound": true
  }
}

3. Current signage device schedule (device authentication)

GET /api/v1/signage/device/schedule
Authorization: Bearer <device_token>

Query:

  • start_date (YYYY-MM-DD)

  • end_date (YYYY-MM-DD)

Response:

{
  "code": 0,
  "message": "success",
  "data": {
    "items": [
      {
        "start_time": "2026-02-04T08:00:00+08:00",
        "end_time": "2026-02-04T09:40:00+08:00",
        "course_id": 12,
        "course_name": "Calculus",
        "course_code": "MATH101",
        "teacher_name": "John Smith",
        "class_name": "CS 2024",
        "sections": [1, 2],
        "source": "course_occurrence"
      }
    ]
  }
}

Notes:

  • The backend resolves SignageDevice.classroom_id from the device token.

  • The current signage device displays only schedules generated by the course system and does not merge manual borrowings.

4. Generate signage binding QR code data (admin)

POST /api/v1/signage/classrooms/{id}/activation-code
Authorization: Bearer <user_access_token>
Content-Type: application/json

Request body (optional):

  • expires_in_minutes: activation-code validity period in minutes. Default: 10 minutes.

Response:

{
  "code": 0,
  "message": "success",
  "data": {
    "activation_code": "ACT-ABC123456789",
    "expires_at": "2026-04-08T10:10:00+08:00",
    "qr_payload": {
      "base_url": "https://example.com/api/v1/",
      "classroom_id": 1,
      "activation_code": "ACT-ABC123456789"
    }
  }
}

Notes:

  • The current implementation generates a one-time activation_code. A long-lived device token is issued only after the signage device scans the code and activates successfully.

  • Android signage devices generate and persist device_code locally, then upload it to the backend during activation.

  • If a classroom has already been bound to a device and the backend has recorded device_code, later activations with a different uploaded device_code are rejected.

  • Android signage devices remain compatible with the old QR-code payload that directly binds a token, but the admin side generates the activation_code payload by default.

5. Admin-side signage configuration APIs (admin)

The following endpoints let the admin side view or control the signage device for a classroom:

GET   /api/v1/signage/classrooms/{id}
GET   /api/v1/signage/classrooms/{id}/schedule
PATCH /api/v1/signage/classrooms/{id}/config
POST  /api/v1/signage/classrooms/{id}/revoke-token

Example body for PATCH /api/v1/signage/classrooms/{id}/config:

{
  "device_label": "5A203 Signage",
  "signage_title": "5A203",
  "signage_subtitle": "Welcome to Smart Signage",
  "announcements": [
    {
      "label": "Notice",
      "text": "Please keep the classroom clean.",
      "type": "info"
    }
  ]
}

Important notes:

  • These are admin-side APIs, not public signage-device APIs.

  • Current Android signage devices should not directly call /signage/classrooms/{id} or its /schedule and /config endpoints.

  • After revoke-token is executed, the old token for the bound device becomes invalid immediately, and the device must scan a QR code again to bind.