Skip to content

Commit e2f0c39

Browse files
wip
Signed-off-by: Patrick José Pereira <patrickelectric@gmail.com>
1 parent 2c76f00 commit e2f0c39

File tree

2 files changed

+79
-16
lines changed

2 files changed

+79
-16
lines changed

core/services/wifi/api/v2/routers/wifi.py

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33

44
from fastapi import APIRouter, HTTPException, status
55
from fastapi_versioning import versioned_api_route
6+
from loguru import logger
67
from typedefs import (
78
ConnectRequest,
89
DisconnectRequest,
@@ -426,9 +427,15 @@ async def set_interface_mode(request: SetInterfaceModeRequest) -> Dict[str, str]
426427
status_code=status.HTTP_400_BAD_REQUEST,
427428
detail=f"Failed to set mode {request.mode} on {request.interface}",
428429
)
430+
except HTTPException as he:
431+
logger.error(f"HTTPException in set_interface_mode: {he.detail}")
432+
raise
429433
except ValueError as e:
434+
logger.error(f"ValueError in set_interface_mode: {e}")
430435
raise HTTPException(status_code=status.HTTP_400_BAD_REQUEST, detail=str(e)) from e
431436
except Exception as e:
432-
raise HTTPException(status_code=status.HTTP_500_INTERNAL_SERVER_ERROR, detail=str(e)) from e
437+
error_msg = str(e) if str(e) else f"{type(e).__name__}: (no message)"
438+
logger.error(f"Exception in set_interface_mode: type={type(e).__name__}, msg={error_msg}")
439+
raise HTTPException(status_code=status.HTTP_500_INTERNAL_SERVER_ERROR, detail=error_msg) from e
433440

434441
return {"status": "success", "interface": request.interface, "mode": request.mode.value}

core/services/wifi/wifi_handlers/networkmanager/networkmanager.py

Lines changed: 71 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55
import select
66
import signal
77
import subprocess
8+
import time
89
from concurrent.futures import CancelledError
910
from 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

Comments
 (0)