mkcontrol-app/app.py

168 lines
5.9 KiB
Python
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

# app.py
import os
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__)
app.config['CONFIG_DIR'] = 'configs'
os.makedirs(app.config['CONFIG_DIR'], exist_ok=True)
# Logging
logging.basicConfig(
filename='app.log',
level=logging.INFO,
format='%(asctime)s | %(levelname)-8s | %(message)s',
datefmt='%Y-%m-%d %H:%M:%S'
)
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():
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
data.setdefault('name', filename.replace('.json', '').replace('_', ' ').title())
configs.append(data)
except Exception as e:
logger.error(f"Fehler beim Laden {filename}: {e}")
return sorted(configs, key=lambda x: x.get('name', ''))
@app.route('/')
def index():
return render_template('index.html', configs=load_configs())
@app.route('/load_config/<filename>')
def load_config(filename):
global current_config
path = os.path.join(app.config['CONFIG_DIR'], filename)
if not os.path.exists(path):
return "Datei nicht gefunden", 404
try:
with open(path, 'r', encoding='utf-8') as f:
current_config = json.load(f)
current_config['filename'] = filename
logger.info(f"Config geladen: {filename}")
return redirect(url_for('control_page'))
except Exception as e:
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)
@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():
global current_hub
if current_config is None:
return jsonify({"success": False, "message": "Keine Konfiguration geladen"}), 400
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():
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():
global current_hub
if current_hub is None:
return jsonify({"success": False, "message": "Nicht verbunden"}), 400
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=5000, debug=True)