🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
320 lines
8.2 KiB
Python
320 lines
8.2 KiB
Python
"""WiFi management API endpoints.
|
|
|
|
Refactored to use WiFiService for all business logic.
|
|
Endpoints are now thin adapters that convert HTTP requests/responses.
|
|
"""
|
|
from fastapi import APIRouter, HTTPException
|
|
from pydantic import BaseModel
|
|
from typing import List, Optional
|
|
|
|
from ..services.container import container
|
|
|
|
router = APIRouter()
|
|
|
|
|
|
class WiFiStatusResponse(BaseModel):
|
|
"""WiFi status response."""
|
|
mode: str
|
|
interfaces: List[dict]
|
|
current_ssid: Optional[str]
|
|
current_ip: Optional[str]
|
|
ap_ssid: Optional[str]
|
|
ap_ip: Optional[str]
|
|
supports_dual: bool
|
|
|
|
|
|
class WiFiNetworkResponse(BaseModel):
|
|
"""WiFi network response."""
|
|
ssid: str
|
|
bssid: str
|
|
signal_strength: int
|
|
frequency: int
|
|
encrypted: bool
|
|
in_use: bool
|
|
|
|
|
|
class SetModeRequest(BaseModel):
|
|
"""Request to set WiFi mode."""
|
|
mode: str # "ap", "client", "dual", "auto", "off"
|
|
|
|
|
|
class ConnectRequest(BaseModel):
|
|
"""Request to connect to network."""
|
|
ssid: str
|
|
password: Optional[str] = None
|
|
interface: Optional[str] = None
|
|
hidden: bool = False
|
|
|
|
|
|
def _service_error_to_http_status(error_code: str) -> int:
|
|
"""Map service error codes to HTTP status codes.
|
|
|
|
Args:
|
|
error_code: Service error code
|
|
|
|
Returns:
|
|
HTTP status code
|
|
"""
|
|
codes = {
|
|
"wifi_status_error": 500,
|
|
"wifi_scan_error": 500,
|
|
"connection_failed": 503,
|
|
"wifi_connect_error": 500,
|
|
"disconnect_failed": 500,
|
|
"wifi_disconnect_error": 500,
|
|
"invalid_mode": 400,
|
|
"mode_not_supported": 400,
|
|
"mode_change_failed": 500,
|
|
"wifi_mode_error": 500,
|
|
"saved_networks_error": 500,
|
|
"network_not_found": 404,
|
|
"forget_network_error": 500,
|
|
}
|
|
return codes.get(error_code, 500)
|
|
|
|
|
|
@router.get("/status", response_model=WiFiStatusResponse)
|
|
async def get_wifi_status():
|
|
"""Get current WiFi status and available interfaces.
|
|
|
|
Uses WiFiService for business logic.
|
|
"""
|
|
result = await container.wifi_service.get_status()
|
|
|
|
if not result.success:
|
|
raise HTTPException(
|
|
status_code=_service_error_to_http_status(result.error.code),
|
|
detail=result.error.message
|
|
)
|
|
|
|
data = result.data
|
|
return WiFiStatusResponse(
|
|
mode=data["mode"],
|
|
interfaces=data["interfaces"],
|
|
current_ssid=data["client"]["ssid"] if data.get("client") else None,
|
|
current_ip=data["client"]["ip"] if data.get("client") else None,
|
|
ap_ssid=data["access_point"]["ssid"],
|
|
ap_ip=data["access_point"]["ip"],
|
|
supports_dual=data["supports_dual"]
|
|
)
|
|
|
|
|
|
@router.get("/scan", response_model=List[WiFiNetworkResponse])
|
|
async def scan_networks(interface: Optional[str] = None):
|
|
"""Scan for available WiFi networks.
|
|
|
|
Uses WiFiService for business logic.
|
|
|
|
Args:
|
|
interface: Optional interface to scan with
|
|
"""
|
|
result = await container.wifi_service.scan_networks(interface)
|
|
|
|
if not result.success:
|
|
raise HTTPException(
|
|
status_code=_service_error_to_http_status(result.error.code),
|
|
detail=result.error.message
|
|
)
|
|
|
|
return [
|
|
WiFiNetworkResponse(**net)
|
|
for net in result.data["networks"]
|
|
]
|
|
|
|
|
|
@router.post("/mode")
|
|
async def set_wifi_mode(request: SetModeRequest):
|
|
"""Set WiFi operation mode.
|
|
|
|
Uses WiFiService for business logic.
|
|
|
|
Args:
|
|
request: Mode to set (ap, client, dual, auto, off)
|
|
"""
|
|
result = await container.wifi_service.set_mode(request.mode)
|
|
|
|
if not result.success:
|
|
raise HTTPException(
|
|
status_code=_service_error_to_http_status(result.error.code),
|
|
detail=result.error.message
|
|
)
|
|
|
|
return {
|
|
"success": True,
|
|
"message": result.data["message"],
|
|
"mode": result.data["mode"]
|
|
}
|
|
|
|
|
|
@router.post("/connect")
|
|
async def connect_to_network(request: ConnectRequest):
|
|
"""Connect to a WiFi network.
|
|
|
|
Uses WiFiService for business logic.
|
|
|
|
Args:
|
|
request: Connection details (SSID, password, interface, hidden)
|
|
"""
|
|
result = await container.wifi_service.connect(
|
|
ssid=request.ssid,
|
|
password=request.password,
|
|
interface=request.interface,
|
|
hidden=request.hidden
|
|
)
|
|
|
|
if not result.success:
|
|
raise HTTPException(
|
|
status_code=_service_error_to_http_status(result.error.code),
|
|
detail=result.error.message
|
|
)
|
|
|
|
return {
|
|
"success": True,
|
|
"message": result.data["message"],
|
|
"ssid": result.data["ssid"],
|
|
"ip": result.data.get("ip")
|
|
}
|
|
|
|
|
|
@router.get("/interfaces")
|
|
async def get_interfaces():
|
|
"""Get available WiFi interfaces.
|
|
|
|
Uses WiFiService for business logic.
|
|
"""
|
|
result = await container.wifi_service.get_status()
|
|
|
|
if not result.success:
|
|
raise HTTPException(
|
|
status_code=_service_error_to_http_status(result.error.code),
|
|
detail=result.error.message
|
|
)
|
|
|
|
return {
|
|
"interfaces": result.data["interfaces"],
|
|
"supports_dual": result.data["supports_dual"]
|
|
}
|
|
|
|
|
|
@router.post("/disconnect")
|
|
async def disconnect_from_network(interface: Optional[str] = None):
|
|
"""Disconnect from current network.
|
|
|
|
Uses WiFiService for business logic.
|
|
|
|
Args:
|
|
interface: Interface to disconnect (optional)
|
|
"""
|
|
result = await container.wifi_service.disconnect(interface)
|
|
|
|
if not result.success:
|
|
raise HTTPException(
|
|
status_code=_service_error_to_http_status(result.error.code),
|
|
detail=result.error.message
|
|
)
|
|
|
|
return {
|
|
"success": True,
|
|
"message": result.data["message"]
|
|
}
|
|
|
|
|
|
@router.get("/saved")
|
|
async def get_saved_networks():
|
|
"""Get list of saved networks.
|
|
|
|
Uses WiFiService for business logic.
|
|
"""
|
|
result = await container.wifi_service.get_saved_networks()
|
|
|
|
if not result.success:
|
|
raise HTTPException(
|
|
status_code=_service_error_to_http_status(result.error.code),
|
|
detail=result.error.message
|
|
)
|
|
|
|
return {"networks": result.data["networks"]}
|
|
|
|
|
|
@router.delete("/saved/{ssid}")
|
|
async def forget_network(ssid: str):
|
|
"""Forget a saved network.
|
|
|
|
Uses WiFiService for business logic.
|
|
|
|
Args:
|
|
ssid: SSID of network to forget
|
|
"""
|
|
result = await container.wifi_service.forget_network(ssid)
|
|
|
|
if not result.success:
|
|
raise HTTPException(
|
|
status_code=_service_error_to_http_status(result.error.code),
|
|
detail=result.error.message
|
|
)
|
|
|
|
return {
|
|
"success": True,
|
|
"message": result.data["message"]
|
|
}
|
|
|
|
|
|
@router.post("/static-ip")
|
|
async def set_static_ip(
|
|
interface: str,
|
|
ip_address: str,
|
|
netmask: str = "255.255.255.0",
|
|
gateway: Optional[str] = None,
|
|
dns: Optional[List[str]] = None,
|
|
):
|
|
"""Set static IP for interface.
|
|
|
|
NOTE: This is a direct manager operation (not yet in WiFiService).
|
|
TODO: Move to WiFiService in future iteration.
|
|
|
|
Args:
|
|
interface: Interface name
|
|
ip_address: Static IP address
|
|
netmask: Network mask (default: 255.255.255.0)
|
|
gateway: Gateway IP (optional)
|
|
dns: DNS servers (optional)
|
|
"""
|
|
try:
|
|
success = await container.wifi_manager.set_static_ip(
|
|
interface, ip_address, netmask, gateway, dns
|
|
)
|
|
|
|
if not success:
|
|
raise HTTPException(status_code=500, detail="Failed to set static IP")
|
|
|
|
return {
|
|
"success": True,
|
|
"message": f"Static IP {ip_address} set for {interface}",
|
|
}
|
|
except Exception as e:
|
|
raise HTTPException(status_code=500, detail=f"Failed to set static IP: {e}")
|
|
|
|
|
|
@router.post("/dhcp")
|
|
async def enable_dhcp(interface: str):
|
|
"""Enable DHCP for interface.
|
|
|
|
NOTE: This is a direct manager operation (not yet in WiFiService).
|
|
TODO: Move to WiFiService in future iteration.
|
|
|
|
Args:
|
|
interface: Interface name
|
|
"""
|
|
try:
|
|
success = await container.wifi_manager.enable_dhcp(interface)
|
|
|
|
if not success:
|
|
raise HTTPException(status_code=500, detail="Failed to enable DHCP")
|
|
|
|
return {
|
|
"success": True,
|
|
"message": f"DHCP enabled for {interface}",
|
|
}
|
|
except Exception as e:
|
|
raise HTTPException(status_code=500, detail=f"Failed to enable DHCP: {e}")
|