Add ping to network panel

This commit is contained in:
David Heinemeier Hansson
2026-06-28 20:11:21 -04:00
parent 6565a4032f
commit 2f4fc48791
4 changed files with 185 additions and 3 deletions
+59
View File
@@ -97,6 +97,57 @@ function throughputState(previous, next, now) {
}
}
function pingSampleValue(raw) {
var value = parseFloat(raw)
if (!isFinite(value) || value < 0) return null
return value
}
function appendPingSample(samples, raw, limit) {
var values = Array.isArray(samples) ? samples.slice() : []
values.push(pingSampleValue(raw))
while (values.length > limit) values.shift()
return values
}
function averagePingLatency(samples) {
var values = Array.isArray(samples) ? samples : []
var total = 0
var count = 0
for (var i = 0; i < values.length; i++) {
var value = values[i]
if (typeof value !== "number" || !isFinite(value) || value < 0) continue
total += value
count++
}
return count > 0 ? total / count : -1
}
function pingLatencyState(previous, next, limit) {
var prev = previous || {}
var sample = next || {}
var iface = sample.iface || ""
var window = Math.max(1, parseInt(limit, 10) || 5)
var reset = iface === "" || iface !== (prev.pingIface || "")
var routerSamples = reset ? [] : prev.routerPingSamples
var internetSamples = reset ? [] : prev.internetPingSamples
routerSamples = sample.router_ping_ms === undefined ? [] : appendPingSample(routerSamples, sample.router_ping_ms, window)
internetSamples = sample.internet_ping_ms === undefined ? [] : appendPingSample(internetSamples, sample.internet_ping_ms, window)
return {
pingIface: iface,
routerPingSamples: routerSamples,
internetPingSamples: internetSamples,
routerPingLatency: averagePingLatency(routerSamples),
internetPingLatency: averagePingLatency(internetSamples)
}
}
function formatBytes(bytes) {
var n = Number(bytes)
if (!isFinite(n) || n < 0) n = 0
@@ -110,6 +161,12 @@ function formatRate(bytesPerSec) {
return formatBytes(bytesPerSec) + "/s"
}
function formatPingLatency(ms) {
var value = parseFloat(ms)
if (!isFinite(value) || value < 0) return "Timeout"
return value.toFixed(value > 0 && value < 10 ? 1 : 0) + " ms"
}
function wifiRow(network) {
if (!network) return null
return {
@@ -168,8 +225,10 @@ if (typeof module !== "undefined") {
headerDetail: headerDetail,
parseKeyValue: parseKeyValue,
throughputState: throughputState,
pingLatencyState: pingLatencyState,
formatBytes: formatBytes,
formatRate: formatRate,
formatPingLatency: formatPingLatency,
wifiRow: wifiRow,
sortWifiRows: sortWifiRows,
wifiSectionTitle: wifiSectionTitle,
+54 -1
View File
@@ -20,7 +20,7 @@ Panel {
}
// Live connection details from `ip` / /sys / iw.
property var info: ({}) // { iface, type, ip, prefix, gateway, speed, duplex, ssid, signal, freq, bitrate, rx_bytes, tx_bytes }
property var info: ({}) // { iface, type, ip, prefix, gateway, speed, duplex, ssid, signal, freq, bitrate, rx_bytes, tx_bytes, router_ping_ms, internet_ping_ms }
// Throughput tracking. Rates are computed as deltas between successive
// `omarchy-network-status --verbose` samples (~1.5s apart via detailsPoll).
@@ -32,6 +32,18 @@ Panel {
property string prevIface: ""
property real downloadRate: 0 // bytes/sec
property real uploadRate: 0 // bytes/sec
property string pingIface: ""
property var routerPingSamples: []
property var internetPingSamples: []
property real routerPingLatency: -1
property real internetPingLatency: -1
readonly property int pingWindow: 5
readonly property bool hasRouterPing: routerPingSamples.length > 0
readonly property bool hasInternetPing: internetPingSamples.length > 0
readonly property bool hasPing: hasRouterPing || hasInternetPing
readonly property bool hasSecondPing: hasRouterPing && hasInternetPing
readonly property string primaryPingLabel: hasRouterPing ? "Router Ping" : (hasInternetPing ? "Internet Ping" : "")
readonly property real primaryPingLatency: hasRouterPing ? routerPingLatency : internetPingLatency
property int connectionPhraseIndex: 0
readonly property var connectionPhrases: [
"Wiring bits",
@@ -141,6 +153,11 @@ Panel {
prevSampleTime = 0
downloadRate = 0
uploadRate = 0
pingIface = ""
routerPingSamples = []
internetPingSamples = []
routerPingLatency = -1
internetPingLatency = -1
if (wifiDevice) wifiDevice.scannerEnabled = false
}
}
@@ -279,6 +296,7 @@ Panel {
var next = Model.parseKeyValue(raw)
info = next
updateThroughput(next)
updatePingLatency(next)
}
function updateThroughput(next) {
@@ -299,6 +317,20 @@ Panel {
uploadRate = state.uploadRate
}
function updatePingLatency(next) {
var state = Model.pingLatencyState({
pingIface: pingIface,
routerPingSamples: routerPingSamples,
internetPingSamples: internetPingSamples
}, next, pingWindow)
pingIface = state.pingIface
routerPingSamples = state.routerPingSamples
internetPingSamples = state.internetPingSamples
routerPingLatency = state.routerPingLatency
internetPingLatency = state.internetPingLatency
}
function formatBytes(bytes) {
return Model.formatBytes(bytes)
}
@@ -307,6 +339,10 @@ Panel {
return Model.formatRate(bytesPerSec)
}
function formatPingLatency(ms) {
return Model.formatPingLatency(ms)
}
function findDevice(type) {
var devices = networkDevices || []
for (var i = 0; i < devices.length; i++) {
@@ -802,6 +838,23 @@ Panel {
columnSpacing: Style.space(20)
rowSpacing: Style.spacing.labelGap
InfoLabel {
visible: root.hasPing
text: root.primaryPingLabel
}
DetailValue {
visible: root.hasPing
text: root.formatPingLatency(root.primaryPingLatency)
}
InfoLabel {
visible: root.hasPing
text: root.hasSecondPing ? "Internet Ping" : ""
}
DetailValue {
visible: root.hasPing
text: root.hasSecondPing ? root.formatPingLatency(root.internetPingLatency) : ""
}
InfoLabel { visible: root.info.rx_bytes !== undefined; text: "Receiving" }
DetailValue { visible: root.info.rx_bytes !== undefined; text: root.formatRate(root.downloadRate) }
InfoLabel { visible: root.info.rx_bytes !== undefined; text: "Sending" }