""" FastAPI router for WiFi management endpoints. """ from fastapi import APIRouter, HTTPException, Request from fastapi.responses import HTMLResponse from pydantic import BaseModel from typing import Optional import logging from . import manager logger = logging.getLogger(__name__) router = APIRouter(prefix="/api/wifi", tags=["wifi"]) # Separate router for the captive portal handler (no prefix) captive_portal_router = APIRouter(tags=["wifi"]) class WiFiConnectRequest(BaseModel): ssid: str password: Optional[str] = "" class WiFiForgetRequest(BaseModel): ssid: str class HotspotPasswordRequest(BaseModel): password: Optional[str] = "" @router.get("/status") async def wifi_status(): """Get current WiFi mode, connection, and IP.""" return manager.get_wifi_status() @router.get("/networks") async def wifi_networks(): """Scan for available WiFi networks.""" return manager.scan_networks() @router.post("/connect") async def wifi_connect(req: WiFiConnectRequest): """Connect to a WiFi network and reboot.""" if not req.ssid: raise HTTPException(status_code=400, detail="SSID is required") result = await manager.connect_to_network(req.ssid, req.password or "") if not result["success"]: raise HTTPException(status_code=400, detail=result["message"]) return result @router.post("/save") async def wifi_save(req: WiFiConnectRequest): """Save a WiFi network without connecting.""" if not req.ssid: raise HTTPException(status_code=400, detail="SSID is required") result = manager.save_network(req.ssid, req.password or "") if not result["success"]: raise HTTPException(status_code=400, detail=result["message"]) return result @router.post("/forget") async def wifi_forget(req: WiFiForgetRequest): """Forget a saved WiFi network.""" if not req.ssid: raise HTTPException(status_code=400, detail="SSID is required") result = manager.forget_network(req.ssid) if not result["success"]: raise HTTPException(status_code=400, detail=result["message"]) return result @router.get("/saved") async def wifi_saved(): """Get list of saved WiFi connections.""" return manager.get_saved_connections() @router.get("/hotspot/password") async def get_hotspot_password(): """Get the current hotspot password.""" return manager.get_hotspot_password() @router.post("/hotspot/password") async def set_hotspot_password(req: HotspotPasswordRequest): """Set or remove the hotspot password.""" result = manager.set_hotspot_password(req.password or "") if not result["success"]: raise HTTPException(status_code=400, detail=result["message"]) return result # --- Captive portal detection endpoints --- # These handle the well-known URLs that phones/tablets probe to detect captive portals. # In hotspot mode, DNS redirects all domains to the Pi, so these probes arrive here. # We serve a minimal HTML page that redirects to the WiFi setup page. CAPTIVE_PORTAL_HTML = """
What would you like to do?
http://10.42.0.1