Skip to main content

Azaan Broadcast

:::info Status Implemented. Source: docs/azan-flow.md, docs/live-broadcast-flow.md, docs/transmitter-audio-uplink-contract.md. :::

Delivers the azan (call to prayer) live from a microphone, or automatically from a recording when nobody broadcasts in time. See Data Flow for the end-to-end diagram; this page is the module-level detail.

Prerequisites (per mosque, one-time)

  1. Azan voices — Admin Panel → Azan Audio: upload/record a Fajr recording (audio_files.prayer = 'fajr') and a general recording (audio_files.prayer IS NULL). Multiple recordings per category are kept; the newest becomes is_default automatically, any older one can be re-promoted.
  2. Prayer times — a saved timetable (mosque_prayer_times) or mosque coordinates/city+pincode for the AlAdhan API fallback. See Prayer Timing module.
  3. Scheduler settings — Admin Panel → Auto Azan (azan_auto_settings): master on/off, grace/window minutes, per-prayer toggles.

Path A — Live azan

A transmitter can also be the source, over its own dedicated audio uplink WebSocket — same FFmpeg → Icecast pipeline, same fan-out function, no second implementation.

Path B — Offline azan (scheduler fallback)

core/azan_scheduler.py, started in the FastAPI lifespan, ticks every AZAN_AUTO_CHECK_INTERVAL_SECS (default 60s).

The Icecast file stream starts before notifications go out — members answering the CallKit/FCM call screen always pull the stream URL, so the mount must already be live.

Race handling — no double azan

ScenarioOutcome
Admin goes live within grace periodon-air flag set → scheduler marks slot handled, never fires
Admin was live, ended within windowaudit row found → no auto azan
Manual azaan trigger sent from apptriggers row → no auto azan
Auto azan fired, server restarts inside windowtrigger row survives in DB → not refired
Admin connects while auto file stream is playingmount flag already true → admin joins as extra broadcaster; watchdog only releases state it still owns
Two prayers fall inside the window after downtimeeach (mosque, date, prayer) slot evaluated independently

Configuration

Per mosque (azan_auto_settings, Admin Panel → Auto Azan)

FieldDefaultMeaning
enabledtruemaster switch
grace_minutes5wait for admin after adhan before falling back (0–60)
window_minutes20stop trying this long after adhan (1–120, must exceed grace)
{prayer}_enabledtrueper-prayer opt-out

Server-wide (env)

VarDefaultMeaning
AZAN_AUTO_ENABLED1global kill switch
AZAN_AUTO_CHECK_INTERVAL_SECS60scheduler tick
AZAN_AUTO_GRACE_MINUTES / AZAN_AUTO_WINDOW_MINUTES5 / 20defaults for mosques without saved settings
AZAN_AUTO_MAX_STREAM_SECS600hard cap on an auto file stream
AZAN_AUTO_STREAM_TAIL_SECS12trailing silence so lagging listeners hear the full azan
BROADCAST_IDLE_TIMEOUT_SECS60live broadcast auto-end on silence

Fan-out (both paths)

Broadcast typeiOSAndroidDevices (MQTT)
Azaan (live or offline)VoIP push → CallKit ringdata-only FCM → native call UIplay command to every bound stream player + transmitter
Non-azaan (e.g. announcement/khutbah)APNs alert (if configured), FCM fallbackFCM notificationnone — push only

Every fan-out step is individually try/excepted — an MQTT or push failure never blocks or ends the audio stream. See MQTT — commands for the exact play payload and Device Authorization for why a blocked/unbound device is excluded (and counted) rather than silently sent a URL.

EndpointPurpose
GET/PUT /api/v1/azan-auto/mosque/{id}/settingsscheduler settings
GET /api/v1/azan-auto/mosque/{id}/statustoday's times + source, powers the status banner
GET /api/v1/azan-auto/mosque/{id}/historyrecent auto-fired azans
GET/POST /api/v1/audio-files/mosque/{id}[/upload]azan recordings
DELETE /api/v1/audio-files/{audio_id}delete a recording

Full schemas: API Reference.

Troubleshooting

SymptomLikely cause
Auto azan never firesmosque inactive; toggles off; no prayer times/location (status source: "none"); no recording uploaded
Fired, but phones silent on answerIcecast mount not live — check FFmpeg file-stream errors
Recording plays for wrong prayercheck audio_files.prayer'fajr' vs NULL
Auto azan double-firedboth DB dedupe checks failed — audit tables missing?
Upload 413nginx client_max_body_size missing on /api/

More operational detail: Operations — Troubleshooting.