diff --git a/modules/home/wayland/apps/eww/bar/css/_net.scss b/modules/home/wayland/apps/eww/bar/css/_net.scss index d457638..1ae5ee6 100644 --- a/modules/home/wayland/apps/eww/bar/css/_net.scss +++ b/modules/home/wayland/apps/eww/bar/css/_net.scss @@ -1,9 +1,24 @@ -.net-icon { - font-size: 14px; - padding: 3pt 0; -} - +// Bar icons +.net-icon { font-size: 14px; padding: 3pt 0; } .net-active { color: $base07; } .net-dim { color: $base02; } .blt-on { color: $base07; } .blt-connected { color: $base0D; } + +// Popup window section accents +.wifi-accent { background-color: $base0C; } +.eth-accent { background-color: $base0B; } +.blt-accent { background-color: $base0D; } + +// Netinfo rows +.netinfo-row { margin-bottom: 3pt; } +.netinfo-label { font-size: 0.72em; color: $base04; min-width: 60px; } +.netinfo-value { font-size: 0.72em; color: $base05; } +.netinfo-dim { font-size: 0.72em; color: $base03; margin-bottom: 4pt; } + +// Bluetooth device list +.bt-device-row { margin-bottom: 4pt; } +.bt-device-name { font-size: 0.78em; color: $base05; } +.bt-device-btn { padding: 2pt 6pt; border-radius: $border-radius; font-size: 1em; } +.bt-btn-on { color: $base0D; } +.bt-btn-off { color: $base03; } diff --git a/modules/home/wayland/apps/eww/bar/eww.yuck b/modules/home/wayland/apps/eww/bar/eww.yuck index 81f7b7a..f60e40c 100644 --- a/modules/home/wayland/apps/eww/bar/eww.yuck +++ b/modules/home/wayland/apps/eww/bar/eww.yuck @@ -7,6 +7,7 @@ (include "windows/calendar.yuck") (include "windows/sys.yuck") +(include "windows/net.yuck") (include "windows/radio.yuck") (include "windows/powermenu.yuck") diff --git a/modules/home/wayland/apps/eww/bar/modules/net.yuck b/modules/home/wayland/apps/eww/bar/modules/net.yuck index 24b2bbf..60dcc80 100644 --- a/modules/home/wayland/apps/eww/bar/modules/net.yuck +++ b/modules/home/wayland/apps/eww/bar/modules/net.yuck @@ -8,6 +8,8 @@ (defwidget net-mod [] (module + (eventbox + :onclick "(sleep 0.1 && eww-open-on-current-screen net --toggle)" (box :orientation "v" (label :class "net-icon ${net.ethernet.connected ? 'net-active' : 'net-dim'}" @@ -20,4 +22,4 @@ (label :class "net-icon ${bluetooth.connected ? 'blt-connected' : bluetooth.powered ? 'blt-on' : 'net-dim'}" :tooltip {bluetooth.connected ? "Bluetooth: ${bluetooth.device}" : bluetooth.powered ? "Bluetooth: On" : "Bluetooth: Off"} - :text {bluetooth.connected ? "󰂱" : bluetooth.powered ? "󰂯" : "󰂲"})))) + :text {bluetooth.connected ? "󰂱" : bluetooth.powered ? "󰂯" : "󰂲"}))))) diff --git a/modules/home/wayland/apps/eww/bar/scripts/net/bt-devices b/modules/home/wayland/apps/eww/bar/scripts/net/bt-devices new file mode 100755 index 0000000..a2826b9 --- /dev/null +++ b/modules/home/wayland/apps/eww/bar/scripts/net/bt-devices @@ -0,0 +1,23 @@ +#!/usr/bin/env bash + +emit() { + local first=true arr="" + while IFS= read -r line; do + local mac name connected + mac=$(echo "$line" | awk '{ print $2 }') + name=$(echo "$line" | awk '{ $1=$2=""; sub(/^ +/, ""); print }') + info=$(bluetoothctl info "$mac" 2>/dev/null) + connected=$(echo "$info" | grep -q "Connected: yes" && echo true || echo false) + $first || arr="${arr}," + arr="${arr}{\"mac\":\"${mac}\",\"name\":\"${name}\",\"connected\":${connected}}" + first=false + done < <(bluetoothctl devices 2>/dev/null) + echo "[${arr}]" +} + +emit +bluetoothctl monitor 2>/dev/null | while IFS= read -r line; do + case "$line" in + *"Powered"*|*"Connected"*|*"Device"*) emit ;; + esac +done diff --git a/modules/home/wayland/apps/eww/bar/scripts/net/bt-toggle b/modules/home/wayland/apps/eww/bar/scripts/net/bt-toggle new file mode 100755 index 0000000..3ad96f7 --- /dev/null +++ b/modules/home/wayland/apps/eww/bar/scripts/net/bt-toggle @@ -0,0 +1,7 @@ +#!/usr/bin/env bash +mac="$1" +if bluetoothctl info "$mac" 2>/dev/null | grep -q "Connected: yes"; then + bluetoothctl disconnect "$mac" +else + bluetoothctl connect "$mac" +fi diff --git a/modules/home/wayland/apps/eww/bar/scripts/net/netinfo b/modules/home/wayland/apps/eww/bar/scripts/net/netinfo new file mode 100755 index 0000000..b613bdc --- /dev/null +++ b/modules/home/wayland/apps/eww/bar/scripts/net/netinfo @@ -0,0 +1,64 @@ +#!/usr/bin/env bash + +get_wifi_iface() { + awk 'NR > 2 { gsub(":", "", $1); print $1; exit }' /proc/net/wireless +} + +freq_band() { + local f="$1" + if [ "$f" -ge 6000 ] 2>/dev/null; then echo "6 GHz" + elif [ "$f" -ge 5000 ] 2>/dev/null; then echo "5 GHz" + elif [ "$f" -ge 2400 ] 2>/dev/null; then echo "2.4 GHz" + else echo ""; fi +} + +wifi_gen_label() { + case "$1" in + 7) echo "Wi-Fi 7" ;; 6) echo "Wi-Fi 6" ;; + 5) echo "Wi-Fi 5" ;; 4) echo "Wi-Fi 4" ;; + *) echo "" ;; + esac +} + +make_content() { + local wifi_iface eth_iface + wifi_iface=$(get_wifi_iface) + eth_iface=$(ip link | awk '/^[0-9]+: en[po]/ { gsub(":",""); print $2; exit }') + + local wifi_ssid="" wifi_ip="" wifi_freq=0 wifi_band="" wifi_gen="" wifi_signal=0 + if [ -n "$wifi_iface" ] && ip -4 addr show "$wifi_iface" 2>/dev/null | grep -q "inet "; then + local wpa + wpa=$(wpa_cli -g "/run/wpa_supplicant/$wifi_iface" status 2>/dev/null) + wifi_ssid=$(echo "$wpa" | awk -F= '/^ssid=/ { print $2 }') + wifi_ip=$(ip -4 addr show "$wifi_iface" | awk '/inet / { print $2 }') + wifi_freq=$(echo "$wpa" | awk -F= '/^freq=/ { print $2 }') + local gen + gen=$(echo "$wpa" | awk -F= '/^wifi_generation=/ { print $2 }') + wifi_band=$(freq_band "$wifi_freq") + wifi_gen=$(wifi_gen_label "$gen") + wifi_signal=$(awk -v iface="$wifi_iface" \ + '$1 == iface ":" { gsub(/\./, "", $4); print $4; exit }' /proc/net/wireless) + fi + + local eth_ip="" eth_speed="" eth_state="down" + if [ -n "$eth_iface" ]; then + eth_state=$(ip link show "$eth_iface" 2>/dev/null | awk '/state/ { print tolower($9) }') + if [ "$eth_state" = "up" ]; then + eth_ip=$(ip -4 addr show "$eth_iface" | awk '/inet / { print $2 }') + local spd + spd=$(cat /sys/class/net/"$eth_iface"/speed 2>/dev/null) + [ "${spd:-0}" -gt 0 ] 2>/dev/null && eth_speed="${spd} Mbps" + fi + fi + + # shellcheck disable=SC2059 + printf '{"wifi":{"ssid":"%s","ip":"%s","freq":%s,"band":"%s","gen":"%s","signal":%s},' \ + "$wifi_ssid" "$wifi_ip" "${wifi_freq:-0}" "$wifi_band" "$wifi_gen" "${wifi_signal:-0}" + printf '"ethernet":{"state":"%s","ip":"%s","speed":"%s","interface":"%s"}}\n' \ + "$eth_state" "$eth_ip" "$eth_speed" "${eth_iface:-}" +} + +make_content +ip monitor | while read -r _; do + make_content +done diff --git a/modules/home/wayland/apps/eww/bar/windows/net.yuck b/modules/home/wayland/apps/eww/bar/windows/net.yuck new file mode 100644 index 0000000..7ac8913 --- /dev/null +++ b/modules/home/wayland/apps/eww/bar/windows/net.yuck @@ -0,0 +1,82 @@ + +(defpoll netinfo + :interval "5s" + :initial '{"wifi":{"ssid":"","ip":"","freq":0,"band":"","gen":"","signal":0},"ethernet":{"state":"down","ip":"","speed":"","interface":""}}' + "scripts/net/netinfo") + +(deflisten bt-devices + :initial "[]" + "scripts/net/bt-devices") + +; --- Shared row widget --- + +(defwidget netinfo-row [label value] + (box :orientation "h" :space-evenly false :class "netinfo-row" + (label :class "netinfo-label" :halign "start" :text label) + (label :class "netinfo-value" :halign "end" :hexpand true :text value))) + +; --- WiFi --- + +(defwidget wifi-net-section [] + (box :orientation "v" :space-evenly false :class "sys-section" + (section-header :title "WiFi" :accent "wifi-accent") + (box :orientation "v" :space-evenly false + :visible {net.wifi.connected} + (netinfo-row :label "SSID" :value {netinfo.wifi.ssid}) + (netinfo-row :label "IP" :value {netinfo.wifi.ip}) + (netinfo-row :label "Signal" :value "${net.wifi.icon} ${netinfo.wifi.signal} dBm") + (netinfo-row :label "Freq" :value "${netinfo.wifi.freq} MHz · ${netinfo.wifi.band} · ${netinfo.wifi.gen}")) + (label :class "netinfo-dim" :halign "start" + :visible {!net.wifi.connected} + :text "Not connected"))) + +; --- Ethernet --- + +(defwidget ethernet-net-section [] + (box :orientation "v" :space-evenly false :class "sys-section" + (section-header :title "Ethernet" :accent "eth-accent") + (box :orientation "v" :space-evenly false + :visible {net.ethernet.connected} + (netinfo-row :label "Interface" :value {netinfo.ethernet.interface}) + (netinfo-row :label "IP" :value {netinfo.ethernet.ip}) + (netinfo-row :label "Speed" :value {netinfo.ethernet.speed})) + (label :class "netinfo-dim" :halign "start" + :visible {!net.ethernet.connected} + :text "No carrier"))) + +; --- Bluetooth --- + +(defwidget bt-device-row [device] + (box :orientation "h" :space-evenly false :class "bt-device-row" :valign "center" + (label :class "bt-device-name" :hexpand true :halign "start" :text {device.name}) + (button + :class "bt-device-btn ${device.connected ? 'bt-btn-on' : 'bt-btn-off'}" + :onclick "scripts/net/bt-toggle ${device.mac}" + :tooltip {device.connected ? "Disconnect" : "Connect"} + (label :text {device.connected ? "󰂱" : "󰂯"})))) + +(defwidget bluetooth-net-section [] + (box :orientation "v" :space-evenly false :class "sys-section" + (section-header :title "Bluetooth" :accent "blt-accent") + (box :orientation "v" :space-evenly false + (for device in {bt-devices} + (bt-device-row :device {device}))))) + +; --- Root --- + +(defwidget net-win [] + (box :class "sys-win" :space-evenly false :orientation "v" + (wifi-net-section) + (box :class "section-sep") + (ethernet-net-section) + (box :class "section-sep") + (bluetooth-net-section))) + +(defwindow net + :monitor 0 + :stacking "overlay" + :geometry (geometry + :x "0%" :y "0%" + :anchor "bottom right" + :width "300px" :height "0px") + (window (net-win)))