Migrate to a better architectured project
This commit is contained in:
parent
cc5ed5f23e
commit
b1b9eaa028
21 changed files with 2712 additions and 1540 deletions
273
internal/wifi/wifi.go
Normal file
273
internal/wifi/wifi.go
Normal file
|
|
@ -0,0 +1,273 @@
|
|||
package wifi
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"os/exec"
|
||||
"sort"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/godbus/dbus/v5"
|
||||
"github.com/nemunaire/repeater/internal/models"
|
||||
)
|
||||
|
||||
const (
|
||||
WLAN_INTERFACE = "wlan0"
|
||||
WPA_CONF = "/etc/wpa_supplicant/wpa_supplicant.conf"
|
||||
|
||||
// D-Bus constants for wpa_supplicant
|
||||
WPA_SUPPLICANT_SERVICE = "fi.w1.wpa_supplicant1"
|
||||
WPA_SUPPLICANT_PATH = "/fi/w1/wpa_supplicant1"
|
||||
WPA_SUPPLICANT_IFACE = "fi.w1.wpa_supplicant1"
|
||||
WPA_INTERFACE_IFACE = "fi.w1.wpa_supplicant1.Interface"
|
||||
WPA_BSS_IFACE = "fi.w1.wpa_supplicant1.BSS"
|
||||
WPA_NETWORK_IFACE = "fi.w1.wpa_supplicant1.Network"
|
||||
)
|
||||
|
||||
var (
|
||||
dbusConn *dbus.Conn
|
||||
wpaSupplicant dbus.BusObject
|
||||
)
|
||||
|
||||
// Initialize initializes the WiFi service with D-Bus connection
|
||||
func Initialize() error {
|
||||
var err error
|
||||
dbusConn, err = dbus.SystemBus()
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to connect to D-Bus: %v", err)
|
||||
}
|
||||
|
||||
wpaSupplicant = dbusConn.Object(WPA_SUPPLICANT_SERVICE, dbus.ObjectPath(WPA_SUPPLICANT_PATH))
|
||||
return nil
|
||||
}
|
||||
|
||||
// Close closes the D-Bus connection
|
||||
func Close() {
|
||||
if dbusConn != nil {
|
||||
dbusConn.Close()
|
||||
}
|
||||
}
|
||||
|
||||
// ScanNetworks scans for available WiFi networks
|
||||
func ScanNetworks() ([]models.WiFiNetwork, error) {
|
||||
interfacePath, err := getWiFiInterfacePath()
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("impossible d'obtenir l'interface WiFi: %v", err)
|
||||
}
|
||||
|
||||
// Trigger a scan
|
||||
wifiInterface := dbusConn.Object(WPA_SUPPLICANT_SERVICE, interfacePath)
|
||||
call := wifiInterface.Call(WPA_INTERFACE_IFACE+".Scan", 0, map[string]dbus.Variant{"Type": dbus.MakeVariant("active")})
|
||||
if call.Err != nil {
|
||||
return nil, fmt.Errorf("erreur lors du scan: %v", call.Err)
|
||||
}
|
||||
|
||||
// Wait for scan to complete
|
||||
time.Sleep(2 * time.Second)
|
||||
|
||||
// Retrieve BSS list
|
||||
bssePaths, err := wifiInterface.GetProperty(WPA_INTERFACE_IFACE + ".BSSs")
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("erreur lors de la récupération des BSS: %v", err)
|
||||
}
|
||||
|
||||
var networks []models.WiFiNetwork
|
||||
seenSSIDs := make(map[string]bool)
|
||||
|
||||
for _, bssPath := range bssePaths.Value().([]dbus.ObjectPath) {
|
||||
bss := dbusConn.Object(WPA_SUPPLICANT_SERVICE, bssPath)
|
||||
|
||||
// Get BSS properties
|
||||
var props map[string]dbus.Variant
|
||||
err = bss.Call("org.freedesktop.DBus.Properties.GetAll", 0, WPA_BSS_IFACE).Store(&props)
|
||||
if err != nil {
|
||||
continue
|
||||
}
|
||||
|
||||
network := models.WiFiNetwork{}
|
||||
|
||||
// Extract SSID
|
||||
if ssidBytes, ok := props["SSID"].Value().([]byte); ok {
|
||||
network.SSID = string(ssidBytes)
|
||||
}
|
||||
|
||||
// Skip duplicates and empty SSIDs
|
||||
if network.SSID == "" || seenSSIDs[network.SSID] {
|
||||
continue
|
||||
}
|
||||
seenSSIDs[network.SSID] = true
|
||||
|
||||
// Extract BSSID
|
||||
if bssidBytes, ok := props["BSSID"].Value().([]byte); ok {
|
||||
network.BSSID = fmt.Sprintf("%02x:%02x:%02x:%02x:%02x:%02x",
|
||||
bssidBytes[0], bssidBytes[1], bssidBytes[2], bssidBytes[3], bssidBytes[4], bssidBytes[5])
|
||||
}
|
||||
|
||||
// Extract signal strength
|
||||
if signal, ok := props["Signal"].Value().(int16); ok {
|
||||
network.Signal = signalToStrength(int(signal))
|
||||
}
|
||||
|
||||
// Extract frequency and calculate channel
|
||||
if frequency, ok := props["Frequency"].Value().(uint16); ok {
|
||||
network.Channel = frequencyToChannel(int(frequency))
|
||||
}
|
||||
|
||||
// Determine security
|
||||
if privacyVal, ok := props["Privacy"].Value().(bool); ok && privacyVal {
|
||||
if wpaProps, ok := props["WPA"].Value().(map[string]dbus.Variant); ok && len(wpaProps) > 0 {
|
||||
network.Security = "WPA"
|
||||
} else if rsnProps, ok := props["RSN"].Value().(map[string]dbus.Variant); ok && len(rsnProps) > 0 {
|
||||
network.Security = "WPA2"
|
||||
} else {
|
||||
network.Security = "WEP"
|
||||
}
|
||||
} else {
|
||||
network.Security = "Open"
|
||||
}
|
||||
|
||||
networks = append(networks, network)
|
||||
}
|
||||
|
||||
// Sort by signal strength
|
||||
sort.Slice(networks, func(i, j int) bool {
|
||||
return networks[i].Signal > networks[j].Signal
|
||||
})
|
||||
|
||||
return networks, nil
|
||||
}
|
||||
|
||||
// Connect connects to a WiFi network using D-Bus
|
||||
func Connect(ssid, password string) error {
|
||||
interfacePath, err := getWiFiInterfacePath()
|
||||
if err != nil {
|
||||
return fmt.Errorf("impossible d'obtenir l'interface WiFi: %v", err)
|
||||
}
|
||||
|
||||
wifiInterface := dbusConn.Object(WPA_SUPPLICANT_SERVICE, interfacePath)
|
||||
|
||||
// Create a new network
|
||||
networkConfig := map[string]dbus.Variant{
|
||||
"ssid": dbus.MakeVariant(ssid),
|
||||
}
|
||||
|
||||
if password != "" {
|
||||
networkConfig["psk"] = dbus.MakeVariant(password)
|
||||
}
|
||||
|
||||
var networkPath dbus.ObjectPath
|
||||
err = wifiInterface.Call(WPA_INTERFACE_IFACE+".AddNetwork", 0, networkConfig).Store(&networkPath)
|
||||
if err != nil {
|
||||
return fmt.Errorf("erreur lors de l'ajout du réseau: %v", err)
|
||||
}
|
||||
|
||||
// Select the network
|
||||
err = wifiInterface.Call(WPA_INTERFACE_IFACE+".SelectNetwork", 0, networkPath).Err
|
||||
if err != nil {
|
||||
return fmt.Errorf("erreur lors de la sélection du réseau: %v", err)
|
||||
}
|
||||
|
||||
// Wait for connection
|
||||
for i := 0; i < 20; i++ {
|
||||
time.Sleep(500 * time.Millisecond)
|
||||
if IsConnected() {
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
return fmt.Errorf("timeout lors de la connexion")
|
||||
}
|
||||
|
||||
// Disconnect disconnects from the current WiFi network
|
||||
func Disconnect() error {
|
||||
interfacePath, err := getWiFiInterfacePath()
|
||||
if err != nil {
|
||||
return fmt.Errorf("impossible d'obtenir l'interface WiFi: %v", err)
|
||||
}
|
||||
|
||||
wifiInterface := dbusConn.Object(WPA_SUPPLICANT_SERVICE, interfacePath)
|
||||
|
||||
// Disconnect
|
||||
err = wifiInterface.Call(WPA_INTERFACE_IFACE+".Disconnect", 0).Err
|
||||
if err != nil {
|
||||
return fmt.Errorf("erreur lors de la déconnexion: %v", err)
|
||||
}
|
||||
|
||||
// Remove all networks
|
||||
var networks []dbus.ObjectPath
|
||||
err = wifiInterface.Call(WPA_INTERFACE_IFACE+".Get", 0, WPA_INTERFACE_IFACE, "Networks").Store(&networks)
|
||||
if err == nil {
|
||||
for _, networkPath := range networks {
|
||||
wifiInterface.Call(WPA_INTERFACE_IFACE+".RemoveNetwork", 0, networkPath)
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// IsConnected checks if WiFi is connected using D-Bus
|
||||
func IsConnected() bool {
|
||||
interfacePath, err := getWiFiInterfacePath()
|
||||
if err != nil {
|
||||
return false
|
||||
}
|
||||
|
||||
wifiInterface := dbusConn.Object(WPA_SUPPLICANT_SERVICE, interfacePath)
|
||||
var state string
|
||||
err = wifiInterface.Call(WPA_INTERFACE_IFACE+".Get", 0, WPA_INTERFACE_IFACE, "State").Store(&state)
|
||||
if err != nil {
|
||||
return false
|
||||
}
|
||||
|
||||
return state == "completed"
|
||||
}
|
||||
|
||||
// IsConnectedLegacy checks if WiFi is connected using iwconfig (fallback)
|
||||
func IsConnectedLegacy() bool {
|
||||
cmd := exec.Command("iwconfig", WLAN_INTERFACE)
|
||||
output, err := cmd.Output()
|
||||
if err != nil {
|
||||
return false
|
||||
}
|
||||
|
||||
return strings.Contains(string(output), "Access Point:")
|
||||
}
|
||||
|
||||
// getWiFiInterfacePath retrieves the D-Bus path for the WiFi interface
|
||||
func getWiFiInterfacePath() (dbus.ObjectPath, error) {
|
||||
var interfacePath dbus.ObjectPath
|
||||
err := wpaSupplicant.Call(WPA_SUPPLICANT_IFACE+".GetInterface", 0, WLAN_INTERFACE).Store(&interfacePath)
|
||||
if err != nil {
|
||||
return "", fmt.Errorf("erreur lors de la récupération des interfaces: %v", err)
|
||||
}
|
||||
|
||||
return interfacePath, nil
|
||||
}
|
||||
|
||||
// frequencyToChannel converts WiFi frequency to channel number
|
||||
func frequencyToChannel(frequency int) int {
|
||||
if frequency >= 2412 && frequency <= 2484 {
|
||||
if frequency == 2484 {
|
||||
return 14
|
||||
}
|
||||
return (frequency-2412)/5 + 1
|
||||
} else if frequency >= 5170 && frequency <= 5825 {
|
||||
return (frequency - 5000) / 5
|
||||
}
|
||||
return 0
|
||||
}
|
||||
|
||||
// signalToStrength converts signal level (dBm) to strength (1-5)
|
||||
func signalToStrength(level int) int {
|
||||
if level >= -30 {
|
||||
return 5
|
||||
} else if level >= -50 {
|
||||
return 4
|
||||
} else if level >= -60 {
|
||||
return 3
|
||||
} else if level >= -70 {
|
||||
return 2
|
||||
} else {
|
||||
return 1
|
||||
}
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue