A lightweight, kiosk-based display system for showing Islamic prayer times, weather forecasts, and weather alerts.
| Prayer Times | Weather | Weather Alert |
|---|---|---|
![]() |
![]() |
![]() |
This project is specifically designed for Proxmox LXC containers (Debian 13) with proper hardware acceleration and audio forwarding. It uses a modern Wayland stack to give you a solid, embedded display system that just works.
At its core, it combines three layers: a Python backend serving static files, a responsive web frontend, and a Wayland-based kiosk display.
- Prayer Times: Accurate calculations using the adhan-js library with support for various calculation methods (ISNA, MWL, etc.) and Madhabs.
- Visual Alerts: Highlights the current prayer and provides a countdown to the next one.
- Audio Notifications: Plays Adhan audio files at prayer times.
- Weather Integration: Displays current conditions and hourly forecasts using OpenWeatherMap, plus National Weather Service (US) weather alerts.
- Kiosk Mode: Auto-starts in full-screen mode, with optional remote VNC access for troubleshooting.
- Containerized Architecture: Designed to run efficiently in an LXC container while leveraging the host's hardware resources.
Here's the tricky part: you can't just run a graphical app in a container and have it access your host's GPU and audio. You need a specific architecture to bridge the gap between the container and the host's hardware.
- Seatd (Host): A seat management daemon that acts as a gatekeeper on your Proxmox host. It manages who gets access to hardware like the GPU (
/dev/dri) and input devices. - Cage (Container): A lightweight Wayland compositor running inside your LXC. It connects to the host's
seatdsocket to ask for permission to render directly to the screen. - Chromium (Container): Runs in kiosk mode on top of Cage, displaying your dashboard.
- PipeWire (Host): Runs as a system service on your Proxmox host, managing the actual audio hardware.
- Socket Forwarding: You'll mount the host's PipeWire socket into the container (bind-mount).
- Client (Container): Chromium inside the container connects to this mounted socket, which essentially "tunnels" audio out to your host's speakers.
- Python Server: A lightweight wrapper around Python's built-in
http.server. It serves the static files and gives the frontend an API to discover audio files on the fly.
- WayVNC: A VNC server that connects to the Cage session, so you can view the kiosk screen remotely on port
5900. This feature is only for debugging since my development machine and the azan dashboard are in different rooms and it helped save time and walks to the other room during development and testing.
I'm assuming you've got Proxmox VE running on your host and you'll be setting this up in a Debian 13 LXC container.
Before setting up the container, the host must be configured to share hardware access.
- Install seatd:
apt update && apt install -y seatd - Configure it to use the
videogroup:Add or modify these lines:systemctl edit seatd.service
[Service] ExecStart= ExecStart=seatd -g video
- Restart seatd:
systemctl daemon-reload systemctl restart seatd
- Install the PipeWire packages:
apt install pipewire pipewire-pulse wireplumber
- Create a dedicated PipeWire user:
useradd -r -s /usr/sbin/nologin -G audio,video pipewire
- Create the systemd service file:
Add this content:
nano /etc/systemd/system/pipewire.service
[Unit] Description=PipeWire System Service After=network.target [Service] Type=simple User=pipewire Group=pipewire RuntimeDirectory=pipewire Environment=XDG_RUNTIME_DIR=/run/pipewire ExecStart=/usr/bin/pipewire ExecStartPost=/bin/bash -c "until [ -S /run/pipewire/pipewire-0 ]; do sleep 0.1; done; chmod 666 /run/pipewire/pipewire-0" Restart=always [Install] WantedBy=multi-user.target
- Enable and start:
systemctl daemon-reload systemctl enable --now pipewire
- Edit
/etc/subgidto allow your container to map the video (44) and audio (29) groups:Add:nano /etc/subgid
root:29:1 root:44:1 - Edit your LXC config file (e.g.,
/etc/pve/lxc/100.conf):Add the following to map groups and mount the sockets:nano /etc/pve/lxc/100.conf
# ID Mappings for Audio/Video access lxc.idmap: u 0 100000 65536 lxc.idmap: g 0 100000 29 lxc.idmap: g 29 29 1 lxc.idmap: g 30 100030 14 lxc.idmap: g 44 44 1 lxc.idmap: g 45 100045 65491 # Mount Seatd Socket lxc.mount.entry: /run/seatd.sock opt/seatd.sock none bind,create=file 0 0 # Mount PipeWire Socket lxc.mount.entry: /run/pipewire/pipewire-0 opt/pipewire-0 none bind,create=file 0 0
- Restart the LXC container — without this, the socket mounts won't work.
Now, log into your Debian LXC container and follow these steps:
-
Clone or Copy the Project: Move the
azandirectory into/opt/azan. -
Run the Installer: The
install.shscript takes care of installing dependencies, creating the right user, and setting up services.cd /opt/azan chmod +x install.sh ./install.sh -
Start Services: The installer creates three systemd services:
azan-dashboard.service: Python web serverazan-kiosk.service: Your graphical display (Cage + Chromium)azan-vnc.service: Remote VNC access
The installer does not start them automatically, you can fire them up manually:
systemctl start azan-dashboard systemctl start azan-kiosk systemctl start azan-vnc
The vnc service is not enabled by default as it has a memory leak or something that makes it take up all the assigned RAM until the LXC becomes unresponsive. Use it for debugging when needed, but disable or uninstall it once done.
All the settings are in static/config.js.
You'll need to set your GPS coordinates so the dashboard can calculate prayer times for your exact location. You can also choose your calculation method (ISNA, Muslim World League, Egyptian, etc.) and how Asr time is calculated. For weather, you'll need an OpenWeatherMap API key — register for a free account, generate an API key, and paste it in the config.js.
Here's how to edit your settings:
-
Open the config file:
nano /opt/azan/static/config.js
-
Update these sections:
- Geographic Location:
latitude: 36.9981184, longitude: -87.1440034, elevation: 6.46, weatherApiKey: 'your_openweathermap_api_key_here'
- Calculation Method:
Set
calculationMethodto NorthAmerica, MuslimWorldLeague, Egyptian, etc. - Audio: Turn specific prayers on or off, or point to different audio folders.
- Weather:
Paste your OpenWeatherMap API key into
weatherApiKey.
- Geographic Location:
-
Apply Changes: Restart the kiosk service to pick up your changes:
systemctl restart azan-kiosk
No audio files are included in this repo, and you'll need to provide your own. I've tested with .mp3 files, but since the dashboard runs in Chromium, most audio formats should work. Download azan recordings from public sources or record your own. Put Fajr azan in the static/audio/fajr folder and everything else in static/audio/others. You can add multiple files to each folder; the dashboard will pick one at random for each prayer.
If your audio files are noisy, use the denoise-audio.sh script to clean them up and make other adjustments. Check the configuration section at the top of that script to see what you can tweak.
Use cron to turn your screen off at night and back on in the morning. This can help with saving power at night and extending screen life.
-
Open your crontab:
crontab -e
-
Add these lines to turn the screen off at 11:00 PM and on at 4:00 AM:
# Turn the screen off at night 0 23 * * * /opt/azan/screen.sh off # Turn the screen back on in the morning 0 4 * * * /opt/azan/screen.sh on
If you run into issues, check that:
- The LXC container's been restarted after config changes
seatdis running on the host (systemctl status seatd)- PipeWire is running (
systemctl status pipewire) - Your audio and video groups are mapped correctly in the LXC config
Feel free to open an issue if you get stuck!
Developed with partial assistance from AI tools


