ed063ab1fe
Capture desktop-specific system, session, Noctalia, and Pi integration alongside shared laptop support. Retire obsolete X11 configuration and make package ownership explicit for repeatable Decman deployments.
120 lines
3.5 KiB
Lua
120 lines
3.5 KiB
Lua
-- Compact, two-line network throughput widget. It samples the interface used
|
|
-- by the default route so virtual and physical interfaces are not double-counted.
|
|
local command = [[
|
|
iface=$(ip route show default 2>/dev/null | awk '$1 == "default" { for (i = 1; i <= NF; i++) if ($i == "dev") { print $(i + 1); exit } }')
|
|
|
|
if [ -z "$iface" ]; then
|
|
for path in /sys/class/net/*; do
|
|
candidate=${path##*/}
|
|
[ "$candidate" = "lo" ] && continue
|
|
[ "$(cat "$path/carrier" 2>/dev/null)" = "1" ] || continue
|
|
iface=$candidate
|
|
break
|
|
done
|
|
fi
|
|
|
|
[ -n "$iface" ] || exit 1
|
|
awk -v iface="$iface" '$1 ~ ("^" iface ":") { sub(":", "", $1); print iface "\t" $2 "\t" $10; exit }' /proc/net/dev
|
|
]]
|
|
|
|
local previous = nil
|
|
local request_in_flight = false
|
|
-- 82px accommodates the longest fixed field ("1023 KiB/s") at 10px while
|
|
-- removing the excess side padding from the original 112px capsule.
|
|
local label_width = 82
|
|
|
|
local function formatRate(bytes_per_second)
|
|
local units = { "B/s", "KiB/s", "MiB/s", "GiB/s" }
|
|
local value = bytes_per_second
|
|
local unit_index = 1
|
|
|
|
while value >= 1024 and unit_index < #units do
|
|
value = value / 1024
|
|
unit_index = unit_index + 1
|
|
end
|
|
|
|
if unit_index == 1 then
|
|
return string.format("%d %s", math.floor(value + 0.5), units[unit_index])
|
|
end
|
|
|
|
if value >= 100 then
|
|
return string.format("%.0f %s", value, units[unit_index])
|
|
end
|
|
|
|
return string.format("%.1f %s", value, units[unit_index])
|
|
end
|
|
|
|
local function renderLines(download, upload)
|
|
-- The explicit minimum width and fixed ten-character value field keep the
|
|
-- capsule width stable while inheriting the bar font used by other modules.
|
|
barWidget.render(ui.column({ gap = 0, align = "center", minWidth = label_width }, {
|
|
ui.label({
|
|
text = string.format("↓ %10s", download),
|
|
maxLines = 1,
|
|
fontSize = 10,
|
|
}),
|
|
ui.label({
|
|
text = string.format("↑ %10s", upload),
|
|
maxLines = 1,
|
|
fontSize = 10,
|
|
}),
|
|
}))
|
|
end
|
|
|
|
function update()
|
|
noctalia.setUpdateInterval(2000)
|
|
|
|
if request_in_flight then
|
|
return
|
|
end
|
|
|
|
request_in_flight = true
|
|
noctalia.runAsync(command, function(result)
|
|
request_in_flight = false
|
|
|
|
if result.exitCode ~= 0 then
|
|
previous = nil
|
|
renderLines("—", "—")
|
|
barWidget.setTooltip("Network throughput unavailable: no active interface")
|
|
return
|
|
end
|
|
|
|
local iface, received, sent = string.match(
|
|
result.stdout,
|
|
"^([^\t]+)\t(%d+)\t(%d+)"
|
|
)
|
|
received = tonumber(received)
|
|
sent = tonumber(sent)
|
|
|
|
if iface == nil or received == nil or sent == nil then
|
|
previous = nil
|
|
renderLines("—", "—")
|
|
barWidget.setTooltip("Network throughput unavailable")
|
|
return
|
|
end
|
|
|
|
local now = os.time()
|
|
if previous == nil or previous.iface ~= iface or now <= previous.time then
|
|
previous = { iface = iface, received = received, sent = sent, time = now }
|
|
renderLines("Sampling…", "Sampling…")
|
|
barWidget.setTooltip("Sampling network throughput on " .. iface)
|
|
return
|
|
end
|
|
|
|
local elapsed = now - previous.time
|
|
local download = math.max(0, (received - previous.received) / elapsed)
|
|
local upload = math.max(0, (sent - previous.sent) / elapsed)
|
|
previous = { iface = iface, received = received, sent = sent, time = now }
|
|
|
|
renderLines(formatRate(download), formatRate(upload))
|
|
barWidget.setTooltip(
|
|
string.format(
|
|
"%s · Download %s · Upload %s",
|
|
iface,
|
|
formatRate(download),
|
|
formatRate(upload)
|
|
)
|
|
)
|
|
end)
|
|
end
|