Files
dotfiles/.local/share/noctalia/plugins/system-status/network-rate.luau
T

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