#!/usr/bin/env python3 import dbus import dbus.mainloop.glib from gi.repository import AppIndicator3 from gi.repository import Gtk, GLib, GObject import gi import subprocess import threading import time import sys # Especificar las versiones antes de importar gi.require_version('Gtk', '3.0') gi.require_version('AppIndicator3', '0.1') class NetworkMenu: def __init__(self): self.app = 'network-menu' # Define el icono inicial self.indicator = AppIndicator3.Indicator.new( self.app, "/home/teraflops/Icons/wifi_disconnected.png", AppIndicator3.IndicatorCategory.SYSTEM_SERVICES ) self.indicator.set_status(AppIndicator3.IndicatorStatus.ACTIVE) self.menu = Gtk.Menu() self.build_menu() self.indicator.set_menu(self.menu) # Inicializar D-Bus dbus.mainloop.glib.DBusGMainLoop(set_as_default=True) self.bus = dbus.SystemBus() try: self.network_manager = self.bus.get_object( 'org.freedesktop.NetworkManager', '/org/freedesktop/NetworkManager') self.network_manager_props = dbus.Interface( self.network_manager, 'org.freedesktop.DBus.Properties') except dbus.exceptions.DBusException as e: print( f"Error al conectar con NetworkManager a través de D-Bus: {e}", file=sys.stderr) sys.exit(1) # Suscribirse a las señales de cambio de propiedades self.bus.add_signal_receiver( self.on_properties_changed, dbus_interface='org.freedesktop.DBus.Properties', signal_name='PropertiesChanged', arg0='org.freedesktop.NetworkManager', path='/org/freedesktop/NetworkManager', ) # Obtener estado inicial self.update_status_initial() def build_menu(self): # Botón Conectar WiFi self.item_connect = Gtk.MenuItem(label='Conectar WiFi') self.item_connect.connect('activate', self.connect_wifi) self.menu.append(self.item_connect) # Botón Desconectar WiFi self.item_disconnect = Gtk.MenuItem(label='Desconectar WiFi') self.item_disconnect.connect('activate', self.disconnect_wifi) self.menu.append(self.item_disconnect) # Botón Ver Redes Disponibles self.item_view_networks = Gtk.MenuItem(label='Ver Redes Disponibles') self.item_view_networks.connect('activate', self.show_network_list) self.menu.append(self.item_view_networks) # Separador self.menu.append(Gtk.SeparatorMenuItem()) # Botón Salir item_quit = Gtk.MenuItem(label='Salir') item_quit.connect('activate', self.quit) self.menu.append(item_quit) self.menu.show_all() def show_network_list(self, _): # Llamar a la función para obtener la lista de redes networks = self.get_available_networks_list() # Crear la ventana de lista de redes window = Gtk.Window(title="Redes Disponibles") window.set_default_size(300, 200) window.set_border_width(10) # Crear una caja vertical para la lista de redes y el botón de cerrar vbox = Gtk.Box(orientation=Gtk.Orientation.VERTICAL, spacing=10) # Añadir botones para cada red disponible for network in networks: button = Gtk.Button(label=network) button.connect( "clicked", self.prompt_password_and_connect, network) vbox.pack_start(button, True, True, 0) # Añadir un botón para cerrar la ventana close_button = Gtk.Button(label="Cerrar") close_button.connect("clicked", lambda x: window.destroy()) vbox.pack_start(close_button, False, False, 0) window.add(vbox) window.show_all() def prompt_password_and_connect(self, button, network): # Crear un diálogo para solicitar la contraseña dialog = Gtk.Dialog( title=f"Conectar a {network}", transient_for=button.get_toplevel(), flags=0 ) dialog.add_buttons( Gtk.STOCK_CANCEL, Gtk.ResponseType.CANCEL, Gtk.STOCK_OK, Gtk.ResponseType.OK ) # Crear un campo de entrada de texto para la contraseña box = dialog.get_content_area() label = Gtk.Label(label="Introduce la contraseña:") box.add(label) password_entry = Gtk.Entry() # Ocultar la contraseña al escribir password_entry.set_visibility(False) box.add(password_entry) dialog.show_all() # Ejecutar el diálogo y obtener la respuesta del usuario response = dialog.run() if response == Gtk.ResponseType.OK: password = password_entry.get_text() print(f"Intentando conectar a la red: { network} con la contraseña ingresada.") # Llamar a nmcli con la contraseña subprocess.run(['nmcli', 'device', 'wifi', 'connect', network, 'password', password]) self.show_notification(f"Intentando conectar a {network}") else: print("Conexión cancelada por el usuario.") dialog.destroy() def quit(self, _): Gtk.main_quit() def connect_wifi(self, _): ssid = self.get_available_ssid() if ssid: print(f"Conectando a {ssid}") subprocess.run(['nmcli', 'device', 'wifi', 'connect', ssid]) self.show_notification(f"Conectando a {ssid}...") else: print("No se encontraron redes WiFi disponibles.") self.show_notification("No se encontraron redes WiFi disponibles.") def disconnect_wifi(self, _): print("Desconectando WiFi...") subprocess.run(['nmcli', 'radio', 'wifi', 'off']) self.show_notification("WiFi Desconectado.") def get_available_ssid(self): try: output = subprocess.getoutput( 'nmcli -t -f SSID,SECURITY dev wifi list') print(f"Redes WiFi disponibles:\n{output}") networks = [line.split(':')[0] for line in output.split('\n') if line] networks = list(dict.fromkeys(filter(None, networks))) print(f"SSIDs filtrados: {networks}") if networks: return networks[0] return None except Exception as e: print(f"Error al obtener SSID disponibles: {e}", file=sys.stderr) return None def get_available_networks_list(self): try: output = subprocess.getoutput( 'nmcli -t -f SSID,SECURITY dev wifi list') networks = [line.split(':')[0] for line in output.split('\n') if line] return list(dict.fromkeys(filter(None, networks))) except Exception as e: print(f"Error al obtener la lista de redes disponibles: { e}", file=sys.stderr) return ["Error al obtener redes"] def show_notification(self, message): subprocess.run(['notify-send', message]) def update_status_initial(self): try: wifi_state = self.get_wifi_state() ssid = self.get_connected_ssid() self.set_indicator(wifi_state, ssid) except dbus.exceptions.DBusException as e: print(f"Error al obtener el estado inicial: {e}", file=sys.stderr) def on_properties_changed(self, interface, changed_properties, invalidated_properties): if 'State' in changed_properties: wifi_state = self.get_wifi_state() ssid = self.get_connected_ssid() self.set_indicator(wifi_state, ssid) def set_indicator(self, wifi_state, ssid): if wifi_state == 'disabled': icon = "/home/teraflops/Icons/wifi_disabled.png" tooltip = "WiFi Apagado" elif wifi_state == 'enabled' and not ssid: icon = "/home/teraflops/Icons/wifi_disconnected.png" tooltip = "WiFi Encendido (No Conectado)" elif wifi_state == 'enabled' and ssid: icon = "/home/teraflops/Icons/wifi_enabled.png" tooltip = f"Conectado a: {ssid}" else: icon = "/home/teraflops/Icons/wifi_unknown.png" tooltip = "WiFi Estado Desconocido" print(f"Actualizando indicador: Icono={icon}, Tooltip='{tooltip}'") self.indicator.set_icon_full(icon, tooltip) self.indicator.set_title(tooltip) self.indicator.set_secondary_activate_target(None) def get_wifi_state(self): try: output = subprocess.getoutput("nmcli radio wifi") print(f"Estado WiFi: {output}") return output.lower() except Exception as e: print(f"Error al obtener el estado de WiFi: {e}", file=sys.stderr) return "unknown" def get_connected_ssid(self): try: # Usar iwgetid para obtener el SSID conectado ssid = subprocess.getoutput("iwgetid -r").strip() if ssid: print(f"SSID conectado obtenido con iwgetid: {ssid}") return ssid else: # Fallback a nmcli si iwgetid no retorna SSID output = subprocess.getoutput( "nmcli -t -f ACTIVE,SSID dev wifi | grep '^yes' | cut -d':' -f2").strip() if output: print(f"SSID conectado obtenido con nmcli: {output}") return output else: print(f"SSID conectado obtenido: None") return None except Exception as e: print(f"Error al obtener SSID conectado: {e}", file=sys.stderr) return None def main(): NetworkMenu() Gtk.main() if __name__ == "__main__": main()