"""REST API – board webhook receiver and JSON relay control.""" from datetime import datetime from flask import Blueprint, request, jsonify, abort, current_app from flask_login import login_required from app import db, socketio from app.models.board import Board from app.services import workflow_engine from app.services.board_service import poll_board api_bp = Blueprint("api", __name__) # ── webhook endpoint (boards POST input events here) ────────────────────────── @api_bp.route("/webhook/", methods=["POST"]) def webhook(board_id: int): board = db.session.get(Board, board_id) if board is None: abort(404) data = request.get_json(silent=True) or {} input_num = data.get("input") state = data.get("state") if input_num is None or state is None: return jsonify({"error": "missing input or state"}), 400 # Update cached input state states = board.input_states states[f"input_{input_num}"] = bool(state) board.input_states = states board.is_online = True board.last_seen = datetime.utcnow() db.session.commit() # Let the workflow engine decide what to do workflow_engine.process_input_event(board_id, int(input_num), bool(state)) # Push live update to all connected clients immediately socketio.emit("board_update", { "board_id": board_id, "is_online": True, "input_states": board.input_states, "relay_states": board.relay_states, }) return jsonify({"status": "ok"}) # ── Manual ping / status check ─────────────────────────────────────────────── @api_bp.route("/boards//ping", methods=["POST"]) @login_required def ping_board(board_id: int): """Trigger an immediate poll for any board (online or offline). Called by the 'Check Status' button in the UI. The poll result is both returned as JSON *and* broadcast via socket.io so all open tabs update. """ db.get_or_404(Board, board_id) # 404 if unknown poll_board(current_app._get_current_object(), board_id) board = db.session.get(Board, board_id) return jsonify({ "board_id": board.id, "is_online": board.is_online, "relay_states": board.relay_states, "input_states": board.input_states, "last_seen": board.last_seen.isoformat() if board.last_seen else None, }) # ── JSON relay status ───────────────────────────────────────────────────────── @api_bp.route("/boards//relays") def relay_states(board_id: int): board = db.get_or_404(Board, board_id) return jsonify({ "board_id": board.id, "name": board.name, "is_online": board.is_online, "relay_states": board.relay_states, "input_states": board.input_states, }) # ── JSON board list ─────────────────────────────────────────────────────────── @api_bp.route("/boards") def board_list(): boards = Board.query.order_by(Board.name).all() return jsonify([ { "id": b.id, "name": b.name, "board_type": b.board_type, "host": b.host, "port": b.port, "is_online": b.is_online, "last_seen": b.last_seen.isoformat() if b.last_seen else None, "relay_states": b.relay_states, "input_states": b.input_states, } for b in boards ])