215 lines
7.4 KiB
Python
215 lines
7.4 KiB
Python
import subprocess
|
|
from fabric import Fabricator
|
|
from fabric.widgets.label import Label
|
|
from fabric.widgets.box import Box
|
|
from fabric.bluetooth import BluetoothClient
|
|
from services import network_client, audio
|
|
import utils.icons as icons
|
|
from fabric.hyprland.widgets import Language, HyprlandEvent, get_hyprland_connection
|
|
|
|
|
|
class SystemIndicators(Box):
|
|
def __init__(self, **kwargs):
|
|
super().__init__(
|
|
orientation="h", spacing=2, name="system-indicator", **kwargs
|
|
)
|
|
|
|
self.bluetooth_icon = Label(name="system-indicator-icon")
|
|
self.wifi_icon = Label(name="system-indicator-icon")
|
|
self.volume_icon_button = Label(name="system-indicator-icon")
|
|
self.language_icon = Label(name="lang-label")
|
|
self.idle_label = Label(name="system-indicator-icon")
|
|
self.night_label = Label(name="system-indicator-icon")
|
|
self.power_label = Label(name="system-indicator-icon")
|
|
|
|
|
|
for widget in [
|
|
self.language_icon,
|
|
self.bluetooth_icon,
|
|
self.wifi_icon,
|
|
self.volume_icon_button,
|
|
self.idle_label,
|
|
self.night_label,
|
|
self.power_label,
|
|
]:
|
|
self.add(widget)
|
|
|
|
self.audio_service = audio
|
|
self.bluetooth_client = BluetoothClient()
|
|
self.network_client = network_client
|
|
self.hyprland = get_hyprland_connection()
|
|
|
|
# Connect signals
|
|
self.bluetooth_client.connect("changed", self.update_bluetooth_status)
|
|
self.audio_service.connect("changed", self.update_volume_status)
|
|
self.network_client.connect("device_ready", self.update_network_status)
|
|
self.hyprland.connect("event::activelayout", self.update_language)
|
|
|
|
|
|
if self.network_client.wifi_device:
|
|
self.network_client.wifi_device.connect(
|
|
"changed", self.update_network_status
|
|
)
|
|
|
|
if self.network_client.ethernet_device:
|
|
self.network_client.ethernet_device.connect(
|
|
"changed", self.update_network_status
|
|
)
|
|
|
|
Fabricator(interval=1000, poll_from=self.update_all_statuses)
|
|
|
|
# Initial updates
|
|
self.update_bluetooth_status()
|
|
self.update_language()
|
|
self.update_idle_night_status()
|
|
|
|
def update_all_statuses(self, *_args):
|
|
self.update_idle_night_status()
|
|
self.update_power_profile()
|
|
self.update_network_status()
|
|
|
|
def update_language(self, _=None, event: HyprlandEvent = None):
|
|
"""Update the language indicator based on Hyprland events."""
|
|
lang = event.data[1] if event else Language().get_label()
|
|
self.language_icon.set_tooltip_text(lang)
|
|
self.language_icon.set_label(lang[:2].lower())
|
|
|
|
def update_idle_night_status(self, *_):
|
|
# Update idle status
|
|
has_idle = (
|
|
subprocess.run(["pgrep", "-x", "wlinhibit"], capture_output=True).returncode
|
|
== 0
|
|
)
|
|
self.idle_label.set_visible(has_idle)
|
|
self.idle_label.set_markup(icons.coffee if has_idle else "")
|
|
|
|
# Update night mode status
|
|
has_night = (
|
|
subprocess.run(
|
|
["pgrep", "-x", "hyprsunset"], capture_output=True
|
|
).returncode
|
|
== 0
|
|
)
|
|
self.night_label.set_visible(has_night)
|
|
self.night_label.set_markup(icons.night if has_night else "")
|
|
|
|
def update_volume_status(self, *_):
|
|
stream = self.audio_service.speaker
|
|
if not stream:
|
|
self.volume_icon_button.set_visible(False)
|
|
return
|
|
|
|
self.volume_icon_button.set_visible(True)
|
|
# Normalize volume to integer percentage (0-100)
|
|
volume_level = stream.volume
|
|
if volume_level > 1:
|
|
volume_level = min(int(volume_level), 100)
|
|
else:
|
|
volume_level = int(volume_level * 100)
|
|
|
|
is_muted = stream.muted
|
|
|
|
if is_muted:
|
|
icon = icons.vol_off
|
|
elif volume_level > 74:
|
|
icon = icons.vol_high
|
|
elif volume_level > 0:
|
|
icon = icons.vol_medium
|
|
else:
|
|
icon = icons.vol_mute
|
|
|
|
self.volume_icon_button.set_markup(icon)
|
|
|
|
tooltip_text = "Muted" if is_muted else f"Volume: {volume_level}%"
|
|
self.volume_icon_button.set_tooltip_text(tooltip_text)
|
|
|
|
|
|
def update_bluetooth_status(self, *_):
|
|
if self.bluetooth_client.enabled:
|
|
self.bluetooth_icon.set_visible(True)
|
|
# Get a list of connected devices
|
|
connected_devices = [
|
|
device.name
|
|
for device in self.bluetooth_client.devices
|
|
if device.connected
|
|
]
|
|
|
|
if connected_devices:
|
|
self.bluetooth_icon.set_markup(icons.bluetooth_connected)
|
|
self.bluetooth_icon.set_tooltip_text(f"{', '.join(connected_devices)}")
|
|
else:
|
|
self.bluetooth_icon.set_markup(icons.bluetooth)
|
|
self.bluetooth_icon.set_tooltip_text(
|
|
"Bluetooth is enabled, no devices connected"
|
|
)
|
|
else:
|
|
self.bluetooth_icon.set_visible(True) # Keep it visible
|
|
self.bluetooth_icon.set_markup(icons.bluetooth_off)
|
|
self.bluetooth_icon.set_tooltip_text("Bluetooth is disabled")
|
|
|
|
def update_network_status(self, *_):
|
|
primary_device = self.network_client.primary_device
|
|
self.wifi_icon.set_visible(True)
|
|
|
|
if primary_device == "wifi" and self.network_client.wifi_device:
|
|
wifi_device = self.network_client.wifi_device
|
|
|
|
self.wifi_icon.set_tooltip_text(
|
|
wifi_device.ssid if wifi_device.ssid else "No WiFi Connection"
|
|
)
|
|
|
|
if wifi_device.enabled:
|
|
strength = wifi_device.strength
|
|
|
|
if strength > 0:
|
|
if strength < 25:
|
|
icon_label = icons.wifi_0
|
|
elif strength < 50:
|
|
icon_label = icons.wifi_1
|
|
elif strength < 75:
|
|
icon_label = icons.wifi_2
|
|
else:
|
|
icon_label = icons.wifi_3
|
|
else:
|
|
icon_label = icons.wifi_off
|
|
else:
|
|
icon_label = icons.wifi_off
|
|
|
|
elif primary_device == "wired":
|
|
icon_label = icons.lan
|
|
self.wifi_icon.set_tooltip_text("Wired Connection")
|
|
|
|
else:
|
|
icon_label = icons.wifi_off # No network
|
|
|
|
self.wifi_icon.set_markup(icon_label)
|
|
|
|
def update_power_profile(self, *_):
|
|
try:
|
|
result = subprocess.run(
|
|
["powerprofilesctl", "get"], capture_output=True, text=True
|
|
)
|
|
current_profile = result.stdout.strip()
|
|
|
|
profile_icons = {
|
|
"performance": icons.power_performance,
|
|
"balanced": "",
|
|
"power-saver": icons.power_saving,
|
|
}
|
|
|
|
icon = profile_icons.get(current_profile)
|
|
tooltip = (
|
|
f"Power Profile: {current_profile.capitalize()}"
|
|
if current_profile
|
|
else "Unknown Profile"
|
|
)
|
|
|
|
self.power_label.set_visible(bool(icon)) # Only show if there's an icon
|
|
self.power_label.set_markup(icon)
|
|
self.power_label.set_tooltip_text(tooltip)
|
|
|
|
except Exception as e:
|
|
self.power_label.set_visible(False)
|
|
self.power_label.set_tooltip_text(f"Error fetching power profile: {e}")
|
|
|