from flask import Flask, render_template, request, redirect, send_from_directory, jsonify, request as flask_request import requests, json, os, random from datetime import datetime from config import * from hijridate import Gregorian app = Flask(__name__) # 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/') 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 = '192.168.0.114' # IP van host waar Sonos API draait def fetch_weather_data(): """Haalt weersinformatie op voor de geconfigureerde locatie""" 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 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 # 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) 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: return settings.get('volume_night', settings.get('volume', 30) // 2) else: return settings.get('volume_day', settings.get('volume', 30)) 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 def fetch_sonos_zones(): 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() 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 = "اللّهُمَّ اجْعَلْ صَلاتِي نُورًا" 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) @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': # Nieuwe volume instellingen if 'volume_day' in request.form and 'volume_night' in request.form: 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['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)) 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') return render_template('settings.html', settings=settings, alle_zones=alle_zones, clips=clips) @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() 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 try: data = flask_request.get_json() prayer_name = data.get('prayer', 'Test') test_time = data.get('time', 'Onbekend') settings = load_settings() # 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) if __name__ == '__main__': app.run(host='0.0.0.0', port=80)