feat: add SocketIO server integration
- Initialize SocketIO with threading mode - Add WebSocket event handlers (connect, disconnect, subscribe_floor) - Add broadcast_scan_update() function for pushing scan results - Integrate broadcast with BT scan endpoint - Update run_server() to use socketio.run() Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
@@ -7,7 +7,8 @@ import time
|
||||
from datetime import datetime
|
||||
from pathlib import Path
|
||||
|
||||
from flask import Flask, jsonify, render_template, request
|
||||
from flask import Flask, current_app, jsonify, render_template, request
|
||||
from flask_socketio import SocketIO, emit
|
||||
|
||||
from ..scanner import RFScanner
|
||||
from ..distance import estimate_distance
|
||||
@@ -16,6 +17,29 @@ from ..bluetooth_identify import identify_single_device, identify_device
|
||||
from ..database import DeviceDatabase, init_database, get_database
|
||||
from ..homeassistant import HAWebhooks, HAWebhookConfig
|
||||
|
||||
# Module-level SocketIO instance
|
||||
socketio = SocketIO()
|
||||
|
||||
|
||||
def broadcast_scan_update(app: Flask, devices: list[dict], scan_type: str = "bluetooth"):
|
||||
"""Broadcast scan results to all connected WebSocket clients."""
|
||||
sio = app.config.get("SOCKETIO")
|
||||
if not sio:
|
||||
return
|
||||
|
||||
scanner_identity = app.config.get("SCANNER_IDENTITY", {})
|
||||
|
||||
sio.emit(
|
||||
"scan_update",
|
||||
{
|
||||
"type": scan_type,
|
||||
"timestamp": datetime.now().isoformat(),
|
||||
"scanner_id": scanner_identity.get("id", "unknown"),
|
||||
"devices": devices,
|
||||
},
|
||||
namespace="/ws/scan",
|
||||
)
|
||||
|
||||
|
||||
class AutoScanner:
|
||||
"""Background scanner that runs periodic scans"""
|
||||
@@ -213,6 +237,10 @@ def create_app(config: Config | None = None) -> Flask:
|
||||
# Store config reference
|
||||
app.config["RF_CONFIG"] = config
|
||||
|
||||
# Initialize SocketIO with threading mode (compatible with existing threads)
|
||||
socketio.init_app(app, cors_allowed_origins="*", async_mode="threading")
|
||||
app.config["SOCKETIO"] = socketio
|
||||
|
||||
# Data directory from config
|
||||
app.config["DATA_DIR"] = config.get_data_dir()
|
||||
app.config["DATA_DIR"].mkdir(parents=True, exist_ok=True)
|
||||
@@ -320,6 +348,31 @@ def create_app(config: Config | None = None) -> Flask:
|
||||
location_label=config.auto_scan.location_label
|
||||
)
|
||||
|
||||
# ==================== WebSocket Event Handlers ====================
|
||||
|
||||
@socketio.on("connect", namespace="/ws/scan")
|
||||
def ws_connect():
|
||||
"""Handle client connection."""
|
||||
scanner_id = current_app.config.get("SCANNER_IDENTITY", {}).get("id", "unknown")
|
||||
emit("connected", {"scanner_id": scanner_id})
|
||||
print(f"[WS] Client connected: {request.sid}")
|
||||
|
||||
@socketio.on("disconnect", namespace="/ws/scan")
|
||||
def ws_disconnect():
|
||||
"""Handle client disconnection."""
|
||||
print(f"[WS] Client disconnected: {request.sid}")
|
||||
|
||||
@socketio.on("subscribe_floor", namespace="/ws/scan")
|
||||
def ws_subscribe_floor(data):
|
||||
"""Subscribe to floor-specific updates."""
|
||||
from flask_socketio import join_room
|
||||
|
||||
floor = data.get("floor", "all")
|
||||
join_room(f"floor_{floor}")
|
||||
emit("subscribed", {"floor": floor})
|
||||
|
||||
# ==================== HTTP Routes ====================
|
||||
|
||||
@app.route("/")
|
||||
def index():
|
||||
"""Main dashboard page"""
|
||||
@@ -1072,6 +1125,9 @@ def create_app(config: Config | None = None) -> Flask:
|
||||
scan_type="bluetooth"
|
||||
)
|
||||
|
||||
# Broadcast to WebSocket clients
|
||||
broadcast_scan_update(current_app, response_data["bluetooth_devices"], "bluetooth")
|
||||
|
||||
return jsonify(response_data)
|
||||
|
||||
# ==================== Historical Data API ====================
|
||||
@@ -1476,10 +1532,12 @@ def run_server(
|
||||
if log_requests:
|
||||
print(f"Request logging: ENABLED")
|
||||
print(f"Log output: {config.get_data_dir() / 'logs'}")
|
||||
print(f"WebSocket: ENABLED (namespace /ws/scan)")
|
||||
print(f"{'='*60}")
|
||||
print(f"Server running at: http://{host}:{port}")
|
||||
print(f"Local access: http://localhost:{port}")
|
||||
print(f"Network access: http://<your-ip>:{port}")
|
||||
print(f"{'='*60}\n")
|
||||
|
||||
app.run(host=host, port=port, debug=debug)
|
||||
# Use socketio.run() for WebSocket support
|
||||
socketio.run(app, host=host, port=port, debug=debug, allow_unsafe_werkzeug=True)
|
||||
|
||||
Reference in New Issue
Block a user