Skip to main content

Device Registration & Claim

:::info Status Implemented. Source: docs/transmitter-mqtt-contract.md §10, docs/user-device-mqtt-contract.md §9. :::

Registration is REST, not MQTT — this page documents how a unit goes from "unprovisioned hardware" to "holds broker credentials and connects per the topic structure". The credential itself is what lets everything else in this protocol reference happen.

All flows end in the same terminal state: the device holds {device_id, mosque_id, broker credentials} in NVS and connects. Until provisioned, a device exposes a local setup mode (Wi-Fi AP + captive portal).

Transmitter: flows A/B/C — registration + claim

  • Supplying serial_number at registration makes this flow C; omitting it (flow A/B) mints a synthetic serial (SYNTH-TX-{uuid}) so every transmitter has a manifest row. Flow B's QR pairing is the same call — the panel renders the setup code as a QR instead of text.
  • The claim response is the broker credential — there is no separate secret-issuance step.
  • broadcast_secret is unrelated to the MQTT fields beside it — it authenticates the audio uplink WebSocket instead, and is returned once, only when this claim freshly minted it (a re-claim of an already-secreted unit gets null — use POST /{device_id}/regenerate-broadcast-secret if needed again).

Transmitter: flow D — auto-registration with approval

{"state": "unknown"} is a normal outcome, not an error. None of flow D is a mosque-admin action — it's Super-Admin-only end to end; the Admin Panel's "Unclaimed devices" card shows these read-only, telling a mosque admin to contact the platform team rather than letting them approve directly.

Stream player: flows A/C/D — mirrors the transmitter, with differences

Identical mechanics to the transmitter's flows A, C, D, with three differences (source: docs/user-device-mqtt-contract.md §9):

  1. The claim endpoint is POST /api/v1/user-devices/claim, binding user_id (the buying member) in addition to mosque_id.
  2. The setup AP is Azan360-RX-{last4 of MAC}.
  3. Claim does not grant streaming. The claim response returns broker credentials and device_id; the registry row lands PENDING awaiting Super Admin authorization. The device connects, publishes status, is visible to Super Admin — but stream_url is withheld and no play is sent until ACTIVE. Until then it publishes ONLINE and plays nothing over Azan360 (Bluetooth is unaffected — see Stream Player module).

Critically: the unit stays subscribed to config and cmd the entire time it is UNBOUND. Approval is a single retained-config publish onto that open session, taking effect in under a second. The unit must never poll for its authorization state after claim — no /announce loop, no periodic sync_config, no reconnect cycle to "check". The backend contract is that approval is instant.

A member who buys several units repeats this per unit — no bulk inheritance: unit two starts PENDING even if unit one is already authorized.

Rebinding is not a provisioning flow

Once claimed and approved, moving a stream player to another mosque is an ordinary config update: the owner picks a mosque they've joined, and the backend publishes a new config with the new mosque_id and stream_url. No setup code, no claim call, no credential change, no topic change, no reboot. See Device Lifecycle.

Admin lifecycle actions (support tooling)

None of these add new device-facing topics or payload fields — transmitter side, all on /api/v1/mosque-devices/{device_id}:

ActionEndpointEffect
Force-claimPOST /{id}/force-claimSuperadmin: marks registered but unclaimed as claimed, skipping setup-code/MAC verification
Regenerate setup codePOST /{id}/regenerate-setup-codeFresh one-time code, 24h expiry
Regenerate broadcast secretPOST /{id}/regenerate-broadcast-secretSuperadmin: rotates the audio-uplink secret
MaintenancePOST /{id}/maintenance {enabled, reason?}Pause/resume
Factory resetPOST /{id}/factory-resetWipes claim state; unit re-provisions
Identity editPATCH /{id} {serial_number?, mac_address?, mqtt_client_id?}Superadmin-only
Retire/deleteDELETE /{id}Soft-retires a claimed device; hard-deletes a never-claimed row

Full schemas: API Reference. Broader lifecycle model (identity/ownership/authorization/binding as separate concepts): see Concepts and Device Lifecycle.