feat(network): wlan0 fallback if T3U dongle is absent

- PRIMARY_INTERFACE=wlan1, FALLBACK_INTERFACE=wlan0
- resolve_interface() checks /sys/class/net at startup and picks whichever exists
- INTERFACE is module-level, so all functions use the correct iface automatically
- restore_fallback() now also tries home-wifi-fb / Hotspot on wlan0 if wlan1 is primary but failed
- install_netwatch.sh creates home-wifi-fb profile on wlan0 (lower autoconnect-priority=5)
- wlan0 stays managed=yes (fallback) instead of being hard-disabled
- Status JSON now includes 'interface' field for diagnostics

Co-authored-by: Copilot <[email protected]>
This commit is contained in:
Mathieu Moran
2026-08-10 02:35:06 -04:00
co-authored by Copilot
parent e533f52e78
commit 908e86d169
2 changed files with 54 additions and 4 deletions
+32 -1
View File
@@ -12,7 +12,8 @@ import uuid
PHONE_CONNECTION = "phone-hotspot"
HOME_CONNECTION = "home-wifi"
AP_CONNECTION = "Hotspot"
INTERFACE = "wlan1" # TP-Link T3U dongle — all client + AP traffic
PRIMARY_INTERFACE = "wlan1" # TP-Link T3U dongle — preferred
FALLBACK_INTERFACE = "wlan0" # onboard — used if T3U is absent
STATUS_PATH = "/var/lib/yaris-obd/uplink-status.json"
PHONE_KEYFILE = (
"/etc/NetworkManager/system-connections/"
@@ -24,6 +25,24 @@ class UplinkError(RuntimeError):
pass
def _interface_exists(iface):
"""Return True if the kernel sees this network interface."""
return os.path.exists(f"/sys/class/net/{iface}")
def resolve_interface():
"""Return PRIMARY_INTERFACE if it exists, else FALLBACK_INTERFACE."""
if _interface_exists(PRIMARY_INTERFACE):
return PRIMARY_INTERFACE
if _interface_exists(FALLBACK_INTERFACE):
return FALLBACK_INTERFACE
return PRIMARY_INTERFACE # return primary anyway; nmcli will error clearly
# Module-level convenience — resolved once at startup.
INTERFACE = resolve_interface()
def run_nmcli(*args, timeout=45, check=True):
process = subprocess.run(
["nmcli", *args],
@@ -149,6 +168,7 @@ def write_status(action, error=None):
status = {
"action": action,
"active_connection": current,
"interface": INTERFACE,
"error": error,
"last_phone_ip": last_phone_ip,
"updated_at": time.time(),
@@ -182,6 +202,17 @@ def restore_fallback():
if activate(HOME_CONNECTION):
write_status("connected_home")
return True
# If the primary interface (wlan1) failed, try the wlan0 fallback profiles
if INTERFACE == PRIMARY_INTERFACE and _interface_exists(FALLBACK_INTERFACE):
# Temporarily try connections bound to wlan0
for conn in (f"{HOME_CONNECTION}-fb", AP_CONNECTION):
proc = subprocess.run(
["nmcli", "--wait", "30", "connection", "up", conn],
capture_output=True, text=True, timeout=35,
)
if proc.returncode == 0:
write_status(f"fallback_wlan0_{conn}")
return True
if activate(AP_CONNECTION):
write_status("restored_local_hotspot")
return True