diff --git a/.DS_Store b/.DS_Store new file mode 100644 index 0000000..ea87080 Binary files /dev/null and b/.DS_Store differ diff --git a/app.log b/app.log new file mode 100644 index 0000000..e69de29 diff --git a/app.py b/app.py new file mode 100644 index 0000000..80377ee --- /dev/null +++ b/app.py @@ -0,0 +1,134 @@ +import json +import os +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 + +app = Flask(__name__) +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') + +tracer = TracerConsole() +advertiser = AdvertiserBTSocket() + +current_hub = None +current_config = None + +def load_configs(): + 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 + +@app.route('/') +def index(): + return render_template('index.html', configs=load_configs()) + +@app.route('/load_config/') +def load_config(filename): + global current_config + 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')) + +@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('/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): + return send_from_directory(app.config['CONFIG_DIR'], filename) + +if __name__ == '__main__': + app.run(host='0.0.0.0', port=5050, debug=True) \ No newline at end of file diff --git a/configs/.DS_Store b/configs/.DS_Store new file mode 100644 index 0000000..5008ddf Binary files /dev/null and b/configs/.DS_Store differ diff --git a/configs/dampflok1.jpg b/configs/dampflok1.jpg new file mode 100644 index 0000000..dea7807 Binary files /dev/null and b/configs/dampflok1.jpg differ diff --git a/configs/dampflok1.json b/configs/dampflok1.json new file mode 100644 index 0000000..f9b55f3 --- /dev/null +++ b/configs/dampflok1.json @@ -0,0 +1,12 @@ +{ + "name": "Dampflok BR 01", + "image": "dampflok1.jpg", + "hub_id": 0, + "hub_type": "4channel", + "channels": [ + {"port": "A", "type": "motor", "name": "Fahrtrichtung", "invert": false, "negative_only": false}, + {"port": "B", "type": "motor", "name": "Unterstützung", "invert": false, "negative_only": false}, + {"port": "C", "type": "light", "name": "Licht vorne", "on_value": 1.0, "off_value": 0.0, "negative_only": false}, + {"port": "D", "type": "fogger", "name": "Dampf", "on_value": -1.0, "off_value": 0.0, "negative_only": true} + ] +} \ No newline at end of file diff --git a/requirements.txt b/requirements.txt new file mode 100644 index 0000000..3515814 --- /dev/null +++ b/requirements.txt @@ -0,0 +1,4 @@ +flask==3.0.3 +# Deine mkconnect-lib – je nach Installationsweg +git+https://git.avalon-skynet.work/oberon/mkconnect-lib.git +# oder falls lokal gebaut: ./path/to/mkconnect-lib \ No newline at end of file diff --git a/static/css/custom.css b/static/css/custom.css new file mode 100644 index 0000000..5c107dc --- /dev/null +++ b/static/css/custom.css @@ -0,0 +1,7 @@ +/* custom.css */ +body { + background-color: #f8f9fa; +} +.card { + margin-bottom: 1.5rem; +} \ No newline at end of file diff --git a/static/js/app.js b/static/js/app.js new file mode 100644 index 0000000..6c46311 --- /dev/null +++ b/static/js/app.js @@ -0,0 +1,2 @@ +// app.js – gemeinsames JavaScript +console.log("MK Control JS geladen"); \ No newline at end of file diff --git a/templates/admin.html b/templates/admin.html new file mode 100644 index 0000000..e69de29 diff --git a/templates/base.html b/templates/base.html new file mode 100644 index 0000000..2817be1 --- /dev/null +++ b/templates/base.html @@ -0,0 +1,84 @@ + + + + + + {% block title %}Mould King Control{% endblock %} + + + + + + + + {% block head_extra %}{% endblock %} + + + + + + + +
+
+ {% with messages = get_flashed_messages(with_categories=true) %} + {% if messages %} + {% for category, message in messages %} + + {% endfor %} + {% endif %} + {% endwith %} + + {% block content %}{% endblock %} +
+
+ + +
+
+

Mould King Bluetooth Control © 2025–2026 | Powered by Flask & Bootstrap

+
+
+ + + + + + + + {% block scripts %}{% endblock %} + + \ No newline at end of file diff --git a/templates/control.html b/templates/control.html new file mode 100644 index 0000000..e69de29 diff --git a/templates/edit.html b/templates/edit.html new file mode 100644 index 0000000..e69de29 diff --git a/templates/index.html b/templates/index.html new file mode 100644 index 0000000..e69de29 diff --git a/templates/logs.html b/templates/logs.html new file mode 100644 index 0000000..e69de29