filoor c3342496dd feat(app): verbeter parallelle data-ophaling en error handling
Het aantal workers voor de ThreadPoolExecutor is verhoogd van 3 naar 5 om de uitvoering te verbeteren. Nieuwe data-ophaalfuncties zijn toegevoegd, inclusief error handling met fallback-opties. De index methode gebruikt nu fetch_data_parallel voor efficiëntere API-calls en fouttolerantie. Dit verbetert de algehele responsiviteit en stabiliteit van de applicatie.
2025-05-29 21:40:20 +02:00

879 lines
33 KiB
Python

from flask import Flask, render_template, request, redirect, send_from_directory, jsonify, request as flask_request
import requests, json, os, random, subprocess
from datetime import datetime, timedelta
from config import *
from hijridate import Gregorian
from functools import lru_cache
import time
from concurrent.futures import ThreadPoolExecutor, as_completed
app = Flask(__name__)
# Cache configuratie
CACHE_DURATION = 300 # 5 minuten cache voor API calls
last_api_call = {}
cached_data = {}
executor = ThreadPoolExecutor(max_workers=5) # Verhoogd naar 5 workers voor betere parallelle uitvoering
def get_cached_data(key, fetch_func, duration=CACHE_DURATION):
"""Haal data uit cache of voer fetch_func uit als cache verlopen is"""
current_time = time.time()
if key in cached_data and key in last_api_call:
if current_time - last_api_call[key] < duration:
return cached_data[key]
data = fetch_func()
cached_data[key] = data
last_api_call[key] = current_time
return data
def fetch_data_parallel():
"""Haal alle data parallel op met verbeterde error handling"""
futures = {
'weather': executor.submit(fetch_weather_data),
'sonos': executor.submit(fetch_sonos_zones),
'date': executor.submit(get_date_info),
'hadith': executor.submit(load_hadith),
'prayer_times': executor.submit(fetch_prayer_times)
}
results = {}
for key, future in futures.items():
try:
results[key] = future.result(timeout=5) # Timeout van 5 seconden per request
except Exception as e:
print(f"⚠️ Fout bij ophalen {key} data: {e}")
results[key] = get_fallback_data(key)
return results
def get_fallback_data(key):
"""Geef fallback data terug als API calls falen"""
fallbacks = {
'weather': {
'temperature': '--',
'feels_like': '--',
'description': 'Weer niet beschikbaar',
'humidity': '--',
'wind_speed': '--',
'icon': '01d'
},
'sonos': ['Woonkamer'],
'date': get_date_info(), # Gebruik lokale functie als fallback
'hadith': {
"text": "De daden die Allah het meest liefheeft zijn degenen die regelmatig worden verricht, zelfs als ze klein zijn.",
"bron": "Sahih al-Bukhari"
},
'prayer_times': {
"Fajr": "06:00",
"Zuhr": "12:30",
"Asr": "15:00",
"Maghrib": "17:30",
"Isha": "19:00"
}
}
return fallbacks.get(key, {})
@lru_cache(maxsize=1)
def fetch_prayer_times():
"""Haal gebedstijden op met caching"""
try:
res = requests.get(VUMG_API, timeout=5)
res.raise_for_status()
data = res.json()[0]
return {
"Fajr": data.get("fajr_jamah", "00:00")[:5],
"Zuhr": data.get("zuhr_jamah", "00:00")[:5],
"Asr": data.get("asr_jamah", "00:00")[:5],
"Maghrib": data.get("maghrib_jamah", "00:00")[:5],
"Isha": data.get("isha_jamah", "00:00")[:5]
}
except Exception as e:
print(f"⚠️ Fout bij ophalen gebedstijden: {e}")
return get_fallback_data('prayer_times')
# Voeg cache-control headers toe voor statische bestanden
@app.after_request
def add_header(response):
if 'Cache-Control' not in response.headers:
response.headers['Cache-Control'] = 'no-store, no-cache, must-revalidate, max-age=0'
return response
# Specifieke route voor statische bestanden met cache-control
@app.route('/static/<path:filename>')
def serve_static(filename):
return send_from_directory('static', filename, cache_timeout=0)
SETTINGS_PATH = os.path.join(os.path.dirname(os.path.abspath(__file__)), 'settings.json')
HADITHS_PATH = os.path.join(os.path.dirname(os.path.abspath(__file__)), 'static', 'hadiths.json')
VUMG_API = 'https://www.vumg.nl/?rest_route=/dpt/v1/prayertime&filter=today'
CLIPS_PATH = os.path.join(os.path.dirname(os.path.abspath(__file__)), 'static', 'clips')
# SONOS_API_IP wordt nu geïmporteerd uit config.py
# Globale variabelen
debug_time_offset = 0 # Offset in seconden voor debug mode
debug_mode_active = False
@lru_cache(maxsize=1)
def load_settings():
try:
if os.path.exists(SETTINGS_PATH) and os.path.getsize(SETTINGS_PATH) > 0:
with open(SETTINGS_PATH) as f:
return json.load(f)
except Exception as e:
print(f"⚠️ Fout bij laden van settings: {e}")
return {"volume": 30, "zones": ["Woonkamer"], "audio_clip": "adhan1.mp3"}
def fetch_weather_data():
"""Haalt weersinformatie op voor de geconfigureerde locatie"""
return get_cached_data('weather', lambda: _fetch_weather_data_impl())
def _fetch_weather_data_impl():
try:
params = {
'q': WEATHER_LOCATION,
'appid': OPENWEATHER_API_KEY,
'units': 'metric',
'lang': 'nl'
}
response = requests.get('https://api.openweathermap.org/data/2.5/weather', params=params, timeout=5)
response.raise_for_status()
data = response.json()
weather_info = {
'temperature': round(data['main']['temp']),
'feels_like': round(data['main']['feels_like']),
'description': data['weather'][0]['description'].capitalize(),
'humidity': data['main']['humidity'],
'wind_speed': round(data['wind']['speed'] * 3.6), # m/s naar km/h
'icon': data['weather'][0]['icon']
}
return weather_info
except Exception as e:
print(f"⚠️ Fout bij ophalen weerdata: {e}")
return {
'temperature': '--',
'feels_like': '--',
'description': 'Weer niet beschikbaar',
'humidity': '--',
'wind_speed': '--',
'icon': '01d'
}
def load_hadith():
try:
with open(HADITHS_PATH) as f:
hadiths = json.load(f)['hadiths']
return random.choice(hadiths)
except Exception as e:
print(f"⚠️ Fout bij laden van hadith: {e}")
return {"text": "De daden die Allah het meest liefheeft zijn degenen die regelmatig worden verricht, zelfs als ze klein zijn.", "bron": "Sahih al-Bukhari"}
def get_current_volume(settings):
"""Bepaal het juiste volume op basis van de huidige tijd"""
from datetime import datetime, timedelta
# Fallback naar oude volume als nieuwe instellingen niet bestaan
if 'volume_day' not in settings or 'volume_night' not in settings:
return settings.get('volume', 30)
# Gebruik debug tijd als actief
global debug_mode_active, debug_time_offset
if debug_mode_active:
current_time = (datetime.now() + timedelta(seconds=debug_time_offset)).strftime('%H:%M')
print(f"🔧 Volume bepaling gebruikt debug tijd: {current_time}")
else:
current_time = datetime.now().strftime('%H:%M')
night_start = settings.get('night_start', '20:00')
day_start = settings.get('day_start', '07:00')
# Converteer tijden naar vergelijkbare format
current_minutes = int(current_time[:2]) * 60 + int(current_time[3:5])
night_minutes = int(night_start[:2]) * 60 + int(night_start[3:5])
day_minutes = int(day_start[:2]) * 60 + int(day_start[3:5])
# Bepaal of het avond is
if night_minutes > day_minutes: # Normale dag/nacht cyclus (bijv. dag 07:00-20:00)
is_night = current_minutes >= night_minutes or current_minutes < day_minutes
else: # Avond gaat over middernacht (bijv. avond 20:00-07:00)
is_night = current_minutes >= night_minutes and current_minutes < day_minutes
if is_night:
volume = settings.get('volume_night', settings.get('volume', 30) // 2)
print(f"🌙 Avond volume gebruikt: {volume} (tijd: {current_time})")
return volume
else:
volume = settings.get('volume_day', settings.get('volume', 30))
print(f"☀️ Dag volume gebruikt: {volume} (tijd: {current_time})")
return volume
def apply_prayer_offsets(gebedstijden, settings):
"""Pas offsets toe op gebedstijden"""
from datetime import datetime, timedelta
offsets = {
'Fajr': settings.get('fajr_offset', 0),
'Zuhr': settings.get('zuhr_offset', 0),
'Asr': settings.get('asr_offset', 0),
'Maghrib': settings.get('maghrib_offset', 0),
'Isha': settings.get('isha_offset', 0)
}
adjusted_times = {}
for prayer, original_time in gebedstijden.items():
offset_minutes = offsets.get(prayer, 0)
if offset_minutes == 0:
adjusted_times[prayer] = original_time
continue
try:
# Parse de originele tijd
time_obj = datetime.strptime(original_time, '%H:%M')
# Voeg offset toe
adjusted_time = time_obj + timedelta(minutes=offset_minutes)
# Converteer terug naar string
adjusted_times[prayer] = adjusted_time.strftime('%H:%M')
if offset_minutes != 0:
print(f"📅 {prayer}: {original_time}{adjusted_times[prayer]} ({offset_minutes:+d} min)")
except Exception as e:
print(f"⚠️ Fout bij aanpassen {prayer} tijd: {e}")
adjusted_times[prayer] = original_time
return adjusted_times
@lru_cache(maxsize=1)
def fetch_sonos_zones():
return get_cached_data('sonos_zones', lambda: _fetch_sonos_zones_impl())
def _fetch_sonos_zones_impl():
try:
res = requests.get(f'http://{SONOS_API_IP}:5005/zones', timeout=5)
res.raise_for_status()
data = res.json()
zones = []
for group in data:
for player in group['members']:
zones.append(player['roomName'])
return sorted(set(zones))
except Exception as e:
print(f'Fout bij ophalen Sonos-zones: {e}')
return ['Woonkamer', 'Slaapkamer', 'Keuken']
def get_date_info():
"""Genereert zowel Gregoriaanse als Hijri datum informatie"""
now = datetime.now()
# Gregoriaanse datum
gregorian_date = now.strftime('%A %d %B %Y')
gregorian_short = now.strftime('%d-%m-%Y')
# Nederlandse maandnamen
dutch_months = {
'January': 'januari', 'February': 'februari', 'March': 'maart',
'April': 'april', 'May': 'mei', 'June': 'juni',
'July': 'juli', 'August': 'augustus', 'September': 'september',
'October': 'oktober', 'November': 'november', 'December': 'december'
}
# Nederlandse dagnamen
dutch_days = {
'Monday': 'maandag', 'Tuesday': 'dinsdag', 'Wednesday': 'woensdag',
'Thursday': 'donderdag', 'Friday': 'vrijdag', 'Saturday': 'zaterdag', 'Sunday': 'zondag'
}
# Vervang Engelse namen door Nederlandse
for eng, nl in dutch_months.items():
gregorian_date = gregorian_date.replace(eng, nl)
for eng, nl in dutch_days.items():
gregorian_date = gregorian_date.replace(eng, nl)
try:
# Converteer naar Hijri datum
hijri_date = Gregorian(now.year, now.month, now.day).to_hijri()
# Arabische maandnamen
hijri_months = [
'محرم', 'صفر', 'ربيع الأول', 'ربيع الثاني', 'جمادى الأولى', 'جمادى الثانية',
'رجب', 'شعبان', 'رمضان', 'شوال', 'ذو القعدة', 'ذو الحجة'
]
# Nederlandse maandnamen voor Hijri
hijri_months_nl = [
'Muharram', 'Safar', 'Rabi al-Awwal', 'Rabi al-Thani', 'Jumada al-Awwal', 'Jumada al-Thani',
'Rajab', 'Sha\'ban', 'Ramadan', 'Shawwal', 'Dhu al-Qi\'dah', 'Dhu al-Hijjah'
]
hijri_arabic = f"{hijri_date.day} {hijri_months[hijri_date.month - 1]} {hijri_date.year} هـ"
hijri_dutch = f"{hijri_date.day} {hijri_months_nl[hijri_date.month - 1]} {hijri_date.year} AH"
except Exception as e:
print(f"⚠️ Fout bij Hijri conversie: {e}")
hijri_arabic = "التاريخ الهجري غير متاح"
hijri_dutch = "Hijri datum niet beschikbaar"
return {
'gregorian_full': gregorian_date,
'gregorian_short': gregorian_short,
'hijri_arabic': hijri_arabic,
'hijri_dutch': hijri_dutch,
'current_time': now.strftime('%H:%M')
}
@app.route('/')
def index():
settings = load_settings()
# Haal alle data parallel op
data = fetch_data_parallel()
# Verwerk gebedstijden
gebedstijden = apply_prayer_offsets(data['prayer_times'], settings)
# Bepaal volgende gebed
now = datetime.now().strftime('%H:%M')
next_time = "Onbekend"
next_name = "Onbekend"
for naam, tijd in gebedstijden.items():
if tijd > now:
next_time = tijd
next_name = naam
break
else:
next_time = list(gebedstijden.values())[0]
next_name = list(gebedstijden.keys())[0]
return render_template('index.html',
next_time=next_time,
next_name=next_name,
dua="اللّهُمَّ اجْعَلْ صَلاتِي نُورًا",
hadith=data['hadith'],
gebedstijden=gebedstijden,
settings=settings,
weather=data['weather'],
date_info=data['date'])
@app.route('/instellingen', methods=['GET', 'POST'])
def instellingen():
if request.method == 'POST':
try:
# Nieuwe volume instellingen
if 'volume_day' in request.form and 'volume_night' in request.form:
settings = load_settings()
settings['volume_day'] = int(request.form['volume_day'])
settings['volume_night'] = int(request.form['volume_night'])
settings['night_start'] = request.form['night_start']
settings['day_start'] = request.form['day_start']
# Update oude volume voor backward compatibility
settings['volume'] = settings['volume_day']
else:
# Fallback: gebruik default waarden als velden ontbreken
settings = load_settings()
settings['volume_day'] = settings.get('volume_day', 15)
settings['volume_night'] = settings.get('volume_night', 8)
settings['night_start'] = settings.get('night_start', '20:00')
settings['day_start'] = settings.get('day_start', '07:00')
settings['volume'] = settings['volume_day']
# Gebedstijd offsets
settings['fajr_offset'] = int(request.form.get('fajr_offset', 0))
settings['zuhr_offset'] = int(request.form.get('zuhr_offset', 0))
settings['asr_offset'] = int(request.form.get('asr_offset', 0))
settings['maghrib_offset'] = int(request.form.get('maghrib_offset', 0))
settings['isha_offset'] = int(request.form.get('isha_offset', 0))
# Adzkaar instellingen
settings['adzkaar_enabled'] = 'adzkaar_enabled' in request.form
settings['adzkaar_duration'] = int(request.form.get('adzkaar_duration', 5))
# Hadith instellingen
settings['hadith_interval_seconds'] = int(request.form.get('hadith_interval_seconds', 30))
# Pi HDMI volume instelling
if 'pi_hdmi_volume' in request.form:
pi_volume = int(request.form.get('pi_hdmi_volume', 70))
settings['pi_hdmi_volume'] = pi_volume
# Probeer Pi volume in te stellen via amixer (alleen als we niet in Docker draaien)
try:
# Check of we in een Docker container draaien
if os.path.exists('/.dockerenv'):
print(f"🔊 Pi HDMI volume opgeslagen: {pi_volume}% (Docker container - amixer niet beschikbaar)")
else:
subprocess.run(['amixer', 'set', 'PCM', f'{pi_volume}%'],
check=True, capture_output=True, text=True)
print(f"🔊 Pi HDMI volume ingesteld op {pi_volume}%")
except (subprocess.CalledProcessError, FileNotFoundError) as e:
print(f"⚠️ Kon Pi volume niet direct instellen: {e} (volume wel opgeslagen)")
except Exception as e:
print(f"❌ Onverwachte fout bij volume instelling: {e}")
settings['zones'] = request.form.getlist('zones')
settings['audio_clip'] = request.form['audio_clip']
with open(SETTINGS_PATH, 'w') as f:
json.dump(settings, f, indent=2)
if 'test_clip' in request.form:
# Gebruik het juiste volume voor de test
test_volume = get_current_volume(settings)
for zone in settings['zones']:
url = f"http://{SONOS_API_IP}:5005/{zone}/clip/{settings['audio_clip']}/{test_volume}"
try:
r = requests.get(url, timeout=3)
print(f"🎵 Test clip verzonden naar {zone}, status: {r.status_code}")
except Exception as e:
print(f"❌ Fout bij afspelen op zone {zone}: {e}")
return redirect('/instellingen')
except Exception as e:
print(f"❌ Fout bij opslaan instellingen: {e}")
# Probeer alsnog door te gaan zonder de Pi volume instelling
return redirect('/instellingen')
# Haal alle data parallel op
data = fetch_data_parallel()
return render_template('settings.html',
settings=load_settings(),
alle_zones=data['sonos'],
date_info=data['date'],
weather=data['weather'])
@app.route('/api/hadith')
def api_hadith():
hadith = load_hadith()
return jsonify(hadith)
@app.route('/api/mute', methods=['POST'])
def toggle_mute():
try:
data = flask_request.get_json()
mute = data.get('mute', False)
with open(SETTINGS_PATH, 'r+') as f:
settings = json.load(f)
settings['mute'] = mute
f.seek(0)
json.dump(settings, f, indent=2)
f.truncate()
return jsonify({'success': True, 'mute': mute})
except Exception as e:
return jsonify({'success': False, 'error': str(e)}), 500
@app.route('/api/weather')
def api_weather():
"""API endpoint voor het ophalen van actuele weerdata"""
weather = fetch_weather_data()
return jsonify(weather)
@app.route('/api/date-info')
def api_date_info():
"""API endpoint voor het ophalen van actuele datum informatie"""
date_info = get_date_info()
return jsonify(date_info)
@app.route('/api/test-audio', methods=['POST'])
def test_audio():
"""Test een audio bestand lokaal in de browser"""
try:
data = flask_request.get_json()
audio_file = data.get('audio_file', 'adhan1.mp3')
# Controleer of het bestand bestaat
audio_path = os.path.join(CLIPS_PATH, audio_file)
if not os.path.exists(audio_path):
return jsonify({'success': False, 'error': 'Audio bestand niet gevonden'}), 404
return jsonify({
'success': True,
'message': f'Audio bestand {audio_file} is beschikbaar',
'url': f'/static/clips/{audio_file}'
})
except Exception as e:
return jsonify({'success': False, 'error': str(e)}), 500
@app.route('/debug')
def debug():
"""Debug pagina voor het testen van adhaan functionaliteit"""
settings = load_settings()
# Check of debug mode is ingeschakeld
if not settings.get('debug_mode', False):
return redirect('/')
gebedstijden = {}
try:
res = requests.get(VUMG_API)
res.raise_for_status()
data = res.json()[0]
gebedstijden = {
"Fajr": data.get("fajr_jamah", "00:00")[:5], # Haal seconden weg
"Zuhr": data.get("zuhr_jamah", "00:00")[:5], # Haal seconden weg
"Asr": data.get("asr_jamah", "00:00")[:5], # Haal seconden weg
"Maghrib": data.get("maghrib_jamah", "00:00")[:5], # Haal seconden weg
"Isha": data.get("isha_jamah", "00:00")[:5] # Haal seconden weg
}
# Pas offsets toe op gebedstijden
gebedstijden = apply_prayer_offsets(gebedstijden, settings)
except Exception as e:
print("❌ Fout bij ophalen gebedstijden voor debug:", e)
# Fallback tijden voor testing
gebedstijden = {
"Fajr": "06:00",
"Zuhr": "12:30",
"Asr": "15:00",
"Maghrib": "17:30",
"Isha": "19:00"
}
# Pas ook offsets toe op fallback tijden
gebedstijden = apply_prayer_offsets(gebedstijden, settings)
return render_template('debug.html',
gebedstijden=gebedstijden,
settings=settings)
@app.route('/api/debug-adhaan', methods=['POST'])
def debug_adhaan():
"""API endpoint voor debug adhaan testing"""
import time
# Check of debug mode is ingeschakeld
settings = load_settings()
if not settings.get('debug_mode', False):
return jsonify({'success': False, 'error': 'Debug mode is niet ingeschakeld'}), 403
try:
data = flask_request.get_json()
prayer_name = data.get('prayer', 'Test')
test_time = data.get('time', 'Onbekend')
# Check mute status
if settings.get('mute', False):
return jsonify({
'success': False,
'error': 'Adhaan niet afgespeeld: mute staat aan',
'muted': True
})
# Verbeterde Sonos afspeling
success_count = 0
error_messages = []
zones = settings.get('zones', ['Woonkamer'])
audio_clip = settings.get('audio_clip', 'adhan1.mp3')
volume = get_current_volume(settings)
for zone in zones:
try:
# Stop eerst alle audio op deze zone
stop_url = f"http://{SONOS_API_IP}:5005/{zone}/pause"
requests.get(stop_url, timeout=2)
time.sleep(0.5) # Korte pauze
# Speel de clip af
clip_url = f"http://{SONOS_API_IP}:5005/{zone}/clip/{audio_clip}/{volume}"
response = requests.get(clip_url, timeout=10)
if response.status_code == 200:
success_count += 1
print(f"🔧 DEBUG: Adhaan afgespeeld op {zone} voor {prayer_name} om {test_time}")
else:
error_messages.append(f"{zone}: HTTP {response.status_code}")
except requests.exceptions.Timeout:
error_messages.append(f"{zone}: Timeout")
except Exception as e:
error_messages.append(f"{zone}: {str(e)}")
# Pauze tussen zones
if len(zones) > 1:
time.sleep(1)
if success_count > 0:
return jsonify({
'success': True,
'message': f'Adhaan afgespeeld op {success_count}/{len(zones)} zone(s)',
'errors': error_messages if error_messages else None
})
else:
return jsonify({
'success': False,
'error': f'Geen zones bereikbaar: {", ".join(error_messages)}'
})
except Exception as e:
return jsonify({'success': False, 'error': str(e)}), 500
@app.route('/api/sonos-status')
def sonos_status():
"""API endpoint voor Sonos status controle"""
try:
settings = load_settings()
zones = settings.get('zones', ['Woonkamer'])
status_info = {}
for zone in zones:
try:
# Haal zone status op
status_url = f"http://{SONOS_API_IP}:5005/{zone}/state"
response = requests.get(status_url, timeout=3)
if response.status_code == 200:
zone_data = response.json()
status_info[zone] = {
'status': 'online',
'playbackState': zone_data.get('playbackState', 'unknown'),
'currentTrack': zone_data.get('currentTrack', {}).get('title', 'Geen track'),
'volume': zone_data.get('volume', 0)
}
else:
status_info[zone] = {'status': 'error', 'error': f'HTTP {response.status_code}'}
except Exception as e:
status_info[zone] = {'status': 'offline', 'error': str(e)}
return jsonify({
'success': True,
'zones': status_info,
'api_ip': SONOS_API_IP
})
except Exception as e:
return jsonify({'success': False, 'error': str(e)}), 500
@app.route('/api/sonos-stop', methods=['POST'])
def sonos_stop():
"""API endpoint om alle Sonos zones te stoppen"""
try:
settings = load_settings()
zones = settings.get('zones', ['Woonkamer'])
stopped_zones = []
for zone in zones:
try:
stop_url = f"http://{SONOS_API_IP}:5005/{zone}/pause"
response = requests.get(stop_url, timeout=3)
if response.status_code == 200:
stopped_zones.append(zone)
except Exception as e:
print(f"Fout bij stoppen {zone}: {e}")
return jsonify({
'success': True,
'message': f'Audio gestopt op {len(stopped_zones)} zone(s)',
'stopped_zones': stopped_zones
})
except Exception as e:
return jsonify({'success': False, 'error': str(e)}), 500
@app.route('/quran')
def quran():
"""Quran speler pagina"""
settings = load_settings()
gebedstijden = {}
try:
res = requests.get(VUMG_API)
res.raise_for_status()
data = res.json()[0]
gebedstijden = {
"Fajr": data.get("fajr_jamah", "00:00")[:5], # Haal seconden weg
"Zuhr": data.get("zuhr_jamah", "00:00")[:5], # Haal seconden weg
"Asr": data.get("asr_jamah", "00:00")[:5], # Haal seconden weg
"Maghrib": data.get("maghrib_jamah", "00:00")[:5], # Haal seconden weg
"Isha": data.get("isha_jamah", "00:00")[:5] # Haal seconden weg
}
# Pas offsets toe op gebedstijden
gebedstijden = apply_prayer_offsets(gebedstijden, settings)
except Exception as e:
print("❌ Fout bij ophalen gebedstijden voor quran pagina:", e)
# Fallback tijden
gebedstijden = {
"Fajr": "06:00",
"Zuhr": "12:30",
"Asr": "15:00",
"Maghrib": "17:30",
"Isha": "19:00"
}
# Pas ook offsets toe op fallback tijden
gebedstijden = apply_prayer_offsets(gebedstijden, settings)
return render_template('quran.html',
gebedstijden=gebedstijden,
settings=settings)
@app.route('/adzkaar')
def adzkaar():
"""Adzkaar pagina na gebed"""
settings = load_settings()
duration_minutes = settings.get('adzkaar_duration', 5) # Default 5 minuten
return render_template('adzkaar.html',
duration_minutes=duration_minutes,
settings=settings)
@app.route('/api/trigger-adzkaar', methods=['POST'])
def trigger_adzkaar():
"""API endpoint om Adzkaar scherm te triggeren (voor debug)"""
try:
settings = load_settings()
# Check of Adzkaar is ingeschakeld
if not settings.get('adzkaar_enabled', True):
return jsonify({
'success': False,
'error': 'Adzkaar is uitgeschakeld in instellingen'
})
duration = settings.get('adzkaar_duration', 5)
return jsonify({
'success': True,
'message': f'Adzkaar scherm wordt getoond voor {duration} minuten',
'duration': duration,
'url': '/adzkaar'
})
except Exception as e:
return jsonify({'success': False, 'error': str(e)}), 500
@app.route('/api/debug-time', methods=['GET', 'POST'])
def debug_time():
"""API endpoint voor debug tijd beheer"""
global debug_time_offset, debug_mode_active
if flask_request.method == 'POST':
# Check of debug mode is ingeschakeld voor POST requests
settings = load_settings()
if not settings.get('debug_mode', False):
return jsonify({'success': False, 'error': 'Debug mode is niet ingeschakeld'}), 403
try:
data = flask_request.get_json()
action = data.get('action')
if action == 'set_offset':
debug_time_offset = data.get('offset', 0)
debug_mode_active = True
return jsonify({
'success': True,
'message': f'Debug tijd offset ingesteld op {debug_time_offset} seconden',
'offset': debug_time_offset,
'debug_active': debug_mode_active
})
elif action == 'reset':
debug_time_offset = 0
debug_mode_active = False
return jsonify({
'success': True,
'message': 'Debug tijd gereset naar echte tijd',
'offset': debug_time_offset,
'debug_active': debug_mode_active
})
elif action == 'adjust':
adjustment = data.get('seconds', 0)
debug_time_offset += adjustment
debug_mode_active = True
return jsonify({
'success': True,
'message': f'Debug tijd aangepast met {adjustment} seconden',
'offset': debug_time_offset,
'debug_active': debug_mode_active
})
else:
return jsonify({'success': False, 'error': 'Onbekende actie'}), 400
except Exception as e:
return jsonify({'success': False, 'error': str(e)}), 500
else: # GET request - altijd beschikbaar voor cron script
from datetime import datetime, timedelta
current_time = datetime.now()
if debug_mode_active:
current_time += timedelta(seconds=debug_time_offset)
return jsonify({
'success': True,
'current_time': current_time.strftime('%H:%M:%S'),
'current_time_display': current_time.strftime('%H:%M'),
'offset': debug_time_offset,
'debug_active': debug_mode_active,
'real_time': datetime.now().strftime('%H:%M:%S')
})
@app.route('/api/set-pi-volume', methods=['POST'])
def set_pi_volume():
"""API endpoint om Pi HDMI volume in te stellen"""
try:
data = flask_request.get_json()
volume = data.get('volume', 70)
# Valideer volume (0-100)
if not isinstance(volume, int) or volume < 0 or volume > 100:
return jsonify({'success': False, 'error': 'Volume moet tussen 0 en 100 zijn'}), 400
# Sla volume op in settings voor persistentie
settings = load_settings()
settings['pi_hdmi_volume'] = volume
with open(SETTINGS_PATH, 'w') as f:
json.dump(settings, f, indent=2)
# Check of we in een Docker container draaien
if os.path.exists('/.dockerenv'):
print(f"🔊 Pi HDMI volume opgeslagen: {volume}% (Docker container)")
return jsonify({
'success': True,
'message': f'Pi HDMI volume opgeslagen op {volume}%. Volume wordt toegepast bij browser herstart.',
'volume': volume,
'info': 'Docker container - volume opgeslagen voor browser audio'
})
else:
# Direct op Pi - probeer amixer
try:
subprocess.run(['amixer', 'set', 'PCM', f'{volume}%'],
check=True, capture_output=True, text=True)
print(f"🔊 Pi HDMI volume direct ingesteld op {volume}%")
return jsonify({
'success': True,
'message': f'Pi HDMI volume ingesteld op {volume}%',
'volume': volume
})
except (subprocess.CalledProcessError, FileNotFoundError) as e:
print(f"⚠️ Amixer fout: {e}")
return jsonify({
'success': True,
'message': f'Volume opgeslagen op {volume}%. Amixer niet beschikbaar.',
'volume': volume,
'warning': str(e)
})
except Exception as e:
print(f"❌ Volume API fout: {e}")
return jsonify({'success': False, 'error': str(e)}), 500
if __name__ == '__main__':
app.run(host='0.0.0.0', port=80)