dotfiles/.local/bin/network_menu.py
2025-05-28 18:33:04 +02:00

269 lines
9.7 KiB
Python
Executable File

#!/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()