"""Application entry point. Run with: python run.py Or for production: gunicorn -w 1 -k eventlet "run:create_socketio_app()" """ import os import threading import time from app import create_app, socketio from app.services.board_service import poll_online_boards, recheck_offline_boards app = create_app(os.environ.get("FLASK_ENV", "development")) def _online_poller(): """Fast loop — polls only online boards every BOARD_POLL_INTERVAL seconds.""" interval = app.config.get("BOARD_POLL_INTERVAL", 10) while True: try: poll_online_boards(app) except Exception as exc: app.logger.warning("Online poller error: %s", exc) time.sleep(interval) def _offline_recheck_poller(): """Slow loop — probes offline boards every OFFLINE_RECHECK_INTERVAL seconds. Waits one full interval before the first check so startup is clean. """ interval = app.config.get("OFFLINE_RECHECK_INTERVAL", 60) while True: time.sleep(interval) # wait first, then check try: recheck_offline_boards(app) except Exception as exc: app.logger.warning("Offline recheck error: %s", exc) def create_socketio_app(): """WSGI callable for gunicorn / production.""" return socketio.middleware(app) if __name__ == "__main__": # Werkzeug debug-reloader starts two processes; only start the poller in # the actual worker (WERKZEUG_RUN_MAIN=true) to avoid duplicate polling. # In non-debug mode (WERKZEUG_RUN_MAIN is unset) we always start it. if not app.debug or os.environ.get("WERKZEUG_RUN_MAIN") == "true": threading.Thread(target=_online_poller, daemon=True).start() threading.Thread(target=_offline_recheck_poller, daemon=True).start() port = int(os.environ.get("PORT", 5000)) socketio.run(app, host="0.0.0.0", port=port, debug=app.debug, allow_unsafe_werkzeug=True)