diff --git a/app.py b/app.py index 80377ee..935cca4 100644 --- a/app.py +++ b/app.py @@ -1,134 +1,81 @@ -import json +# app.py import os +import json import logging -from flask import Flask, render_template, jsonify, request, send_from_directory, redirect, url_for - -from mkconnect.mouldking.MouldKing import MouldKing -from mkconnect.tracer.TracerConsole import TracerConsole -from mkconnect.advertiser.AdvertiserBTSocket import AdvertiserBTSocket +from flask import Flask, render_template, redirect, url_for, send_from_directory, request, jsonify app = Flask(__name__) + +# Konfiguration app.config['CONFIG_DIR'] = 'configs' os.makedirs(app.config['CONFIG_DIR'], exist_ok=True) -# Logging setup -logging.basicConfig(filename='app.log', level=logging.INFO, format='%(asctime)s - %(levelname)s - %(message)s') +# Logging +logging.basicConfig( + filename='app.log', + level=logging.INFO, + format='%(asctime)s | %(levelname)-8s | %(message)s', + datefmt='%Y-%m-%d %H:%M:%S' +) -tracer = TracerConsole() -advertiser = AdvertiserBTSocket() - -current_hub = None -current_config = None +logger = logging.getLogger(__name__) def load_configs(): + """Liest alle .json Dateien aus dem configs-Ordner""" configs = [] - for file in os.listdir(app.config['CONFIG_DIR']): - if file.endswith('.json'): - with open(os.path.join(app.config['CONFIG_DIR'], file)) as f: - cfg = json.load(f) - cfg['filename'] = file - configs.append(cfg) - return configs + if not os.path.exists(app.config['CONFIG_DIR']): + return configs + + for filename in os.listdir(app.config['CONFIG_DIR']): + if filename.lower().endswith('.json'): + path = os.path.join(app.config['CONFIG_DIR'], filename) + try: + with open(path, 'r', encoding='utf-8') as f: + data = json.load(f) + data['filename'] = filename + # Optional: name fallback, falls nicht vorhanden + data.setdefault('name', filename.replace('.json', '').replace('_', ' ').title()) + configs.append(data) + except Exception as e: + logger.error(f"Fehler beim Laden von {filename}: {e}") + return sorted(configs, key=lambda x: x.get('name', '')) + @app.route('/') def index(): - return render_template('index.html', configs=load_configs()) + configs = load_configs() + return render_template('index.html', configs=configs) + @app.route('/load_config/') def load_config(filename): - global current_config + # Später: Config in Session speichern oder global (für Entwicklung erstmal redirect) + # Hier nur redirect zur Steuerseite – echte Logik kommt später path = os.path.join(app.config['CONFIG_DIR'], filename) if not os.path.exists(path): - return "Config not found", 404 - with open(path) as f: - current_config = json.load(f) - current_config['filename'] = filename - return redirect(url_for('control_page')) + return "Konfigurationsdatei nicht gefunden", 404 + + # Für den Moment nur redirect – später speichern wir current_config + return redirect(url_for('control_page', filename=filename)) -@app.route('/control') -def control_page(): - if not current_config: - return redirect(url_for('index')) - return render_template('control.html', config=current_config) -@app.route('/api/connect', methods=['POST']) -def connect(): - global current_hub - if not current_config: - return jsonify({"error": "Keine Config geladen"}), 400 - try: - hub_id = current_config['hub_id'] - current_hub = MouldKing(hub_id=hub_id, advertiser=advertiser, tracer=tracer) - current_hub.connect() - logging.info(f"Verbunden mit Hub {hub_id} für {current_config['name']}") - return jsonify({"status": "connected"}) - except Exception as e: - logging.error(f"Connect-Fehler: {str(e)}") - return jsonify({"error": str(e)}), 500 +@app.route('/control/') +def control_page(filename): + # Später: Config laden und übergeben + # Aktuell nur Dummy-Template + return render_template('control.html', filename=filename) -@app.route('/api/control', methods=['POST']) -def control(): - if not current_hub: - return jsonify({"error": "Nicht verbunden"}), 400 - data = request.json - port = data['port'] - value = float(data['value']) - - ch = next((c for c in current_config['channels'] if c['port'] == port), None) - if not ch: - return jsonify({"error": "Port nicht in Config"}), 400 - - if ch.get('negative_only', False) and value > 0: - value = -abs(value) # Force negative - - if ch.get('invert', False): - value = -value - - if ch['type'] != 'motor': - value = ch.get('on_value', 1.0) if value != 0 else ch.get('off_value', 0.0) - - try: - current_hub.set_motor(channel=port, power=value) # Passe an reale Methode an - logging.info(f"Control: {port} @ {value}") - return jsonify({"status": "ok"}) - except Exception as e: - logging.error(f"Control-Fehler: {str(e)}") - return jsonify({"error": str(e)}), 500 - -@app.route('/api/stop_all', methods=['POST']) -def stop_all(): - if current_hub: - current_hub.stop_all() - logging.info("Alle gestoppt") - return jsonify({"status": "ok"}) - -@app.route('/admin') -def admin(): - return render_template('admin.html', configs=load_configs()) - -@app.route('/admin/edit/', methods=['GET', 'POST']) -def edit_config(filename): - path = os.path.join(app.config['CONFIG_DIR'], filename) - if request.method == 'POST': - data = request.form['config'] - with open(path, 'w') as f: - json.dump(json.loads(data), f, indent=4) - return redirect(url_for('admin')) - if not os.path.exists(path): - return "Not found", 404 - with open(path) as f: - config = json.load(f) - return render_template('edit.html', config=json.dumps(config, indent=4), filename=filename) - -@app.route('/admin/logs') -def view_logs(): - with open('app.log') as f: - logs = f.read() - return render_template('logs.html', logs=logs) @app.route('/configs/') def serve_config_file(filename): + """Bilder und andere Dateien aus configs/ ausliefern""" return send_from_directory(app.config['CONFIG_DIR'], filename) + +@app.route('/admin') +def admin(): + return render_template('admin.html') + + if __name__ == '__main__': - app.run(host='0.0.0.0', port=5050, debug=True) \ No newline at end of file + app.run(host='0.0.0.0', port=5050, debug=True) diff --git a/configs/testlok.jpg b/configs/testlok.jpg new file mode 100644 index 0000000..c2f50a6 Binary files /dev/null and b/configs/testlok.jpg differ diff --git a/configs/testlok.json b/configs/testlok.json new file mode 100644 index 0000000..10467e5 --- /dev/null +++ b/configs/testlok.json @@ -0,0 +1,7 @@ + +{ + "name": "Test Dampflok", + "image": "testlok.jpg", + "hub_id": 0, + "hub_type": "4channel" +} \ No newline at end of file diff --git a/static/css/custom.css b/static/css/custom.css index 5c107dc..6d3efc4 100644 --- a/static/css/custom.css +++ b/static/css/custom.css @@ -4,4 +4,15 @@ body { } .card { margin-bottom: 1.5rem; +} + +.card-img-top { + background-color: #e9ecef; +} +.card { + transition: transform 0.15s ease-in-out; +} +.card:hover { + transform: translateY(-4px); + box-shadow: 0 8px 16px rgba(0,0,0,0.15) !important; } \ No newline at end of file diff --git a/static/js/app.js b/static/js/app.js index 6c46311..5e68713 100644 --- a/static/js/app.js +++ b/static/js/app.js @@ -1,2 +1,7 @@ // app.js – gemeinsames JavaScript -console.log("MK Control JS geladen"); \ No newline at end of file +console.log("MK Control JS geladen"); + +document.addEventListener('DOMContentLoaded', () => { + console.log('MK Control Frontend geladen'); + // Hier kommen später alle Fetch-Logiken, Slider-Handler etc. rein +}); \ No newline at end of file diff --git a/templates/index.html b/templates/index.html index e69de29..bf23768 100644 --- a/templates/index.html +++ b/templates/index.html @@ -0,0 +1,56 @@ +{% extends "base.html" %} + +{% block title %}Zugmodelle auswählen{% endblock %} + +{% block content %} + +
+

Wähle dein Zugmodell

+ + {% if configs %} +
+ {% for cfg in configs %} +
+
+ {% if cfg.image %} + {{ cfg.name }} + {% else %} +
+ +
+ {% endif %} +
+
{{ cfg.name }}
+

+ Hub-ID: {{ cfg.hub_id | default('?') }} • + {{ cfg.hub_type | default('unbekannt') }} +

+ +
+
+
+ {% endfor %} +
+ {% else %} +
+

Noch keine Konfigurationen vorhanden

+

Lege im configs/-Ordner JSON-Dateien an oder gehe zum Admin-Bereich.

+
+ {% endif %} + + +
+ +{% endblock %} \ No newline at end of file