From b117bd10e42dc1e62aeea9289021b54b627784b6 Mon Sep 17 00:00:00 2001 From: "Hairo R. Carela" Date: Sun, 9 Aug 2026 16:40:02 -0400 Subject: [PATCH] pixel2: New system image changes --- main-ui/devices/gkd/gkd_device.py | 67 +++++++------ main-ui/devices/gkd/gkd_pixel2.py | 64 ++++++------- .../{connman_wifi_menu.py => nm_wifi_menu.py} | 95 ++++++++---------- ...man_wifi_scanner.py => nm_wifi_scanner.py} | 96 +++++++------------ 4 files changed, 135 insertions(+), 187 deletions(-) rename main-ui/devices/gkd/{connman_wifi_menu.py => nm_wifi_menu.py} (69%) rename main-ui/devices/gkd/{connman_wifi_scanner.py => nm_wifi_scanner.py} (59%) diff --git a/main-ui/devices/gkd/gkd_device.py b/main-ui/devices/gkd/gkd_device.py index f82c0658..8e84e1ad 100644 --- a/main-ui/devices/gkd/gkd_device.py +++ b/main-ui/devices/gkd/gkd_device.py @@ -1,6 +1,4 @@ -import re import socket -import subprocess from apps.miyoo.miyoo_app_finder import MiyooAppFinder from controller.controller_inputs import ControllerInput from controller.sdl.sdl2_controller_interface import Sdl2ControllerInterface @@ -18,7 +16,7 @@ from utils.logger import PyUiLogger class GKDDevice(DeviceCommon): - + def __init__(self): self.button_remapper = ButtonRemapper(self.system_config) self.game_utils = MiyooTrimGameSystemUtils() @@ -44,31 +42,31 @@ def capture_framebuffer(self): def restore_framebuffer(self): pass - + def power_off_cmd(self): return "poweroff" - + def reboot_cmd(self): return "reboot" - + def _set_lumination_to_config(self): with open("/sys/class/backlight/backlight/brightness", "w") as f: f.write(str(self.map_backlight_from_10_to_full_255(self.system_config.backlight))) def _set_contrast_to_config(self): - ProcessRunner.run(["modetest", "-M", "rockchip", "-a", "-w", + ProcessRunner.run(["modetest", "-M", "rockchip", "-a", "-w", "179:contrast:"+str(self.system_config.contrast * 5)]) - + def _set_saturation_to_config(self): - ProcessRunner.run(["modetest", "-M", "rockchip", "-a", "-w", + ProcessRunner.run(["modetest", "-M", "rockchip", "-a", "-w", "179:saturation:"+str(self.system_config.saturation * 5)]) def _set_brightness_to_config(self): - ProcessRunner.run(["modetest", "-M", "rockchip", "-a", "-w", + ProcessRunner.run(["modetest", "-M", "rockchip", "-a", "-w", "179:brightness:"+str(self.system_config.brightness * 5)]) def _set_hue_to_config(self): - ProcessRunner.run(["modetest", "-M", "rockchip", "-a", "-w", + ProcessRunner.run(["modetest", "-M", "rockchip", "-a", "-w", "179:hue:"+str(self.system_config.hue * 5)]) def get_volume(self): @@ -78,13 +76,12 @@ def sleep(self): # system handles this, not sure if implementing pass - def run_game(self, rom_info): return MiyooTrimCommon.run_game(self, rom_info) def run_cmd(self, args, dir = None, is_power_cmd = False): MiyooTrimCommon.run_cmd(self, args, dir, is_power_cmd) - + def run_app(self, folder,launch): MiyooTrimCommon.run_app(self, folder,launch) @@ -93,7 +90,7 @@ def map_digital_input(self, sdl_input): if(ControllerInput.UNKNOWN == mapping): PyUiLogger.get_logger().error(f"Unknown input {sdl_input}") return mapping - + def map_key(self, key_code): if(116 == key_code): return ControllerInput.POWER_BUTTON @@ -105,7 +102,6 @@ def map_key(self, key_code): PyUiLogger.get_logger().debug(f"Unrecognized keycode {key_code}") return None - def special_input(self, controller_input, length_in_seconds): if(ControllerInput.POWER_BUTTON == controller_input): if(length_in_seconds < 1): @@ -166,8 +162,13 @@ def get_ip_addr_text(self): return "No USB adapter" try: - wlan_addrs = psutil.net_if_addrs().get("wlan0") - eth_addrs = psutil.net_if_addrs().get("eth0") + all_addrs = psutil.net_if_addrs() + for k in all_addrs.keys(): + if k.startswith("wlan"): + wlan_addrs = all_addrs.get(k) + break + + eth_addrs = all_addrs.get("eth0") wlan_addrs = wlan_addrs if wlan_addrs else [] eth_addrs = eth_addrs if eth_addrs else [] @@ -189,7 +190,7 @@ def disable_wifi(self): self.system_config.reload_config() self.system_config.set_wifi(0) self.system_config.save_config() - ProcessRunner.run(["connmanctl", "disable", "wifi"]) + ProcessRunner.run(["rfkill", "block", "wifi"]) self.get_wifi_status.force_refresh() self.get_ip_addr_text.force_refresh() @@ -197,8 +198,7 @@ def enable_wifi(self): self.system_config.reload_config() self.system_config.set_wifi(1) self.system_config.save_config() - ProcessRunner.run(["systemctl", "restart", "connman"]) - ProcessRunner.run(["connmanctl", "enable", "wifi"]) + ProcessRunner.run(["rfkill", "unblock", "wifi"]) self.get_wifi_status.force_refresh() self.get_ip_addr_text.force_refresh() @@ -207,31 +207,30 @@ def get_charge_status(self): #Probably need to find the power and not just usb with open("/sys/class/power_supply/usb/online", "r") as f: ac_online = int(f.read().strip()) - + if(ac_online): return ChargeStatus.CHARGING else: return ChargeStatus.DISCONNECTED - + @throttle.limit_refresh(15) def get_battery_percent(self): with open("/sys/class/power_supply/battery/capacity", "r") as f: - return int(f.read().strip()) + return int(f.read().strip()) return 0 - + def get_app_finder(self): return MiyooAppFinder() - + def parse_favorites(self) -> list[GameEntry]: return self.miyoo_games_file_parser.parse_favorites() - + def parse_recents(self) -> list[GameEntry]: return self.miyoo_games_file_parser.parse_recents() def is_bluetooth_enabled(self): return False - - + def disable_bluetooth(self): pass @@ -246,10 +245,10 @@ def get_bluetooth_scanner(self): def get_favorites_path(self): return "/mnt/SDCARD/Saves/pyui-favorites.json" - + def get_recents_path(self): return "/mnt/SDCARD/Saves/pyui-recents.json" - + def get_apps_config_path(self): return "/mnt/SDCARD/Saves/pyui-apps.json" @@ -276,16 +275,16 @@ def remap_buttons(self): def supports_wifi(self): return True - + def get_game_system_utils(self): return self.game_utils - + def get_roms_dir(self): return "/mnt/SDCARD/Roms/" def take_snapshot(self, path): return None - + def supports_brightness_calibration(): return True @@ -320,4 +319,4 @@ def check_for_button_remap(self, input): @throttle.limit_refresh(1) def post_present_operations(self): - self.clear_display_cache_if_memory_full("MemAvailable", 100) + self.clear_display_cache_if_memory_full("MemAvailable", 100) diff --git a/main-ui/devices/gkd/gkd_pixel2.py b/main-ui/devices/gkd/gkd_pixel2.py index 0877270d..991d2293 100644 --- a/main-ui/devices/gkd/gkd_pixel2.py +++ b/main-ui/devices/gkd/gkd_pixel2.py @@ -1,6 +1,5 @@ from pathlib import Path import os -import subprocess import threading from audio.audio_player_delegate_sdl2 import AudioPlayerDelegateSdl2 from controller.controller_inputs import ControllerInput @@ -10,16 +9,14 @@ from controller.key_watcher_controller_dataclasses import InputResult, KeyEvent from devices.miyoo.miyoo_games_file_parser import MiyooGamesFileParser from devices.gkd.gkd_device import GKDDevice -from devices.gkd.connman_wifi_scanner import ConnmanWiFiScanner -from devices.gkd.connman_wifi_menu import ConnmanWifiMenu +from devices.gkd.nm_wifi_scanner import NmWiFiScanner +from devices.gkd.nm_wifi_menu import NmWifiMenu from devices.std_in_based_send_event_binary_helper import StdInBasedSendEventBinaryHelper from devices.utils.file_watcher import FileWatcher -from devices.utils.process_runner import ProcessRunner from menus.settings.timezone_menu import TimezoneMenu from utils import throttle from utils.ffmpeg_image_utils import FfmpegImageUtils -from utils.logger import PyUiLogger from utils.py_ui_config import PyUiConfig class GKDPixel2(GKDDevice): @@ -31,7 +28,7 @@ def __init__(self, device_name, main_ui_mode): self._load_system_config("/mnt/SDCARD/Saves/gkd-pixel2-system.json", source) if(main_ui_mode): - self.miyoo_games_file_parser = MiyooGamesFileParser() + self.miyoo_games_file_parser = MiyooGamesFileParser() threading.Thread(target=self.monitor_wifi, daemon=True).start() threading.Thread(target=self.startup_init, daemon=True).start() self.config_watcher_thread, self.config_watcher_thread_stop_event = FileWatcher().start_file_watcher( @@ -48,9 +45,8 @@ def __init__(self, device_name, main_ui_mode): power_key_polling_thread = threading.Thread(target=self.power_key_watcher.poll_keyboard, daemon=True) power_key_polling_thread.start() # Done to try to account for external systems editting the config file - + super().__init__() - def startup_init(self, include_wifi=True): self._set_lumination_to_config() @@ -58,7 +54,7 @@ def startup_init(self, include_wifi=True): self._set_saturation_to_config() self._set_brightness_to_config() self._set_hue_to_config() - + #Untested @throttle.limit_refresh(5) def is_hdmi_connected(self): @@ -84,7 +80,7 @@ def get_scale_factor(self): return 2.25 else: return 1 - + def supports_brightness_calibration(self): return False @@ -102,15 +98,15 @@ def get_image_utils(self): def get_controller_interface(self): - key_mappings = {} + key_mappings = {} key_mappings[KeyEvent(1, 304, 0)] = [InputResult(ControllerInput.B, KeyState.RELEASE)] key_mappings[KeyEvent(1, 304, 1)] = [InputResult(ControllerInput.B, KeyState.PRESS)] - key_mappings[KeyEvent(1, 305, 0)] = [InputResult(ControllerInput.A, KeyState.RELEASE)] - key_mappings[KeyEvent(1, 305, 1)] = [InputResult(ControllerInput.A, KeyState.PRESS)] - key_mappings[KeyEvent(1, 307, 0)] = [InputResult(ControllerInput.X, KeyState.RELEASE)] - key_mappings[KeyEvent(1, 307, 1)] = [InputResult(ControllerInput.X, KeyState.PRESS)] - key_mappings[KeyEvent(1, 308, 0)] = [InputResult(ControllerInput.Y, KeyState.RELEASE)] - key_mappings[KeyEvent(1, 308, 1)] = [InputResult(ControllerInput.Y, KeyState.PRESS)] + key_mappings[KeyEvent(1, 305, 0)] = [InputResult(ControllerInput.A, KeyState.RELEASE)] + key_mappings[KeyEvent(1, 305, 1)] = [InputResult(ControllerInput.A, KeyState.PRESS)] + key_mappings[KeyEvent(1, 307, 0)] = [InputResult(ControllerInput.X, KeyState.RELEASE)] + key_mappings[KeyEvent(1, 307, 1)] = [InputResult(ControllerInput.X, KeyState.PRESS)] + key_mappings[KeyEvent(1, 308, 0)] = [InputResult(ControllerInput.Y, KeyState.RELEASE)] + key_mappings[KeyEvent(1, 308, 1)] = [InputResult(ControllerInput.Y, KeyState.PRESS)] key_mappings[KeyEvent(1, 544, 1)] = [InputResult(ControllerInput.DPAD_UP, KeyState.PRESS)] key_mappings[KeyEvent(1, 544, 0)] = [InputResult(ControllerInput.DPAD_UP, KeyState.RELEASE)] @@ -121,35 +117,35 @@ def get_controller_interface(self): key_mappings[KeyEvent(1, 547, 1)] = [InputResult(ControllerInput.DPAD_RIGHT, KeyState.PRESS)] key_mappings[KeyEvent(1, 547, 0)] = [InputResult(ControllerInput.DPAD_RIGHT, KeyState.RELEASE)] - key_mappings[KeyEvent(1, 310, 1)] = [InputResult(ControllerInput.L1, KeyState.PRESS)] - key_mappings[KeyEvent(1, 310, 0)] = [InputResult(ControllerInput.L1, KeyState.RELEASE)] - key_mappings[KeyEvent(1, 312, 1)] = [InputResult(ControllerInput.L2, KeyState.PRESS)] - key_mappings[KeyEvent(1, 312, 0)] = [InputResult(ControllerInput.L2, KeyState.RELEASE)] + key_mappings[KeyEvent(1, 310, 1)] = [InputResult(ControllerInput.L1, KeyState.PRESS)] + key_mappings[KeyEvent(1, 310, 0)] = [InputResult(ControllerInput.L1, KeyState.RELEASE)] + key_mappings[KeyEvent(1, 312, 1)] = [InputResult(ControllerInput.L2, KeyState.PRESS)] + key_mappings[KeyEvent(1, 312, 0)] = [InputResult(ControllerInput.L2, KeyState.RELEASE)] - key_mappings[KeyEvent(1, 311, 1)] = [InputResult(ControllerInput.R1, KeyState.PRESS)] - key_mappings[KeyEvent(1, 311, 0)] = [InputResult(ControllerInput.R1, KeyState.RELEASE)] - key_mappings[KeyEvent(1, 313, 1)] = [InputResult(ControllerInput.R2, KeyState.PRESS)] - key_mappings[KeyEvent(1, 313, 0)] = [InputResult(ControllerInput.R2, KeyState.RELEASE)] + key_mappings[KeyEvent(1, 311, 1)] = [InputResult(ControllerInput.R1, KeyState.PRESS)] + key_mappings[KeyEvent(1, 311, 0)] = [InputResult(ControllerInput.R1, KeyState.RELEASE)] + key_mappings[KeyEvent(1, 313, 1)] = [InputResult(ControllerInput.R2, KeyState.PRESS)] + key_mappings[KeyEvent(1, 313, 0)] = [InputResult(ControllerInput.R2, KeyState.RELEASE)] - key_mappings[KeyEvent(1, 315, 1)] = [InputResult(ControllerInput.START, KeyState.PRESS)] + key_mappings[KeyEvent(1, 315, 1)] = [InputResult(ControllerInput.START, KeyState.PRESS)] key_mappings[KeyEvent(1, 315, 0)] = [InputResult(ControllerInput.START, KeyState.RELEASE)] - key_mappings[KeyEvent(1, 314, 1)] = [InputResult(ControllerInput.SELECT, KeyState.PRESS)] + key_mappings[KeyEvent(1, 314, 1)] = [InputResult(ControllerInput.SELECT, KeyState.PRESS)] key_mappings[KeyEvent(1, 314, 0)] = [InputResult(ControllerInput.SELECT, KeyState.RELEASE)] key_mappings[KeyEvent(1, 704, 1)] = [InputResult(ControllerInput.MENU, KeyState.PRESS)] - key_mappings[KeyEvent(1, 704, 0)] = [InputResult(ControllerInput.MENU, KeyState.RELEASE)] + key_mappings[KeyEvent(1, 704, 0)] = [InputResult(ControllerInput.MENU, KeyState.RELEASE)] return KeyWatcherController(event_path="/dev/input/event2", mapping_provider=DictKeyMappingProvider(key_mappings)) - + def get_device_name(self): return self.device_name - + def get_audio_system(self): return self.audio_player - + def get_core_name_overrides(self, core_name): return [core_name, core_name+"-64"] - + def supports_timezone_setting(self): return True @@ -174,10 +170,10 @@ def might_require_surface_format_conversion(self): return True # RA save state images don't seem to load w/o conversion? def get_wifi_menu(self): - return ConnmanWifiMenu() + return NmWifiMenu() def get_new_wifi_scanner(self): - return ConnmanWiFiScanner() + return NmWiFiScanner() def volume_up(self): StdInBasedSendEventBinaryHelper.send_key_down_and_up("/dev/input/event1",115) diff --git a/main-ui/devices/gkd/connman_wifi_menu.py b/main-ui/devices/gkd/nm_wifi_menu.py similarity index 69% rename from main-ui/devices/gkd/connman_wifi_menu.py rename to main-ui/devices/gkd/nm_wifi_menu.py index 93b52403..249c6dc4 100644 --- a/main-ui/devices/gkd/connman_wifi_menu.py +++ b/main-ui/devices/gkd/nm_wifi_menu.py @@ -1,10 +1,9 @@ from asyncio import subprocess from pathlib import Path -import configparser import time from controller.controller_inputs import ControllerInput from devices.device import Device -from devices.gkd.connman_wifi_scanner import WiFiNetwork +from devices.gkd.nm_wifi_scanner import WiFiNetwork from devices.utils.process_runner import ProcessRunner from display.display import Display from display.on_screen_keyboard import OnScreenKeyboard @@ -16,80 +15,51 @@ from menus.language.language import Language -class ConnmanWifiMenu: +class NmWifiMenu: def __init__(self): self.on_screen_keyboard = OnScreenKeyboard() + def get_iface_name(self): + res = "wlan0" + interfaces = Path("/sys/class/net/") + + for i in interfaces.iterdir(): + if i.name.startswith("wlan"): + res = i.name + + return res + def wifi_adjust(self): if Device.get_device().is_wifi_enabled(): Device.get_device().disable_wifi() else: Device.get_device().enable_wifi() - - def write_connman_conf(self, network: WiFiNetwork, passwd: str): - config_folder = Path("/storage/.cache/connman") - - # Build config options - config = configparser.RawConfigParser() - config.optionxform = lambda option: option - - config.add_section("Settings") - config["Settings"]["AutoConnect"] = "true" - - net_section = f"service_{network.id_str}" - config.add_section(net_section) - config[net_section]["Type"] = "wifi" - config[net_section]["Name"] = network.ssid - config[net_section]["Passphrase"] = passwd - - filename = network.id_str.split("_")[2] - full_path = config_folder.joinpath(filename).with_suffix(".config") - - # Write to file + def nm_connect(self, ssid: str, passwd: str): try: - with open(full_path, "w") as f: - config.write(f) - - PyUiLogger.get_logger().info( - f"Installed network '{network.ssid}' into {str(full_path)}" - ) - - except OSError as e: - PyUiLogger.get_logger().error(f"Failed writing {str(full_path)}: {e}") - - - def connman_connect(self, id_str: str): - try: - ProcessRunner.run(["connmanctl", "connect", id_str]) - PyUiLogger.get_logger().info(f"Connected to {id_str}.") + ProcessRunner.run(["nmcli", "device", "wifi", "connect", ssid, "password", passwd, "ifname", self._interface]) + PyUiLogger.get_logger().info(f"Connected to {ssid}.") except subprocess.CalledProcessError as e: - PyUiLogger.get_logger().error(f"Error connecting to {id_str}: {e}") + PyUiLogger.get_logger().error(f"Error connecting to {ssid}: {e}") #TODO add confirmation or failed popups def switch_network(self, net: WiFiNetwork): PyUiLogger.get_logger().info(f"Selected {net.ssid}!") if(net.requires_password()): - password = self.on_screen_keyboard.get_input(Language.label("wifiPassword", "WiFi Password")) + password = self.on_screen_keyboard.get_input("WiFi Password") if(password is not None and 8 <= len(password) <= 63): - self.write_connman_conf(net, password) - Display.display_message( - Language.label("updatingWifiConfig", "Updating config file for {ssid} with password {password}") - .replace("{ssid}", net.ssid) - .replace("{password}", password), - duration_ms=5000, - ) + self.nm_connect(net.ssid, password) + Display.display_message(f"Updating config file for {net.ssid} with password {password}", duration_ms=5000) else: - Display.display_message(Language.label("invalidWifiPasswordLength", "Invalid WiFi password length! Must be between 8 and 63"), duration_ms=5000) - - self.connman_connect(net.id_str) + Display.display_message("Invalid WiFi password length! Must be between 8 and 63", duration_ms=5000) def _build_options( self, wifi_enabled: bool, networks: list[WiFiNetwork], connected_ssid: str | None, + connected_is_5ghz: bool, ): option_list = [] @@ -97,7 +67,7 @@ def _build_options( option_list.append( GridOrListEntry( primary_text=Language.status(), - value_text="< " + Language.on_off_label(wifi_enabled) + " >", + value_text="< " + ("On" if wifi_enabled else "Off") + " >", image_path=None, image_path_selected=None, description=None, @@ -124,12 +94,19 @@ def _build_options( seen_names = set() for net in networks: name = net.ssid + is_5ghz = 5000 <= net.frequency <= 6000 + + if is_5ghz: + name += " (5Ghz)" if name in seen_names: continue seen_names.add(name) - connected = connected_ssid == net.ssid + connected = ( + connected_ssid == net.ssid + and is_5ghz == connected_is_5ghz + ) option_list.append( GridOrListEntry( @@ -149,7 +126,7 @@ def adapter_is_connected(self): interfaces = Path("/sys/class/net/") for i in interfaces.iterdir(): - if i.name in ["wlan0", "eth0"]: + if i.name.startswith("eth") or i.name.startswith("wlan"): return True return False @@ -167,10 +144,14 @@ def _show_menu(self): selected = Selection(None, None, 0) self.wifi_scanner = Device.get_device().get_new_wifi_scanner() + self._interface = self.get_iface_name() + self.wifi_scanner.interface = self._interface + # Start background scanning immediately self.wifi_scanner.scan_networks() connected_ssid = None + connected_is_5ghz = False accepted_inputs = [ ControllerInput.A, @@ -191,14 +172,16 @@ def _show_menu(self): else [] ) - ssid = self.wifi_scanner.get_connected_ssid() + ssid, freq = self.wifi_scanner.get_connected_ssid() connected_ssid = ssid + connected_is_5ghz = bool(freq and 5000 <= freq <= 6000) # Build options (single source of truth) option_list = self._build_options( wifi_enabled=wifi_enabled, networks=networks, connected_ssid=connected_ssid, + connected_is_5ghz=connected_is_5ghz, ) # Render view @@ -224,5 +207,5 @@ def _show_menu(self): time.sleep(0.05) finally: - Display.display_message(Language.label("stoppingWifiScanner", "Stopping WiFi scanner...")) + Display.display_message("Stopping WiFi scanner...") self.wifi_scanner.stop() diff --git a/main-ui/devices/gkd/connman_wifi_scanner.py b/main-ui/devices/gkd/nm_wifi_scanner.py similarity index 59% rename from main-ui/devices/gkd/connman_wifi_scanner.py rename to main-ui/devices/gkd/nm_wifi_scanner.py index 446fca65..c7c5ac96 100644 --- a/main-ui/devices/gkd/connman_wifi_scanner.py +++ b/main-ui/devices/gkd/nm_wifi_scanner.py @@ -1,28 +1,17 @@ -import subprocess -import json import time import threading -from dataclasses import dataclass from typing import List, Set from devices.device import Device from devices.utils.process_runner import ProcessRunner from utils.logger import PyUiLogger +from devices.wifi.wifi_scanner import WiFiNetwork -@dataclass -class WiFiNetwork: - id_str: str - signal_level: int - security: str - ssid: str - - def requires_password(self) -> bool: - return "psk" in self.security or "wep" in self.security - -class ConnmanWiFiScanner: +class NmWiFiScanner: def __init__(self, interface="wlan0", delay=2): self.interface = interface self.delay = delay + self.connected_network = None # Thread state self._thread: threading.Thread | None = None @@ -31,7 +20,7 @@ def __init__(self, interface="wlan0", delay=2): # Shared scan results self._lock = threading.Lock() self._known_ssids: Set[str] = set() - self._known_id_str: Set[str] = set() + self._known_bssids: Set[str] = set() self._networks: List[WiFiNetwork] = [] # ---------------------------- @@ -49,6 +38,7 @@ def _scan_worker(self): log.exception("WiFi scan worker error") # Cooperative sleep so stop() reacts immediately + log.info("Scanning...") self._stop_event.wait(self.delay) log.info("WiFi scan thread stopped") @@ -59,42 +49,47 @@ def _scan_once_internal(self): """ log = PyUiLogger.get_logger() - result = ProcessRunner.run(["connmanctl", "scan", "wifi"]) - if "Scan completed" not in result.stdout: - log.error("wlan0 seems broken, restarting and retrying") + result = ProcessRunner.run(["nmcli", "dev", "wifi", "rescan", "ifname", self.interface]) + if "Error:" in result.stdout: + log.error(f"{self.interface} seems broken, restarting and retrying") Device.get_device().wifi_error_detected() time.sleep(15) - ProcessRunner.run(["connmanctl", "scan", "wifi"]) + ProcessRunner.run(["nmcli", "dev", "wifi", "rescan", "ifname", self.interface]) time.sleep(self.delay) - jdata = self._get_connman_services() + result = ProcessRunner.run(["nmcli", "-t", "device", "wifi", "list"]) + lines = result.stdout.replace("\\:", "-").splitlines() + new_networks: List[WiFiNetwork] = [] - for service in jdata: - if 'Name' in service[1].keys(): - ssid = service[1]['Name']['data'] - else: - continue + for line in lines: + parts = line.split(":") - id_str = service[0].split("/")[-1] # Connman uses it's own ID string - signal = service[1]['Strength']['data'] - security = " ".join(service[1]['Security']['data']) + bssid = parts[1].replace("-", ":") + ssid = parts[2] + freq = 5100 if int(parts[4]) > 30 else 2400 + signal = int(parts[6]) + flags = parts[8] network = WiFiNetwork( - id_str=id_str, - signal_level=int(signal), - security=security, - ssid=ssid, + bssid=bssid, + frequency=freq, + signal_level=signal, + flags=flags, + ssid=ssid, ) new_networks.append(network) + if parts[0] == "*": + self.connected_network = network + # Merge uniquely seen networks with self._lock: for net in new_networks: - if net.id_str not in self._known_id_str: - self._known_id_str.add(net.id_str) + if net.bssid not in self._known_bssids: + self._known_bssids.add(net.bssid) self._known_ssids.add(net.ssid) self._networks.append(net) @@ -140,7 +135,7 @@ def stop(self): with self._lock: self._known_ssids.clear() - self._known_id_str.clear() + self._known_bssids.clear() self._networks.clear() # ---------------------------- @@ -148,32 +143,7 @@ def stop(self): # ---------------------------- def get_connected_ssid(self): - ssid = None - - jdata = self._get_connman_services() - - if jdata: - for service in jdata: - if service[1]['State']['data'] == "online": - ssid = service[1]['Name']['data'] - break + if self.connected_network: + return self.connected_network.ssid, self.connected_network.frequency else: - PyUiLogger.get_logger().error("Failed to get Wi-Fi details") - - return ssid - - def _get_connman_services(self): - res = [] - try: - result = ProcessRunner.run([ - "busctl", "-j", "--system", - "call", "net.connman", "/", - "net.connman.Manager", "GetServices" - ]) - - jdata = json.loads(result.stdout) - res = jdata['data'][0] - except (subprocess.CalledProcessError, json.JSONDecodeError) as e: - PyUiLogger.get_logger().error(f"Failed to get connman services: {e}") - - return res + return None, None \ No newline at end of file