Skip to content

Commit 2c13fdd

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

File tree

3 files changed

+86
-2
lines changed

3 files changed

+86
-2
lines changed

core/services/wifi/settings.py

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,3 +24,26 @@ def migrate(self, data: Dict[str, Any]) -> None:
2424
super().migrate(data)
2525

2626
data["VERSION"] = SettingsV1.VERSION
27+
28+
29+
class SettingsV2(SettingsV1):
30+
VERSION = 2
31+
# Stores user-selected mode per interface: {"wlan0": "normal", "wlan1": "hotspot"}
32+
interface_modes = pykson.ObjectField(dict, default_value={})
33+
34+
def __init__(self, *args: str, **kwargs: int) -> None:
35+
super().__init__(*args, **kwargs)
36+
self.VERSION = SettingsV2.VERSION
37+
38+
def migrate(self, data: Dict[str, Any]) -> None:
39+
if data["VERSION"] == SettingsV2.VERSION:
40+
return
41+
42+
if data["VERSION"] < SettingsV2.VERSION:
43+
super().migrate(data)
44+
45+
# Migration from version 1 to 2: add interface_modes
46+
if "interface_modes" not in data:
47+
data["interface_modes"] = {}
48+
49+
data["VERSION"] = SettingsV2.VERSION

core/services/wifi/wifi_handlers/AbstractWifiHandler.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@
33
from typing import List, Optional
44

55
from commonwealth.settings.manager import Manager
6-
from settings import SettingsV1
6+
from settings import SettingsV2
77
from typedefs import SavedWifiNetwork, ScannedWifiNetwork, WifiCredentials, WifiStatus
88
from wifi_handlers.wpa_supplicant.wpa_supplicant import WPASupplicant
99

@@ -12,7 +12,7 @@ class AbstractWifiManager:
1212
wpa = WPASupplicant()
1313

1414
def __init__(self) -> None:
15-
self._settings_manager = Manager("wifi-manager", SettingsV1)
15+
self._settings_manager = Manager("wifi-manager", SettingsV2)
1616
self._settings_manager.load()
1717

1818
@abc.abstractmethod

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

Lines changed: 61 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -104,6 +104,60 @@ def __init__(self) -> None:
104104
self._nm_settings = NetworkManagerSettings(self._bus)
105105
logger.info("NetworkManagerWifi initialized")
106106

107+
def _save_interface_mode(self, interface: str, mode: WifiInterfaceMode) -> None:
108+
"""Persist the interface mode to settings."""
109+
interface_modes = getattr(self._settings_manager.settings, "interface_modes", {}) or {}
110+
interface_modes[interface] = mode.value
111+
self._settings_manager.settings.interface_modes = interface_modes
112+
self._settings_manager.save()
113+
logger.info(f"Saved {interface} mode: {mode.value}")
114+
115+
def _get_stored_interface_mode(self, interface: str) -> Optional[WifiInterfaceMode]:
116+
"""Get the stored mode for an interface from settings, or None if not set."""
117+
interface_modes = getattr(self._settings_manager.settings, "interface_modes", {}) or {}
118+
mode_str = interface_modes.get(interface)
119+
if mode_str:
120+
try:
121+
return WifiInterfaceMode(mode_str)
122+
except ValueError:
123+
logger.warning(f"Invalid stored mode '{mode_str}' for {interface}")
124+
return None
125+
126+
async def get_default_mode_for_interface(self, interface: str) -> WifiInterfaceMode:
127+
"""Get the default mode for an interface based on stored setting or capabilities."""
128+
caps = await self.get_interface_capabilities(interface)
129+
130+
# First check if user has explicitly set a mode
131+
stored_mode = self._get_stored_interface_mode(interface)
132+
if stored_mode is not None:
133+
# Verify the stored mode is still supported by this hardware
134+
if stored_mode in caps.available_modes:
135+
return stored_mode
136+
logger.warning(f"Stored mode {stored_mode} no longer supported on {interface}, using default")
137+
138+
# No stored preference or stored mode not supported - determine based on capabilities
139+
if caps.supports_dual_mode:
140+
return WifiInterfaceMode.DUAL
141+
return WifiInterfaceMode.NORMAL
142+
143+
async def restore_interface_modes(self) -> None:
144+
"""Restore interface modes from settings on startup."""
145+
for interface in list(self._device_paths.keys()):
146+
try:
147+
default_mode = await self.get_default_mode_for_interface(interface)
148+
current_mode = self._interface_modes.get(interface, WifiInterfaceMode.NORMAL)
149+
if default_mode != current_mode:
150+
logger.info(f"Restoring {interface} to {default_mode} mode")
151+
success = await self.set_interface_mode(interface, default_mode)
152+
if not success:
153+
logger.warning(f"Failed to restore {interface} to {default_mode} mode, defaulting to NORMAL")
154+
self._interface_modes[interface] = WifiInterfaceMode.NORMAL
155+
else:
156+
self._interface_modes[interface] = default_mode
157+
except Exception as e:
158+
logger.error(f"Error restoring mode for {interface}: {e}")
159+
self._interface_modes[interface] = WifiInterfaceMode.NORMAL
160+
107161
async def _set_interface_managed(self, interface: str, managed: bool) -> None:
108162
"""Set the NetworkManager 'Managed' property for an interface via D-Bus."""
109163
device_path = self._device_paths.get(interface)
@@ -259,6 +313,10 @@ async def start(self) -> None:
259313

260314
# Create virtual AP interface if needed
261315
await self._create_virtual_interface()
316+
317+
# Restore interface modes from settings
318+
await self.restore_interface_modes()
319+
262320
self._tasks.append(asyncio.get_event_loop().create_task(self._autoscan()))
263321
self._tasks.append(asyncio.get_event_loop().create_task(self.hotspot_watchdog()))
264322

@@ -1102,6 +1160,7 @@ async def set_interface_mode(self, interface: str, mode: WifiInterfaceMode) -> b
11021160
if mode == WifiInterfaceMode.NORMAL:
11031161
# Just disable hotspot (already done above), interface returns to normal
11041162
self._interface_modes[interface] = mode
1163+
self._save_interface_mode(interface, mode)
11051164
return True
11061165

11071166
if mode == WifiInterfaceMode.HOTSPOT:
@@ -1114,13 +1173,15 @@ async def set_interface_mode(self, interface: str, mode: WifiInterfaceMode) -> b
11141173
success = await self._enable_hotspot_direct(interface)
11151174
if success:
11161175
self._interface_modes[interface] = mode
1176+
self._save_interface_mode(interface, mode)
11171177
return success
11181178

11191179
# mode == WifiInterfaceMode.DUAL
11201180
# Enable hotspot using virtual interface (existing behavior)
11211181
success = await self.enable_hotspot_on_interface(interface, save_settings=True)
11221182
if success:
11231183
self._interface_modes[interface] = mode
1184+
self._save_interface_mode(interface, mode)
11241185
return success
11251186

11261187
async def _enable_hotspot_direct(self, interface: str, save_settings: bool = True) -> bool:

0 commit comments

Comments
 (0)