Annexe : Noyau - wifi.py

Rôle et utilité

Le fichier core/wifi.py est responsable de la gestion des réseaux sans fil. En l'absence d'outils lourds comme NetworkManager, il s'interface directement avec l'outil système bas niveau wpa_supplicant pour scanner les réseaux, s'y connecter et vérifier le statut de la connexion.

Implémentation technique

Pistes de modification

Code Source

import subprocess
import os

class WifiManager:
    def __init__(self, desktop):
        self.desktop = desktop
        self.iface = self._detect_iface()

    def _detect_iface(self):
        try:
            net_path = "/sys/class/net"
            if os.path.exists(net_path):
                for iface in os.listdir(net_path):
                    if iface.startswith("wl"):
                        return iface
        except:
            pass
        return None

    def scan_networks(self, sudo_pwd):
        if not self.iface:
            return []

        try:
            # Déclenchement du scan via wpa_cli (car wpa_supplicant tourne déjà)
            subprocess.run(['sudo', '-S', '/sbin/wpa_cli', '-i', self.iface, 'scan'], input=sudo_pwd + '\n', text=True, capture_output=True)

            import time
            time.sleep(5) # Laisser le temps au scan complet de s'effectuer (souvent 3 à 5 secondes)

            # Récupération des résultats
            res = subprocess.run(['sudo', '-S', '/sbin/wpa_cli', '-i', self.iface, 'scan_results'], input=sudo_pwd + '\n', text=True, capture_output=True)

            # DEBUG : Sauvegarder la sortie brute pour analyse
            try:
                with open('/tmp/wifi_debug.txt', 'w') as f:
                    f.write(res.stdout)
            except:
                pass

            networks = {}
            lines = res.stdout.strip().split('\n')
            # Format attendu : bssid / frequency / signal level / flags / ssid (séparés par des tabulations)
            for line in lines[1:]:
                parts = line.split('\t')
                if len(parts) >= 5:
                    signal = parts[2]
                    ssid = parts[4]

                    if ssid and not ssid.startswith('\\x00'):
                        try:
                            # Ne garder que le meilleur signal par SSID
                            sig_val = float(signal)
                            if ssid not in networks or networks[ssid]['signal'] < sig_val:
                                networks[ssid] = {'ssid': ssid, 'signal': sig_val}
                        except:
                            pass

            valid_nets = sorted(networks.values(), key=lambda x: x['signal'], reverse=True)
            return valid_nets
        except Exception as e:
            print("Scan error:", e)
            return []

    def connect_network(self, sudo_pwd, ssid, psk):
        if not self.iface:
            return False, "Aucune interface Wi-Fi détectée"

        try:
            def run_wpa_cmd(args):
                cmd = ['sudo', '-S', '/sbin/wpa_cli', '-i', self.iface] + args
                res = subprocess.run(cmd, input=sudo_pwd + '\n', text=True, capture_output=True)
                return res.stdout.strip()

            out = run_wpa_cmd(['add_network'])
            # Output of add_network should be the network ID (integer as string)
            # but sometimes it has leading/trailing debug info. Let's just grab the last line.
            lines = out.split('\n')
            net_id = lines[-1].strip()

            if not net_id.isdigit():
                return False, f"Erreur de création de réseau ({out})"

            run_wpa_cmd(['set_network', net_id, 'ssid', f'"{ssid}"'])
            if psk:
                run_wpa_cmd(['set_network', net_id, 'psk', f'"{psk}"'])
            else:
                run_wpa_cmd(['set_network', net_id, 'key_mgmt', 'NONE'])

            run_wpa_cmd(['enable_network', net_id])
            run_wpa_cmd(['save_config'])
            run_wpa_cmd(['select_network', net_id])

            return True, "Connexion initiée."
        except Exception as e:
            return False, str(e)

    def get_current_status(self, sudo_pwd):
        if not self.iface:
            return None, None

        try:
            res = subprocess.run(['sudo', '-S', '/sbin/wpa_cli', '-i', self.iface, 'status'], input=sudo_pwd + '\n', text=True, capture_output=True)

            ssid = None
            ip = None
            wpa_state = None
            for line in res.stdout.split('\n'):
                if line.startswith('ssid='):
                    ssid = line.split('=', 1)[1]
                elif line.startswith('ip_address='):
                    ip = line.split('=', 1)[1]
                elif line.startswith('wpa_state='):
                    wpa_state = line.split('=', 1)[1]

            if wpa_state == 'COMPLETED':
                return ssid, ip
        except:
            pass
        return None, None