انتقل إلى المحتوى الرئيسي

REST API Reference

Base URL: https://<device-ip>/api. Requests and responses are JSON. Mutating endpoints require Authorization: Bearer <token> (the token model is explained in REST design). Errors use HTTP status codes plus {"error":"human readable"}.

Legend: Auth = requires the bearer token. Limit = rate limit class beyond the default.

Playback

MethodPathAuthPurpose
GET/playback/statusnoCurrent playback snapshot
POST/playback/playyesPlay a file or a stream
POST/playback/pauseyesPause
POST/playback/resumeyesResume from pause
POST/playback/stopyesStop
POST/playback/seekyesSeek within the current file
POST/playback/volumeyesSet volume
POST/playback/resume-savedyesResume the persisted position
POST/playback/nextyesPlay the next available surah of the current reciter
POST/playback/prevyesPlay the previous available surah
GET/playback/modenoCurrent advance mode (plus hifz config when active)
POST/playback/modeyesSet the advance mode
GET /api/playback/status
{
"state": "playing",
"mode": "file",
"position": 128470,
"duration": 431000,
"volume": 50,
"underrun_count": 0,
"track": "/sdcard/Al-Husary/001_Al-Fatihah.mp3",
"reciter": "Al-Husary",
"surah_number": 1,
"advance_mode": "continuous",
"hasResumeState": false
}

state: playing, paused, buffering, stopped. mode: file, stream, idle. position/duration in milliseconds; live streams report duration 0. underrun_count is the audio health counter. track/reciter/surah_number describe the current source; advance_mode is one of off, continuous, repeat_one, hifz_repeat, hifz_ab. In the hifz modes the response gains a hifz object with repeat_done, repeat_target, pause_s, waiting and, for hifz_ab, ab_start/ab_end.

POST /api/playback/mode
{"mode": "continuous"} // or "off", "repeat_one"
{"mode": "hifz_repeat", "repeat": 3, "pause_s": 5} // repeat current surah
{"mode": "hifz_ab", "repeat": 3, "ab": {"from": 78, "to": 114}, "pause_s": 5}

repeat 0 means endless (hifz_repeat only). pause_s (0-600) inserts silence between repetitions; the display keeps the now-playing screen during the gap. Missing files inside an A-B range are skipped and the range wraps from to back to from.

POST /api/playback/play
{"path": "/sdcard/Al-Husary/001.mp3"} // file form
{"url": "https://qurango.net/radio/tarateel"} // stream form
POST /api/playback/seek
{"position": 60000}

Seek is rejected with 400 for live streams. POST /playback/volume takes {"volume": 0..100} and answers with the applied value.

Library

MethodPathAuthPurpose
GET/library/recitersnoScanned reciter list
GET/library/surahs?reciter=NAMEnoThe 114-slot availability map plus names for one reciter

Favorites

MethodPathAuthPurpose
GET/favoritesnoAll favorites with play counts
POST/favoritesyesAdd ({"type":"surah","reciter":...,"surah":n} or {"type":"stream","url":...,"name":...})
DELETE/favorites/{id}yesRemove
POST/favorites/toggleyesAdd or remove in one call, body as in add

Radio stations

MethodPathAuthPurpose
GET/radio/stationsnoSaved stations
POST/radio/stationsyesAdd {"name":..., "url":...} (URL validated, see streaming)
DELETE/radio/stations/{id}yesRemove

NFC cards

MethodPathAuthPurpose
GET/rfid/cardsyesRegistered cards
POST/rfid/cardsyesRegister {"uid":..., "name":..., binding}
DELETE/rfid/cards/{uid}yesDelete
GET/rfid/scanyesLast tapped UID, for the registration form's scan button. Returns {"uid":..., "uid_len":n, "registered":bool, "age_ms":n}, or {"uid":"", "age_ms":-1} when nothing has been tapped yet. A tap also arrives live over the WebSocket nfc message; this endpoint is the polling fallback

Prayer times

