Skip to main content

Programme - API Bouchon

"""
parc-stub · Flask control API

GET  /api/state                     → full state snapshot
POST /api/start                     → start publishing
POST /api/stop                      → stop publishing
POST /api/config                    → update interval & bad_rate
POST /api/serres                    → replace serres/bacs layout
POST /api/serre/<int>/connect       → manual connect controleur
POST /api/serre/<int>/disconnect    → manual disconnect controleur
GET  /                              → Web UI
"""

import logging
from flask import Flask, jsonify, render_template, request
import stub

logging.basicConfig(level=logging.INFO, format="%(asctime)s %(levelname)s %(message)s")
app = Flask(__name__)


# ── State ─────────────────────────────────────────────────────────────────────
@app.get("/api/state")
def api_state():
    with stub.state_lock:
        return jsonify(dict(stub.state))


# ── Start / Stop ──────────────────────────────────────────────────────────────
@app.post("/api/start")
def api_start():
    stub.start()
    return jsonify({"ok": True})

@app.post("/api/stop")
def api_stop():
    stub.stop()
    return jsonify({"ok": True})


# ── Global config ─────────────────────────────────────────────────────────────
@app.post("/api/config")
def api_config():
    data = request.get_json(force=True)
    with stub.state_lock:
        if "interval" in data:
            v = float(data["interval"])
            if v < 1:
                return jsonify({"error": "interval min 1s"}), 400
            stub.state["interval"] = v
        if "bad_rate" in data:
            v = float(data["bad_rate"])
            if not 0.0 <= v <= 1.0:
                return jsonify({"error": "bad_rate 0–1"}), 400
            stub.state["bad_rate"] = v
    stub.save_config()
    return jsonify({"ok": True})


# ── Serres layout ─────────────────────────────────────────────────────────────
@app.post("/api/serres")
def api_serres():
    """
    Body: [
      {
        "numero": 1,
        "mac": "24:D7:EB:38:DC:38",
        "ip": "192.168.1.101",
        "disconnect_rate": 0.05,
        "bacs": [{"numero": 1}, {"numero": 2}]
      }, …
    ]
    """
    data = request.get_json(force=True)
    if not isinstance(data, list):
        return jsonify({"error": "liste attendue"}), 400
    for s in data:
        for field in ("numero", "mac", "ip", "bacs"):
            if field not in s:
                return jsonify({"error": f"champ manquant : {field}"}), 400
        for b in s["bacs"]:
            if "numero" not in b:
                return jsonify({"error": "chaque bac doit avoir numero"}), 400

    with stub.state_lock:
        # preserve connected state for existing serres
        existing = {s["numero"]: s.get("connected", False) for s in stub.state["serres"]}
        for s in data:
            s["connected"] = existing.get(s["numero"], False)
            s.setdefault("disconnect_rate", 0.0)
        stub.state["serres"] = data
    stub.save_config()
    return jsonify({"ok": True})


# ── Controleur connect / disconnect ───────────────────────────────────────────
@app.post("/api/serre/<int:numero>/connect")
def api_connect(numero: int):
    stub.connect_serre(numero)
    return jsonify({"ok": True})

@app.post("/api/serre/<int:numero>/disconnect")
def api_disconnect(numero: int):
    stub.disconnect_serre(numero)
    return jsonify({"ok": True})


# ── UI ────────────────────────────────────────────────────────────────────────
@app.get("/")
def index():
    return render_template("index.html")


if __name__ == "__main__":
    app.run(host="0.0.0.0", port=5000, debug=False)