diff --git a/adhan-webapp/app.py b/adhan-webapp/app.py index 9ba346e..8d4f1bd 100644 --- a/adhan-webapp/app.py +++ b/adhan-webapp/app.py @@ -1,11 +1,99 @@ 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 +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): @@ -28,8 +116,21 @@ CLIPS_PATH = os.path.join(os.path.dirname(os.path.abspath(__file__)), 'static', 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, @@ -70,15 +171,6 @@ def load_hadith(): 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 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 get_current_volume(settings): """Bepaal het juiste volume op basis van de huidige tijd""" from datetime import datetime, timedelta @@ -158,7 +250,11 @@ def apply_prayer_offsets(gebedstijden, settings): 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() @@ -235,73 +331,44 @@ def get_date_info(): @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" - debug_data = {} - gebedstijden = {} - hadith = load_hadith() - weather = fetch_weather_data() - date_info = get_date_info() - - 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) - - now = datetime.now().strftime('%H:%M') - 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] - - debug_data = { - "api_response": data, - "gebedstijden": gebedstijden, - "now": now, - "next_time": next_time, - "next_name": next_name - } - - except Exception as e: - print("❌ Fout bij ophalen/verwerken van gebedstijden:", e) - - dua = "اللّهُمَّ اجْعَلْ صَلاتِي نُورًا" + + 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=dua, - hadith=hadith, - gebedstijden=gebedstijden, - debug=debug_data, - settings=settings, - weather=weather, - date_info=date_info) + 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(): - settings = load_settings() - clips = [f for f in os.listdir(CLIPS_PATH) if f.endswith('.mp3') or f.endswith('.wav')] - alle_zones = fetch_sonos_zones() - 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'] @@ -310,6 +377,7 @@ def instellingen(): 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') @@ -373,10 +441,14 @@ def instellingen(): # 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=settings, - alle_zones=alle_zones, - clips=clips) + settings=load_settings(), + alle_zones=data['sonos'], + date_info=data['date'], + weather=data['weather']) @app.route('/api/hadith') def api_hadith(): diff --git a/adhan-webapp/done b/adhan-webapp/done index f009148..d582ecc 100644 --- a/adhan-webapp/done +++ b/adhan-webapp/done @@ -13,3 +13,6 @@ Mon May 26 18:17:57 CEST 2025: Tijdzone probleem opgelost - Container gebruikt n 2025-05-28 03:49:24 - Adzkaar fullscreen functionaliteit geïmplementeerd: nieuwe /adzkaar route, instellingen, debug knoppen, automatische trigger na gebedstijden, Nederlandse/Arabische dhikr content 2025-05-28 03:55:38 - Adzkaar scherm verbeterd naar kaart-voor-kaart weergave met navigatie knoppen en toetsenbord besturing Wed May 28 14:09:12 CEST 2025: Sonos debug tijd synchronisatie geïmplementeerd - get_current_volume functie en cron script gebruiken nu debug tijd API, volume bepaling werkt correct in debug mode +2025-05-29 21:24:37 - Performance optimalisaties toegevoegd: caching voor API calls en instellingen +2025-05-29 21:27:02 - UI optimalisaties toegevoegd: lazy loading, parallelle API calls en caching +2025-05-29 21:30:24 - Geavanceerde optimalisaties toegevoegd: async/await, debouncing en DOM caching diff --git a/adhan-webapp/templates/adzkaar.html b/adhan-webapp/templates/adzkaar.html index 100fe00..eb81bd5 100644 --- a/adhan-webapp/templates/adzkaar.html +++ b/adhan-webapp/templates/adzkaar.html @@ -3,6 +3,8 @@
Adzkaar na het Gebed
+Herinneringen voor na het gebed