MethodPathAuthPurpose
GET/prayernoToday's times, next prayer with countdown, Hijri date, config echo
POST/prayer/configyesLocation, method, Asr school, offsets, adhan settings
POST/prayer/test-adhanyesPlay the configured adhan now
POST /api/prayer/config
{
"lat": 30.0444, "lng": 31.2357,
"method": 2, "asr_hanafi": false,
"offsets_min": [0, 0, 0, 0, 0, 0],
"adhan": {"enabled": true, "prayer_mask": 61, "volume": 80,
"path": "/sdcard/adhan.mp3"}
}

method: 0 MWL, 1 ISNA, 2 Egyptian, 3 Umm al-Qura, 4 Karachi. offsets_min adjusts Fajr, Sunrise, Dhuhr, Asr, Maghrib, Isha by minutes. prayer_mask is a bitmask over the same order (bit 1, Sunrise, is ignored); volume 0 keeps the current volume during the adhan. Times are computed offline from the solar position, so no network is needed after the location is set; the Hijri date is tabular (about one day of tolerance versus sighting-based announcements).

Scheduler

MethodPathAuthPurpose
GET/schedulesnoAll schedules
POST/schedulesyesCreate {"hour":..,"minute":..,"days":mask,"target":...,"volume":optional}
DELETE/schedules/{id}yesDelete
POST/schedules/{id}/enableyes{"enabled": bool}

Settings and system

MethodPathAuthPurpose
GET/settingsnoAll settings, credentials redacted, plus current time, time_synced, time_source (none / rtc / sntp / manual), time_format_24h and rtc_temperature when a DS3231 is fitted
POST/settings/wifiyesSet {"ssid":..., "password":...}; device reconnects
POST/settings/brightnessyes{"brightness": 0..255}
POST/settings/timezoneyes{"timezone": "POSIX rule"} (validated printable ASCII; the dashboard offers a preset list plus a custom entry)
POST/settings/time-formatyes{"time_format_24h": bool}; picks 12-hour (default) or 24-hour clock, applied to the display immediately
POST/settings/timeyes{"epoch": unix seconds}; sets the system clock and the RTC, for offline installs
POST/settings/auto-resumeyes{"enabled": bool}
POST/settings/auto-offyes{"auto_off_minutes": 0..240}; deep sleep after idle, 0 disables. Playback, hifz gaps, drills, OTA and connected dashboard clients keep the device awake
POST/settings/card-removalyes{"policy": "continue" / "pause" / "stop"}
GET/systemnoVersion, uptime, free heap, NVS stats, boot_failures array
GET/batteryno{"present":..., "voltage":..., "percentage":..., "charging":...}; present false means no battery is attached (floating or unwired sense) and the other fields read zero
POST/system/sleepyesDeep sleep now; wakes on the button or, when the RTC is fitted, at the next enabled schedule
POST/resetyesFactory reset
GET /api/system
{
"version": "2.1.0",
"uptime": 15,
"free_heap": 7772024,
"boot_failures": [
{"component": "SD", "error": "ESP_ERR_TIMEOUT"}
]
}

OTA

MethodPathAuthLimitPurpose
GET/ota/statusno{"in_progress":.., "progress":0-100, "mode":"pull" / "upload" / "idle", "status":..., "error":...}
POST/ota/uploadyes2/minBody is the raw Safi.bin
POST/ota/pullyes2/min{"url": "https://..."}, subject to OTA hardening

Debug

MethodPathAuthLimitPurpose
GET/debug/screenshotyes10/10 sThe live screen as a 24-bit BMP (design)

Non-API routes

GET /ws upgrades to the WebSocket; GET /* serves the dashboard from LittleFS with API routes taking precedence.

Status code summary

CodeMeaning here
200Success; body is the result object
400Malformed body or invalid value; error says which field
401Missing or wrong bearer token
404Unknown entity (favorite id, card uid, station id)
429Rate limit exceeded for that endpoint class
500Device-side failure; the serial log has the detail