added bluetooth connect and test-hub script

This commit is contained in:
oberon 2026-02-10 21:39:54 +01:00
parent 6f30454fb9
commit d3ba2c270d
2 changed files with 103 additions and 49 deletions

143
app.py
View File

@ -4,9 +4,13 @@ import json
import logging
from flask import Flask, render_template, redirect, url_for, send_from_directory, request, jsonify
# ── Bluetooth ────────────────────────────────────────────────────────────────
from mkconnect.mouldking.MouldKing import MouldKing
from mkconnect.tracer.TracerConsole import TracerConsole
from mkconnect.advertiser.AdvertiserBTSocket import AdvertiserBTSocket
app = Flask(__name__)
# Konfiguration
app.config['CONFIG_DIR'] = 'configs'
os.makedirs(app.config['CONFIG_DIR'], exist_ok=True)
@ -17,15 +21,18 @@ logging.basicConfig(
format='%(asctime)s | %(levelname)-8s | %(message)s',
datefmt='%Y-%m-%d %H:%M:%S'
)
logger = logging.getLogger(__name__)
def load_configs():
"""Liest alle .json Dateien aus dem configs-Ordner"""
configs = []
if not os.path.exists(app.config['CONFIG_DIR']):
return configs
# Bluetooth-Komponenten (einmalig initialisieren)
tracer = TracerConsole()
advertiser = AdvertiserBTSocket() # ← ohne tracer, wie korrigiert
# Globale Zustände (für Einzelbenutzer / Entwicklung später Session/DB)
current_config = None
current_hub: MouldKing | None = None
def load_configs():
configs = []
for filename in os.listdir(app.config['CONFIG_DIR']):
if filename.lower().endswith('.json'):
path = os.path.join(app.config['CONFIG_DIR'], filename)
@ -33,91 +40,129 @@ def load_configs():
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}")
logger.error(f"Fehler beim Laden {filename}: {e}")
return sorted(configs, key=lambda x: x.get('name', ''))
@app.route('/')
def index():
configs = load_configs()
return render_template('index.html', configs=configs)
return render_template('index.html', configs=load_configs())
@app.route('/configs/<path:filename>')
def serve_config_file(filename):
"""Liefert Bilder und Dateien aus dem configs-Ordner aus"""
try:
return send_from_directory(app.config['CONFIG_DIR'], filename)
except Exception as e:
logger.error(f"Fehler beim Ausliefern von {filename}: {e}")
return "Datei nicht gefunden", 404
current_config = None
current_filename = None
@app.route('/load_config/<filename>')
def load_config(filename):
global current_config, current_filename
global current_config
path = os.path.join(app.config['CONFIG_DIR'], filename)
if not os.path.exists(path):
return "Konfigurationsdatei nicht gefunden", 404
return "Datei nicht gefunden", 404
try:
with open(path, 'r', encoding='utf-8') as f:
current_config = json.load(f)
current_config['filename'] = filename
current_filename = filename
logger.info(f"Konfiguration geladen: {filename}")
logger.info(f"Config geladen: {filename}")
return redirect(url_for('control_page'))
except Exception as e:
logger.error(f"Fehler beim Laden der Config {filename}: {e}")
return "Fehler beim Laden der Konfiguration", 500
logger.error(f"Config-Ladefehler {filename}: {e}")
return "Fehler beim Laden", 500
@app.route('/control')
def control_page():
if current_config is None:
return redirect(url_for('index'))
return render_template('control.html', config=current_config)
# Neue API-Route (Platzhalter später echte Bluetooth-Steuerung)
@app.route('/configs/<path:filename>')
def serve_config_file(filename):
return send_from_directory(app.config['CONFIG_DIR'], filename)
# ── API ──────────────────────────────────────────────────────────────────────
@app.route('/api/connect', methods=['POST'])
def api_connect():
# Hier kommt später der echte Code mit MouldKing
# Aktuell nur simuliert
global current_hub
if current_config is None:
return jsonify({"success": False, "message": "Keine Konfiguration geladen"}), 400
logger.info(f"Simulierter Connect zu Hub {current_config.get('hub_id')}")
return jsonify({
"success": True,
"message": "Verbunden (Simulation)",
"hub_id": current_config.get('hub_id')
})
try:
hub_id = int(current_config.get('hub_id', 0))
# Falls schon verbunden → erstmal trennen (sauberer Neustart)
if current_hub is not None:
try:
current_hub.disconnect() # oder .close() je nach Methode
except:
pass
current_hub = MouldKing(
hub_id=hub_id,
advertiser=advertiser,
tracer=tracer # ← falls das doch geht; sonst entfernen
)
current_hub.connect() # ← das ist der entscheidende Aufruf
logger.info(f"Erfolgreich verbunden mit Hub-ID {hub_id}")
return jsonify({"success": True, "message": f"Hub {hub_id} verbunden"})
except Exception as e:
logger.exception("Connect-Fehler")
return jsonify({
"success": False,
"message": f"Verbindungsfehler: {str(e)}"
}), 500
@app.route('/api/control', methods=['POST'])
def api_control():
# Platzhalter für spätere echte Steuerung
data = request.get_json()
logger.info(f"Steuerbefehl empfangen: {data}")
return jsonify({"success": True, "message": "Befehl gesendet (Simulation)"})
global current_hub
if current_hub is None:
return jsonify({"success": False, "message": "Nicht verbunden"}), 400
try:
data = request.get_json()
port = data['port']
value = float(data['value'])
# Config-spezifische Anpassungen
channel_config = next((c for c in current_config['channels'] if c['port'] == port), None)
if channel_config:
if channel_config.get('invert', False):
value = -value
if channel_config.get('negative_only', False) and value >= 0:
value = -abs(value)
if channel_config['type'] != 'motor':
value = channel_config.get('on_value', 1.0) if value != 0 else channel_config.get('off_value', 0.0)
# Hier kommt der echte Aufruf
current_hub.set_motor(channel=port, power=value) # ← ← ← Kernaufruf
logger.info(f"Steuerung: {port}{value:.2f}")
return jsonify({"success": True})
except Exception as e:
logger.exception("Control-Fehler")
return jsonify({"success": False, "message": str(e)}), 500
@app.route('/api/stop_all', methods=['POST'])
def api_stop_all():
logger.info("Alle stoppen (Simulation)")
return jsonify({"success": True, "message": "Alle Kanäle gestoppt"})
global current_hub
if current_hub is None:
return jsonify({"success": False, "message": "Nicht verbunden"}), 400
@app.route('/admin')
def admin():
return render_template('admin.html')
try:
current_hub.stop_all() # ← oder alle Kanäle manuell auf 0 setzen
logger.info("Alle Kanäle gestoppt")
return jsonify({"success": True})
except Exception as e:
logger.exception("Stop-Fehler")
return jsonify({"success": False, "message": str(e)}), 500
if __name__ == '__main__':
app.run(host='0.0.0.0', port=5055, debug=True)
app.run(host='0.0.0.0', port=5000, debug=True)

9
other/test-hub.py Normal file
View File

@ -0,0 +1,9 @@
# test_hub.py
from mkconnect.mouldking.MouldKing import MouldKing
from mkconnect.tracer.TracerConsole import TracerConsole
from mkconnect.advertiser.AdvertiserBTSocket import AdvertiserBTSocket
tracer = TracerConsole()
adv = AdvertiserBTSocket()
hub = MouldKing(hub_id=0, advertiser=adv, tracer=tracer)
print(dir(hub)) # ← zeigt alle Methoden