55import select
66import signal
77import subprocess
8+ import time
89from concurrent .futures import CancelledError
910from typing import Any , Dict , List , Optional
1011
@@ -1002,8 +1003,11 @@ async def set_interface_mode(self, interface: str, mode: WifiInterfaceMode) -> b
10021003 return True
10031004
10041005 if mode == WifiInterfaceMode .HOTSPOT :
1005- # Disconnect from any network first
1006- await self .disconnect_interface (interface )
1006+ # Disconnect from any network first (ignore errors if not connected)
1007+ try :
1008+ await self .disconnect_interface (interface )
1009+ except Exception as e :
1010+ logger .debug (f"Disconnect before hotspot mode (expected if not connected): { e } " )
10071011 # Enable hotspot in "direct" mode (no virtual interface)
10081012 success = await self ._enable_hotspot_direct (interface )
10091013 if success :
@@ -1030,24 +1034,76 @@ async def _enable_hotspot_direct(self, interface: str, save_settings: bool = Tru
10301034 credentials = self .hotspot_credentials ()
10311035
10321036 # Use create_ap directly on the physical interface (no virtual interface)
1037+ cmd = [
1038+ "create_ap" ,
1039+ "-n" , # No internet sharing
1040+ "--freq-band" ,
1041+ "2.4" ,
1042+ interface , # Use physical interface directly
1043+ credentials .ssid ,
1044+ credentials .password ,
1045+ ]
1046+
10331047 try :
1034- cmd = [
1035- "create_ap" ,
1036- "--daemon" ,
1037- "-n" , # No internet sharing
1038- "--freq-band" ,
1039- "2.4" ,
1040- interface , # Use physical interface directly
1041- credentials .ssid ,
1042- credentials .password ,
1043- ]
1044- # pylint: disable-next=consider-using-with
1045- process = subprocess .Popen (cmd , stdout = subprocess .PIPE , stderr = subprocess .PIPE , text = True )
1048+ # Stop existing hotspot on this interface if running
1049+ if interface in self ._create_ap_processes :
1050+ existing_process = self ._create_ap_processes [interface ]
1051+ if existing_process .poll () is None :
1052+ logger .info (f"Stopping existing hotspot on { interface } ..." )
1053+ existing_process .terminate ()
1054+ existing_process .wait ()
1055+ del self ._create_ap_processes [interface ]
1056+
1057+ # pylint: disable=consider-using-with
1058+ process = subprocess .Popen (
1059+ cmd , stdout = subprocess .PIPE , stderr = subprocess .STDOUT , universal_newlines = True , bufsize = 1
1060+ )
1061+ # pylint: enable=consider-using-with
1062+
1063+ success = False
1064+ loop = asyncio .get_running_loop ()
1065+ start_time = loop .time ()
1066+ timeout = 30
1067+
1068+ while process .poll () is None :
1069+ if select .select ([process .stdout ], [], [], 0 )[0 ]:
1070+ assert process .stdout is not None
1071+ line = process .stdout .readline ()
1072+
1073+ line = line .strip ()
1074+ if line :
1075+ logger .info (f"create_ap ({ interface } direct): { line } " )
1076+ if "Done" in line or "AP-ENABLED" in line :
1077+ success = True
1078+ break
1079+ if "ERROR" in line :
1080+ logger .error (f"create_ap error on { interface } : { line } " )
1081+ return False
1082+
1083+ if loop .time () - start_time > timeout :
1084+ logger .error (f"Timeout waiting for create_ap on { interface } " )
1085+ return False
1086+
1087+ await asyncio .sleep (0.1 )
1088+
1089+ if not success :
1090+ exit_code = process .poll ()
1091+ logger .error (
1092+ f"Failed to start create_ap directly on { interface } , exit code: { exit_code } . "
1093+ "The adapter may report AP mode support but cannot actually run as an access point."
1094+ )
1095+ return False
1096+
1097+ logger .info (f"Successfully started create_ap directly on { interface } with PID { process .pid } " )
10461098 self ._create_ap_processes [interface ] = process
10471099 self ._hotspot_interface = interface
10481100 self ._create_ap_process = process # Backward compatibility
1049- logger .info (f"Started hotspot directly on { interface } with SSID { credentials .ssid } " )
1101+
1102+ self ._tasks .append (loop .create_task (self ._monitor_create_ap_output (process )))
1103+
1104+ logger .info (f"Hotspot enabled directly on { interface } " )
10501105 return True
1106+
10511107 except Exception as e :
10521108 logger .error (f"Failed to start direct hotspot on { interface } : { e } " )
10531109 return False
0 commit comments