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 import logging
from flask import Flask, render_template, redirect, url_for, send_from_directory, request, jsonify 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__) app = Flask(__name__)
# Konfiguration
app.config['CONFIG_DIR'] = 'configs' app.config['CONFIG_DIR'] = 'configs'
os.makedirs(app.config['CONFIG_DIR'], exist_ok=True) os.makedirs(app.config['CONFIG_DIR'], exist_ok=True)
@ -17,15 +21,18 @@ logging.basicConfig(
format='%(asctime)s | %(levelname)-8s | %(message)s', format='%(asctime)s | %(levelname)-8s | %(message)s',
datefmt='%Y-%m-%d %H:%M:%S' datefmt='%Y-%m-%d %H:%M:%S'
) )
logger = logging.getLogger(__name__) logger = logging.getLogger(__name__)
# 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(): def load_configs():
"""Liest alle .json Dateien aus dem configs-Ordner"""
configs = [] configs = []
if not os.path.exists(app.config['CONFIG_DIR']):
return configs
for filename in os.listdir(app.config['CONFIG_DIR']): for filename in os.listdir(app.config['CONFIG_DIR']):
if filename.lower().endswith('.json'): if filename.lower().endswith('.json'):
path = os.path.join(app.config['CONFIG_DIR'], filename) 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: with open(path, 'r', encoding='utf-8') as f:
data = json.load(f) data = json.load(f)
data['filename'] = filename data['filename'] = filename
# Optional: name fallback, falls nicht vorhanden
data.setdefault('name', filename.replace('.json', '').replace('_', ' ').title()) data.setdefault('name', filename.replace('.json', '').replace('_', ' ').title())
configs.append(data) configs.append(data)
except Exception as e: 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', '')) return sorted(configs, key=lambda x: x.get('name', ''))
@app.route('/') @app.route('/')
def index(): def index():
configs = load_configs() return render_template('index.html', configs=load_configs())
return render_template('index.html', configs=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>') @app.route('/load_config/<filename>')
def load_config(filename): def load_config(filename):
global current_config, current_filename global current_config
path = os.path.join(app.config['CONFIG_DIR'], filename) path = os.path.join(app.config['CONFIG_DIR'], filename)
if not os.path.exists(path): if not os.path.exists(path):
return "Konfigurationsdatei nicht gefunden", 404 return "Datei nicht gefunden", 404
try: try:
with open(path, 'r', encoding='utf-8') as f: with open(path, 'r', encoding='utf-8') as f:
current_config = json.load(f) current_config = json.load(f)
current_config['filename'] = filename current_config['filename'] = filename
current_filename = filename logger.info(f"Config geladen: {filename}")
logger.info(f"Konfiguration geladen: {filename}")
return redirect(url_for('control_page')) return redirect(url_for('control_page'))
except Exception as e: except Exception as e:
logger.error(f"Fehler beim Laden der Config {filename}: {e}") logger.error(f"Config-Ladefehler {filename}: {e}")
return "Fehler beim Laden der Konfiguration", 500 return "Fehler beim Laden", 500
@app.route('/control') @app.route('/control')
def control_page(): def control_page():
if current_config is None: if current_config is None:
return redirect(url_for('index')) return redirect(url_for('index'))
return render_template('control.html', config=current_config) 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']) @app.route('/api/connect', methods=['POST'])
def api_connect(): def api_connect():
# Hier kommt später der echte Code mit MouldKing global current_hub
# Aktuell nur simuliert
if current_config is None: if current_config is None:
return jsonify({"success": False, "message": "Keine Konfiguration geladen"}), 400 return jsonify({"success": False, "message": "Keine Konfiguration geladen"}), 400
logger.info(f"Simulierter Connect zu Hub {current_config.get('hub_id')}") try:
return jsonify({ hub_id = int(current_config.get('hub_id', 0))
"success": True,
"message": "Verbunden (Simulation)", # Falls schon verbunden → erstmal trennen (sauberer Neustart)
"hub_id": current_config.get('hub_id') 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']) @app.route('/api/control', methods=['POST'])
def api_control(): def api_control():
# Platzhalter für spätere echte Steuerung global current_hub
data = request.get_json() if current_hub is None:
logger.info(f"Steuerbefehl empfangen: {data}") return jsonify({"success": False, "message": "Nicht verbunden"}), 400
return jsonify({"success": True, "message": "Befehl gesendet (Simulation)"})
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']) @app.route('/api/stop_all', methods=['POST'])
def api_stop_all(): def api_stop_all():
logger.info("Alle stoppen (Simulation)") global current_hub
return jsonify({"success": True, "message": "Alle Kanäle gestoppt"}) if current_hub is None:
return jsonify({"success": False, "message": "Nicht verbunden"}), 400
@app.route('/admin') try:
def admin(): current_hub.stop_all() # ← oder alle Kanäle manuell auf 0 setzen
return render_template('admin.html') 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__': 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