Files
Modus/modules/dashboard/network.py
T

292 lines
10 KiB
Python

from fabric.utils import bulk_connect
from fabric.widgets.box import Box
from fabric.widgets.button import Button
from fabric.widgets.centerbox import CenterBox
from fabric.widgets.image import Image
from fabric.widgets.label import Label
from fabric.widgets.scrolledwindow import ScrolledWindow
from gi.repository import Gtk
from modules.dashboard.tile import Tile
from services.network import NetworkClient
import utils.icons as icons
class WifiAccessPointSlot(CenterBox):
"""A widget representing a single WiFi access point in the dashboard."""
def __init__(self, access_point, network_service: NetworkClient, wifi_service, **kwargs):
super().__init__(name="wifi-ap-slot", **kwargs)
self.access_point = access_point
self.network_service = network_service
self.wifi_service = wifi_service
ssid = access_point.ssid
icon_name = access_point.icon
self.is_active = access_point.is_active
self.ap_icon = Image(icon_name=icon_name, size=24)
self.ap_label = Label(
label=ssid,
h_expand=True,
h_align="start",
ellipsization="end"
)
self.connect_button = Button(
name="wifi-connect-button",
label="Connected" if self.is_active else "Connect",
sensitive=not self.is_active,
on_clicked=self._on_connect_clicked,
style_classes=["connected"] if self.is_active else None,
)
self.set_start_children([
Box(spacing=8, h_expand=True, h_align="fill", children=[
self.ap_icon,
self.ap_label,
])
])
self.set_end_children([self.connect_button])
def _on_connect_clicked(self, *_):
"""Handle connect button click."""
if not self.is_active and self.access_point.bssid:
self.connect_button.set_label("Connecting...")
self.connect_button.set_sensitive(False)
self.network_service.connect_wifi_bssid(self.access_point.bssid)
class Network(Tile):
"""Network tile for the dashboard that shows WiFi status and networks."""
def __init__(self, **kwargs):
# Create status label
self.label = Label(
style_classes=["desc-label", "off"],
label="Disconnected",
h_align="start",
)
self.state = False
super().__init__(
label="Wi-Fi",
props=self.label,
markup=icons.wifi,
menu=True,
**kwargs,
)
# Initialize network client
self.network_client = NetworkClient()
self.network_client.connect("changed", self.handle_connection_change)
self.network_client.connect("ready", self.handle_connection_change)
self.network_client.connect("wifi_device_added", self._on_wifi_device_added)
# Initialize state
self._update_tile_state()
# Track if content has been created to avoid showing status during init
self._content_created = False
# Check if WiFi device is already available
if self.network_client.wifi_device:
self._setup_wifi_device()
def handle_connection_change(self, *_):
"""Handle network connection state changes."""
self._update_tile_state()
def _update_tile_state(self):
"""Update tile visual state based on WiFi status."""
connected = False
con_label = "Disconnected"
if self.network_client.wifi_device and self.network_client.wifi_device.active_access_point:
connected = True
con_label = self.network_client.wifi_device.active_access_point.ssid
elif self.network_client.wifi_device and not self.network_client.wifi_device.wireless_enabled:
con_label = "Off"
if self.state != connected:
self.state = connected
if self.state:
self.remove_style_class("off")
self.add_style_class("on")
else:
self.remove_style_class("on")
self.add_style_class("off")
print("WiFi state change:", connected)
self.label.set_label(con_label)
def create_content(self):
"""Create the detailed network content for the dashboard."""
# Create status label
self.status_label = Label(
label="Initializing ",
h_expand=True,
visible=False,
h_align="center"
)
# Create refresh button with icon
self.refresh_button_icon = Label(
name="network-refresh-label",
markup=icons.reload
)
self.refresh_button = Button(
name="network-refresh",
child=self.refresh_button_icon,
tooltip_text="Scan for Wi-Fi networks",
on_clicked=self._refresh_access_points
)
# Create back button with chevron left icon
self.back_button_icon = Label(
name="network-back-label",
markup=icons.chevron_left
)
self.back_button = Button(
name="network-back",
child=self.back_button_icon,
tooltip_text="Back to notifications",
on_clicked=self._on_back_clicked
)
# Create header
header_box = CenterBox(
name="network-header",
start_children=[self.back_button],
center_children=[Label(name="network-title", label="Wi-Fi Networks")],
end_children=[self.refresh_button]
)
# Create access points list
self.ap_list_box = Box(orientation="v", spacing=4)
scrolled_window = ScrolledWindow(
name="network-ap-scrolled-window",
child=self.ap_list_box,
h_expand=True,
v_expand=True,
propagate_width=False,
propagate_height=False,
)
# Main container
main_container = Box(
name="network-content",
orientation="v",
spacing=8,
h_expand=True,
v_expand=True,
h_align="fill",
style_classes=["tile-content"],
children=[
header_box,
self.status_label,
scrolled_window,
]
)
# Initialize button states
self.refresh_button.set_sensitive(False)
# Mark content as created
self._content_created = True
# Now update UI state since content is ready
if self.network_client.wifi_device:
self._update_wifi_status_ui()
return main_container
def _on_wifi_device_added(self, *_):
"""Handle when WiFi device is added."""
self._setup_wifi_device()
def _setup_wifi_device(self):
"""Setup WiFi device connections and initial state."""
if self.network_client.wifi_device:
self.network_client.wifi_device.connect("changed", self._load_access_points)
self.network_client.wifi_device.connect("notify::wireless-enabled", self._update_wifi_status_ui)
# Only update UI if content has been created
if self._content_created and hasattr(self, 'refresh_button'):
self._update_wifi_status_ui()
else:
# Only show error if content has been created
if self._content_created and hasattr(self, 'status_label'):
self.status_label.set_label("Wi-Fi device not available.")
self.status_label.set_visible(True)
if hasattr(self, 'refresh_button'):
self.refresh_button.set_sensitive(False)
def _update_wifi_status_ui(self, *_):
"""Update WiFi status UI elements."""
if not hasattr(self, 'refresh_button') or not self._content_created:
return # Content not created yet
if self.network_client.wifi_device:
enabled = self.network_client.wifi_device.wireless_enabled
self.refresh_button.set_sensitive(enabled)
if not enabled:
self.status_label.set_label("Wi-Fi disabled.")
self.status_label.set_visible(True)
self._clear_ap_list()
else:
self.refresh_button.set_sensitive(False)
def _on_back_clicked(self, *_):
"""Handle back button click - return to default (notifications) view."""
if self.dashboard_instance:
self.dashboard_instance.reset_to_default()
def _refresh_access_points(self, *_):
"""Refresh access points list."""
if self.network_client.wifi_device and self.network_client.wifi_device.wireless_enabled:
self.status_label.set_label("Scanning for Wi-Fi networks...")
self.status_label.set_visible(True)
self._clear_ap_list()
self.network_client.wifi_device.scan()
return False
def _clear_ap_list(self):
"""Clear the access points list."""
for child in self.ap_list_box.get_children():
child.destroy()
def _load_access_points(self, *_):
"""Load and display access points."""
if not hasattr(self, 'ap_list_box'):
return # Content not created yet
if not self.network_client.wifi_device or not self.network_client.wifi_device.wireless_enabled:
self._clear_ap_list()
self.status_label.set_label("Wi-Fi disabled.")
self.status_label.set_visible(True)
return
self._clear_ap_list()
access_points = self.network_client.wifi_device.access_points
if not access_points:
self.status_label.set_label("No Wi-Fi networks found.")
self.status_label.set_visible(True)
else:
self.status_label.set_visible(False)
# Sort access points by connection status (connected first) then by signal strength
sorted_aps = sorted(access_points, key=lambda x: (not x.is_active, -x.strength))
for access_point in sorted_aps:
slot = WifiAccessPointSlot(
access_point, self.network_client, self.network_client.wifi_device
)
self.ap_list_box.add(slot)
self.ap_list_box.show_all()