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 app = Flask(__name__) # Cache configuratie CACHE_DURATION = 300 # 5 minuten cache voor API calls last_api_call = {} cached_data = {} executor = ThreadPoolExecutor(max_workers=3) # Voor parallelle API calls 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""" futures = { 'weather': executor.submit(fetch_weather_data), 'sonos': executor.submit(fetch_sonos_zones), 'date': executor.submit(get_date_info) } return {key: future.result() for key, future in futures.items()} # 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 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() 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(): 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)