"""WiFi Service Layer. This service provides WiFi management operations that can be consumed by multiple interfaces (REST API, BLE GATT, etc.). """ from typing import Optional, List, Dict, Any from ..managers.wifi_manager import WiFiManager, WiFiMode, WiFiStatus, WiFiNetwork from .pm3_service import PM3ServiceError, PM3ServiceResult class WiFiService: """WiFi service layer for network management. This service can be used by multiple interfaces: - REST API (FastAPI endpoints) - BLE GATT (Bluetooth handlers) - Plugin system (future) It encapsulates: - Network scanning - Connection management - WiFi mode switching - Status queries """ def __init__(self, wifi_manager: Optional[WiFiManager] = None): """Initialize WiFi service. Args: wifi_manager: WiFi manager instance (creates new if not provided) """ self.wifi_manager = wifi_manager or WiFiManager() async def get_status(self) -> PM3ServiceResult: """Get current WiFi status. Returns: PM3ServiceResult with WiFi status (mode, interfaces, connections) """ try: status = await self.wifi_manager.get_status() return PM3ServiceResult( success=True, data={ "mode": status.mode.value, "interfaces": [ { "name": iface.name, "mac": iface.mac, "is_usb": iface.is_usb, "is_up": iface.is_up, "connected": iface.connected, "ssid": iface.ssid, "ip_address": iface.ip_address, "mode": iface.mode # "AP" or "managed" } for iface in status.interfaces ], "client": { "ssid": status.current_ssid, "ip": status.current_ip } if status.current_ssid else None, "access_point": { "ssid": status.ap_ssid, "ip": status.ap_ip }, "supports_dual": status.supports_dual } ) except Exception as e: return PM3ServiceResult( success=False, error=PM3ServiceError( code="wifi_status_error", message="Failed to get WiFi status", details=str(e) ) ) async def scan_networks( self, interface: Optional[str] = None ) -> PM3ServiceResult: """Scan for available WiFi networks. Args: interface: Interface to scan with (default: auto-select) Returns: PM3ServiceResult with list of available networks """ try: networks = await self.wifi_manager.scan_networks(interface) return PM3ServiceResult( success=True, data={ "networks": [ { "ssid": net.ssid, "bssid": net.bssid, "signal_strength": net.signal_strength, "frequency": net.frequency, "encrypted": net.encrypted, "in_use": net.in_use } for net in networks ], "count": len(networks) } ) except Exception as e: return PM3ServiceResult( success=False, error=PM3ServiceError( code="wifi_scan_error", message="Failed to scan for networks", details=str(e) ) ) async def connect( self, ssid: str, password: Optional[str] = None, interface: Optional[str] = None, hidden: bool = False ) -> PM3ServiceResult: """Connect to a WiFi network. Args: ssid: Network SSID password: Network password (if encrypted) interface: Interface to use (default: auto-select) hidden: Whether network is hidden Returns: PM3ServiceResult indicating connection success/failure """ try: success = await self.wifi_manager.connect_to_network( ssid=ssid, password=password, interface=interface, hidden=hidden ) if success: # Get updated status to include new connection info status = await self.wifi_manager.get_status() return PM3ServiceResult( success=True, data={ "message": f"Successfully connected to {ssid}", "ssid": ssid, "ip": status.current_ip } ) else: return PM3ServiceResult( success=False, error=PM3ServiceError( code="connection_failed", message=f"Failed to connect to {ssid}", details="Connection attempt failed or timed out" ) ) except Exception as e: return PM3ServiceResult( success=False, error=PM3ServiceError( code="wifi_connect_error", message=f"Error connecting to {ssid}", details=str(e) ) ) async def disconnect( self, interface: Optional[str] = None ) -> PM3ServiceResult: """Disconnect from current network. Args: interface: Interface to disconnect (default: first connected) Returns: PM3ServiceResult indicating success/failure """ try: success = await self.wifi_manager.disconnect_from_network(interface) if success: return PM3ServiceResult( success=True, data={"message": "Disconnected from network"} ) else: return PM3ServiceResult( success=False, error=PM3ServiceError( code="disconnect_failed", message="Failed to disconnect", details="No active connection found or disconnect failed" ) ) except Exception as e: return PM3ServiceResult( success=False, error=PM3ServiceError( code="wifi_disconnect_error", message="Error disconnecting from network", details=str(e) ) ) async def set_mode(self, mode: str) -> PM3ServiceResult: """Set WiFi operation mode. Args: mode: WiFi mode ("ap", "client", "dual", "auto", "off") Returns: PM3ServiceResult indicating success/failure """ try: # Validate and convert mode string to enum try: wifi_mode = WiFiMode(mode) except ValueError: return PM3ServiceResult( success=False, error=PM3ServiceError( code="invalid_mode", message=f"Invalid WiFi mode: {mode}", details=f"Valid modes: ap, client, dual, auto, off" ) ) success = await self.wifi_manager.set_mode(wifi_mode) if success: return PM3ServiceResult( success=True, data={ "message": f"WiFi mode set to {mode}", "mode": mode } ) else: return PM3ServiceResult( success=False, error=PM3ServiceError( code="mode_change_failed", message=f"Failed to set WiFi mode to {mode}", details="Mode change operation failed" ) ) except ValueError as e: # Handle dual mode without USB adapter return PM3ServiceResult( success=False, error=PM3ServiceError( code="mode_not_supported", message=str(e), details="Dual mode requires USB WiFi adapter" ) ) except Exception as e: return PM3ServiceResult( success=False, error=PM3ServiceError( code="wifi_mode_error", message="Error setting WiFi mode", details=str(e) ) ) async def get_saved_networks(self) -> PM3ServiceResult: """Get list of saved networks. Returns: PM3ServiceResult with list of saved networks """ try: networks = await self.wifi_manager.get_saved_networks() return PM3ServiceResult( success=True, data={ "networks": networks, "count": len(networks) } ) except Exception as e: return PM3ServiceResult( success=False, error=PM3ServiceError( code="saved_networks_error", message="Failed to get saved networks", details=str(e) ) ) async def forget_network(self, ssid: str) -> PM3ServiceResult: """Forget a saved network. Args: ssid: SSID of network to forget Returns: PM3ServiceResult indicating success/failure """ try: success = await self.wifi_manager.forget_network(ssid) if success: return PM3ServiceResult( success=True, data={ "message": f"Network '{ssid}' forgotten", "ssid": ssid } ) else: return PM3ServiceResult( success=False, error=PM3ServiceError( code="network_not_found", message=f"Network '{ssid}' not found in saved networks" ) ) except Exception as e: return PM3ServiceResult( success=False, error=PM3ServiceError( code="forget_network_error", message=f"Error forgetting network '{ssid}'", details=str(e) ) )