Skip to main content

The Web Dashboard

Safi serves its own control panel over HTTPS. There is nothing to install: any browser on the same network works, phone or desktop. The dashboard carries the same emerald and gold identity as this site, follows your system's light or dark preference (with a manual toggle), and is fully bilingual: the language button in the header switches between English and Arabic with proper right-to-left layout.

Connecting

  1. Browse to https://safi.local. The device announces itself over mDNS, so on most home networks the name just works. If your network blocks mDNS, read the IP address from the device's WiFi screen (or your router's client list) and browse to https://<ip> instead.
  2. Accept the certificate warning. The device generates a self-signed certificate, so the browser cannot know it in advance. This is expected for local devices; the connection is still encrypted. Installing a certificate your browser trusts is possible and covered in the developer HTTPS chapter.

You can also install the dashboard like an app: browsers offer "Add to Home Screen" for it, which gives a full-screen icon on a phone.

The API token

The dashboard needs a token before it will control the device. Reading information (what is playing, the battery level, the library) is open, but every action that changes something (play a station, set the volume, edit settings, start an update) requires the device's API token. Without it those actions come back as Request failed: unauthorized.

You do not have to hunt for where to enter it. The first time you open the dashboard, a prompt appears asking for the token, and it reopens on its own only when the stored token is rejected with the token-changed reply described below. Paste the token there and press Save and connect. It is stored in your browser and attached to every request from then on, so you only do this once per browser. You can also view or change it later under Settings, Access.

If the device's token ever changes (after a factory reset, a re-flash, or a re-provisioning), every protected action is refused with the device's "Invalid or expired token" message. The dashboard recognizes that specific reply, opens the prompt again with a token-changed notice, and marks the connection status dot red; paste the current token and press Save and connect. The dashboard also strips any ?token= from the address bar on load and never writes the token into the URL, so the token stays out of the browser history.

How to get the token

The device creates a random token the very first time it boots and prints it once on its USB serial console. To read it:

  1. Connect the device to your computer with the USB cable (the COM port).
  2. Open a serial monitor on that port at 115200 baud. Any serial terminal works: idf.py -p PORT monitor if you flashed with ESP-IDF, or the Arduino IDE Serial Monitor, PuTTY, screen, or minicom.
  3. Reset the device (the reset button, or unplug and replug) and watch the first lines it prints. On a first boot you will see a block like this:
W (612) API_AUTH: ============================================
W (613) API_AUTH: NEW API TOKEN GENERATED (save this!):
W (613) API_AUTH: 3f121aae376ab54849b6bb22c0f8df30d571597a3852b630edde96f19fa8f12f
W (614) API_AUTH: ============================================

The long line between the separators is your token: 64 hexadecimal characters. Copy it and paste it into the dashboard prompt.

The token can also be read on demand, at any time, not only at first boot. Open a serial terminal on the device's console port and type token followed by Enter: the device prints the current token.

You can also read the token off the device itself. While the WiFi status screen is up, press and hold the encoder button for about 1.5 seconds; the token appears on screen for 5 seconds and then hides again. The screen returns to the idle clock on its own after 10 seconds, so start the hold while it is still showing.

The token is printed only when it is first created

On every later boot the device just logs API token loaded from NVS and does not reprint the token, on purpose, so it never travels where it could be captured. Recovering a lost token never requires a reset: the token console command prints the current one on demand, and the TFT hold-reveal shows it while the WiFi status screen is up. If you want a brand new token instead, clear the device's stored settings so it generates a fresh one on the next boot: parttool.py -p PORT erase_partition --partition-name=nvs, then reset the device and read the new token as above. This also clears saved WiFi credentials, so you will re-provision WiFi. The reasoning behind never exposing the token over the network is in REST security.

The eight pages

Home

Playback controls, a progress bar, the volume slider, and live status: what is playing, elapsed time, and the stream title on radio. The device panel shows battery, free memory, uptime, firmware version, the audio underrun counter (should stay at zero on a healthy network), and the RTC temperature when the clock module is fitted. Updates arrive over a WebSocket, so several open browsers all stay in sync with the device and each other.

Library

The scanned SD card content: reciters on one side, their files on the other. Files named with surah numbers show proper surah names, Arabic and transliteration, instead of raw filenames. Click any row to play.

Favorites

Everything you saved, with play counts. The form on the side adds a favorite by surah, stream, or file path, with a real surah picker.

Radio

Preset Quran radio stations plus your own saved streams. Supported streams are MP3, AAC, and HE-AAC over HTTP or HTTPS. The dashboard ships with a handful of built-in presets; the custom streams you add are saved on the device, so every browser and NFC card sees the same list.

Cards (NFC)

Card registration and management, described fully in NFC cards. The surah field is a picker with all 114 names.

Scheduler

Time-based playback rules, described in favorites and scheduler.

Settings

Volume, display brightness, the timezone (from a preset list or a custom POSIX rule), the 12-hour or 24-hour clock format, the device clock (source, and a one-tap "use browser time" for offline installs), auto-resume, the NFC card removal policy, WiFi credentials, the API token field, and a Sleep button that puts the device into deep sleep until the button is pressed or the next schedule is due. Each setting is documented in settings.

Update (OTA)

Firmware updates by file upload or by URL pull, with live progress. See updating.

Live updates

The dashboard listens on a WebSocket for pushes from the device: playback state, volume, battery, WiFi changes, NFC taps, stream titles, buffering warnings, and update progress. The pill in the header shows the connection state together with a colored dot: gold while connecting, green once the socket is authenticated, and red when it is unauthorized (the token no longer matches) or closed. An unauthorized socket does not reconnect on its own until a working token is saved; otherwise, if the connection drops, the page reconnects automatically with backoff, and a manual refresh always resynchronizes everything. Failures of any action surface as a toast in the corner rather than disappearing silently.

Multiple viewers

Any number of browsers can be open at once. State changes made anywhere (including on the device itself, for example turning the volume knob) propagate to all of them within a fraction of a second.