Compare commits
2
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
0a0d8130d0 | ||
|
|
e5e1d34e5c |
@@ -15,9 +15,9 @@ QtObject {
|
|||||||
property string uploadSpeed: ""
|
property string uploadSpeed: ""
|
||||||
property string ping: ""
|
property string ping: ""
|
||||||
|
|
||||||
property var downloadHistory: []
|
property var downloadHistory: [{time: Date.now() - 60000, value: 0}, {time: Date.now(), value: 0}]
|
||||||
property var uploadHistory: []
|
property var uploadHistory: [{time: Date.now() - 60000, value: 0}, {time: Date.now(), value: 0}]
|
||||||
property var pingHistory: []
|
property var pingHistory: [{time: Date.now() - 60000, value: 0}, {time: Date.now(), value: 0}]
|
||||||
property int _maxHistory: 60
|
property int _maxHistory: 60
|
||||||
property bool internetAvailable: Networking.connectivity === NetworkConnectivity.Full
|
property bool internetAvailable: Networking.connectivity === NetworkConnectivity.Full
|
||||||
property bool vpnConnected: false
|
property bool vpnConnected: false
|
||||||
@@ -34,6 +34,7 @@ QtObject {
|
|||||||
property real _prevTxBytes: 0
|
property real _prevTxBytes: 0
|
||||||
property var _prevUpdateTime: 0
|
property var _prevUpdateTime: 0
|
||||||
property string _trafficActiveDevice: ""
|
property string _trafficActiveDevice: ""
|
||||||
|
property string _lastNetworkSignature: ""
|
||||||
|
|
||||||
readonly property var networking: Networking
|
readonly property var networking: Networking
|
||||||
|
|
||||||
@@ -53,11 +54,32 @@ QtObject {
|
|||||||
return ""
|
return ""
|
||||||
}
|
}
|
||||||
|
|
||||||
property var _pendingDevices: []
|
|
||||||
|
|
||||||
function updateAll() {
|
function updateAll() {
|
||||||
|
updateNetworks(true)
|
||||||
|
updateStats()
|
||||||
|
}
|
||||||
|
|
||||||
|
function updateNetworks(force) {
|
||||||
var connected = findAllConnectedDevices()
|
var connected = findAllConnectedDevices()
|
||||||
_pendingDevices = connected
|
|
||||||
|
var sig = ""
|
||||||
|
for (var i = 0; i < connected.length; i++) {
|
||||||
|
var dev = connected[i]
|
||||||
|
sig += dev.name + ":" + dev.type + ":" + dev.connected
|
||||||
|
if (dev.type === DeviceType.Wifi) {
|
||||||
|
var nets = dev.networks.values
|
||||||
|
for (var j = 0; j < nets.length; j++) {
|
||||||
|
if (nets[j].connected) {
|
||||||
|
sig += ":" + nets[j].name
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
sig += ";"
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!force && sig === _lastNetworkSignature) return
|
||||||
|
_lastNetworkSignature = sig
|
||||||
|
|
||||||
activeConnections.clear()
|
activeConnections.clear()
|
||||||
var hasWifi = false
|
var hasWifi = false
|
||||||
@@ -109,7 +131,6 @@ QtObject {
|
|||||||
|
|
||||||
if (trafficDev) {
|
if (trafficDev) {
|
||||||
_trafficActiveDevice = trafficDev.name
|
_trafficActiveDevice = trafficDev.name
|
||||||
trafficScript.running = true
|
|
||||||
}
|
}
|
||||||
|
|
||||||
root.computeStatus(hasWired, hasWifi, bestWifiSignal)
|
root.computeStatus(hasWired, hasWifi, bestWifiSignal)
|
||||||
@@ -117,13 +138,27 @@ QtObject {
|
|||||||
if (connected.length > 0) {
|
if (connected.length > 0) {
|
||||||
ipv4Script.running = true
|
ipv4Script.running = true
|
||||||
}
|
}
|
||||||
|
|
||||||
|
networkDetailScript.running = true
|
||||||
|
}
|
||||||
|
|
||||||
|
function updateStats() {
|
||||||
|
if (!_trafficActiveDevice) return
|
||||||
|
statsScript.running = true
|
||||||
}
|
}
|
||||||
|
|
||||||
property Timer networkTimer: Timer {
|
property Timer networkTimer: Timer {
|
||||||
interval: 10000
|
interval: 5000
|
||||||
repeat: true
|
repeat: true
|
||||||
running: true
|
running: true
|
||||||
onTriggered: root.updateAll()
|
onTriggered: root.updateNetworks()
|
||||||
|
}
|
||||||
|
|
||||||
|
property Timer statsTimer: Timer {
|
||||||
|
interval: 1000
|
||||||
|
repeat: true
|
||||||
|
running: true
|
||||||
|
onTriggered: root.updateStats()
|
||||||
}
|
}
|
||||||
|
|
||||||
property Process ipv4Script: Process {
|
property Process ipv4Script: Process {
|
||||||
@@ -161,16 +196,8 @@ QtObject {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
property Process trafficScript: Process {
|
property Process networkDetailScript: Process {
|
||||||
command: ["sh", "-c",
|
command: ["sh", "-c",
|
||||||
"echo '===TRAFFIC==='; " +
|
|
||||||
"DEV=\"" + root._trafficActiveDevice + "\"; " +
|
|
||||||
"if [ -n \"$DEV\" ]; then " +
|
|
||||||
" echo \"RX_BYTES=$(cat /sys/class/net/$DEV/statistics/rx_bytes 2>/dev/null || echo 0)\"; " +
|
|
||||||
" echo \"TX_BYTES=$(cat /sys/class/net/$DEV/statistics/tx_bytes 2>/dev/null || echo 0)\"; " +
|
|
||||||
"fi; " +
|
|
||||||
"echo '===PING==='; " +
|
|
||||||
"ping -c 1 -W 2 1.1.1.1 2>/dev/null | awk -F/ '/rtt/{print $5}' || echo ''; " +
|
|
||||||
"echo '===VPN_ALL==='; " +
|
"echo '===VPN_ALL==='; " +
|
||||||
"nmcli -t -f NAME,TYPE connection show 2>/dev/null | grep -i vpn || true; " +
|
"nmcli -t -f NAME,TYPE connection show 2>/dev/null | grep -i vpn || true; " +
|
||||||
"echo '===VPN_ACTIVE==='; " +
|
"echo '===VPN_ACTIVE==='; " +
|
||||||
@@ -182,7 +209,117 @@ QtObject {
|
|||||||
]
|
]
|
||||||
|
|
||||||
stdout: StdioCollector {
|
stdout: StdioCollector {
|
||||||
onStreamFinished: root.parseOutput(text.trim())
|
onStreamFinished: root.parseNetworkDetails(text.trim())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function parseNetworkDetails(output) {
|
||||||
|
var sections = output.split("===TAILSCALE_IP===")
|
||||||
|
var tailscaleIpSection = sections.length > 1 ? sections[1].trim() : ""
|
||||||
|
|
||||||
|
var beforeTsIp = sections[0]
|
||||||
|
sections = beforeTsIp.split("===TAILSCALE_EXISTS===")
|
||||||
|
var tailscaleExistsSection = sections.length > 1 ? sections[1].trim() : ""
|
||||||
|
|
||||||
|
var beforeTsExist = sections[0]
|
||||||
|
sections = beforeTsExist.split("===VPN_ACTIVE===")
|
||||||
|
var vpnActiveSection = sections.length > 1 ? sections[1].trim() : ""
|
||||||
|
|
||||||
|
var beforeVpnActive = sections[0]
|
||||||
|
sections = beforeVpnActive.split("===VPN_ALL===")
|
||||||
|
var vpnAllSection = sections.length > 1 ? sections[1].trim() : ""
|
||||||
|
|
||||||
|
root.vpnExists = vpnAllSection.length > 0
|
||||||
|
if (vpnActiveSection) {
|
||||||
|
root.vpnConnected = true
|
||||||
|
var vpnLines = vpnActiveSection.split("\n")
|
||||||
|
if (vpnLines.length > 0) {
|
||||||
|
root.vpnName = vpnLines[0].split(":")[0] || ""
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
root.vpnConnected = false
|
||||||
|
root.vpnName = ""
|
||||||
|
root.vpnIp = ""
|
||||||
|
}
|
||||||
|
|
||||||
|
root.tailscaleExists = tailscaleExistsSection.indexOf("EXISTS") >= 0
|
||||||
|
if (tailscaleIpSection) {
|
||||||
|
root.tailscaleConnected = true
|
||||||
|
root.tailscaleIp = tailscaleIpSection
|
||||||
|
} else {
|
||||||
|
root.tailscaleConnected = false
|
||||||
|
root.tailscaleIp = ""
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
property Process statsScript: Process {
|
||||||
|
command: ["sh", "-c",
|
||||||
|
"echo '===TRAFFIC==='; " +
|
||||||
|
"DEV=\"" + root._trafficActiveDevice + "\"; " +
|
||||||
|
"if [ -n \"$DEV\" ]; then " +
|
||||||
|
" echo \"RX_BYTES=$(cat /sys/class/net/$DEV/statistics/rx_bytes 2>/dev/null || echo 0)\"; " +
|
||||||
|
" echo \"TX_BYTES=$(cat /sys/class/net/$DEV/statistics/tx_bytes 2>/dev/null || echo 0)\"; " +
|
||||||
|
"fi; " +
|
||||||
|
"echo '===PING==='; " +
|
||||||
|
"ping -c 1 -W 2 1.1.1.1 2>/dev/null | awk -F/ '/rtt/{print $5}' || echo ''"
|
||||||
|
]
|
||||||
|
|
||||||
|
stdout: StdioCollector {
|
||||||
|
onStreamFinished: root.parseStats(text.trim())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function parseStats(output) {
|
||||||
|
var sections = output.split("===PING===")
|
||||||
|
root.ping = sections.length > 1 ? sections[1].trim() : ""
|
||||||
|
|
||||||
|
var pingVal = parseFloat(root.ping)
|
||||||
|
if (!isNaN(pingVal) && pingVal > 0) {
|
||||||
|
var pingHist = root.pingHistory.slice()
|
||||||
|
pingHist.push({time: Date.now(), value: pingVal})
|
||||||
|
if (pingHist.length > root._maxHistory) pingHist.shift()
|
||||||
|
root.pingHistory = pingHist
|
||||||
|
}
|
||||||
|
|
||||||
|
var trafficSection = sections[0].replace("===TRAFFIC===", "").trim()
|
||||||
|
|
||||||
|
if (trafficSection) {
|
||||||
|
var trafficLines = trafficSection.split("\n")
|
||||||
|
var rxBytes = 0
|
||||||
|
var txBytes = 0
|
||||||
|
for (var t = 0; t < trafficLines.length; t++) {
|
||||||
|
var tline = trafficLines[t].trim()
|
||||||
|
if (tline.indexOf("RX_BYTES=") === 0) rxBytes = parseFloat(tline.substring("RX_BYTES=".length))
|
||||||
|
else if (tline.indexOf("TX_BYTES=") === 0) txBytes = parseFloat(tline.substring("TX_BYTES=".length))
|
||||||
|
}
|
||||||
|
|
||||||
|
if (root._prevRxBytes > 0 && root._prevUpdateTime > 0) {
|
||||||
|
var now = Date.now()
|
||||||
|
var interval = (now - root._prevUpdateTime) / 1000
|
||||||
|
if (interval > 0) {
|
||||||
|
var dlBytes = (rxBytes - root._prevRxBytes) / interval
|
||||||
|
var ulBytes = (txBytes - root._prevTxBytes) / interval
|
||||||
|
root.downloadSpeed = root.formatSpeed(dlBytes)
|
||||||
|
root.uploadSpeed = root.formatSpeed(ulBytes)
|
||||||
|
|
||||||
|
var dlHist = root.downloadHistory.slice()
|
||||||
|
dlHist.push({time: now, value: dlBytes})
|
||||||
|
if (dlHist.length > root._maxHistory) dlHist.shift()
|
||||||
|
root.downloadHistory = dlHist
|
||||||
|
|
||||||
|
var ulHist = root.uploadHistory.slice()
|
||||||
|
ulHist.push({time: now, value: ulBytes})
|
||||||
|
if (ulHist.length > root._maxHistory) ulHist.shift()
|
||||||
|
root.uploadHistory = ulHist
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
root.downloadSpeed = ""
|
||||||
|
root.uploadSpeed = ""
|
||||||
|
}
|
||||||
|
|
||||||
|
root._prevRxBytes = rxBytes
|
||||||
|
root._prevTxBytes = txBytes
|
||||||
|
root._prevUpdateTime = Date.now()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -214,100 +351,6 @@ QtObject {
|
|||||||
return bytesPerSec.toFixed(0) + " B/s"
|
return bytesPerSec.toFixed(0) + " B/s"
|
||||||
}
|
}
|
||||||
|
|
||||||
function parseOutput(output) {
|
|
||||||
var sections = output.split("===TAILSCALE_IP===")
|
|
||||||
var tailscaleIpSection = sections.length > 1 ? sections[1].trim() : ""
|
|
||||||
|
|
||||||
var beforeTsIp = sections[0]
|
|
||||||
sections = beforeTsIp.split("===TAILSCALE_EXISTS===")
|
|
||||||
var tailscaleExistsSection = sections.length > 1 ? sections[1].trim() : ""
|
|
||||||
|
|
||||||
var beforeTsExist = sections[0]
|
|
||||||
sections = beforeTsExist.split("===VPN_ACTIVE===")
|
|
||||||
var vpnActiveSection = sections.length > 1 ? sections[1].trim() : ""
|
|
||||||
|
|
||||||
var beforeVpnActive = sections[0]
|
|
||||||
sections = beforeVpnActive.split("===VPN_ALL===")
|
|
||||||
var vpnAllSection = sections.length > 1 ? sections[1].trim() : ""
|
|
||||||
|
|
||||||
var beforeVpnAll = sections[0]
|
|
||||||
sections = beforeVpnAll.split("===PING===")
|
|
||||||
root.ping = sections.length > 1 ? sections[1].trim() : ""
|
|
||||||
|
|
||||||
var pingVal = parseFloat(root.ping)
|
|
||||||
if (!isNaN(pingVal) && pingVal > 0) {
|
|
||||||
var pingHist = root.pingHistory.slice()
|
|
||||||
pingHist.push(pingVal)
|
|
||||||
if (pingHist.length > root._maxHistory) pingHist.shift()
|
|
||||||
root.pingHistory = pingHist
|
|
||||||
}
|
|
||||||
|
|
||||||
var beforePing = sections[0]
|
|
||||||
sections = beforePing.split("===TRAFFIC===")
|
|
||||||
var trafficSection = sections.length > 1 ? sections[1].trim() : ""
|
|
||||||
|
|
||||||
if (trafficSection) {
|
|
||||||
var trafficLines = trafficSection.split("\n")
|
|
||||||
var rxBytes = 0
|
|
||||||
var txBytes = 0
|
|
||||||
for (var t = 0; t < trafficLines.length; t++) {
|
|
||||||
var tline = trafficLines[t].trim()
|
|
||||||
if (tline.indexOf("RX_BYTES=") === 0) rxBytes = parseFloat(tline.substring("RX_BYTES=".length))
|
|
||||||
else if (tline.indexOf("TX_BYTES=") === 0) txBytes = parseFloat(tline.substring("TX_BYTES=".length))
|
|
||||||
}
|
|
||||||
|
|
||||||
if (root._prevRxBytes > 0 && root._prevUpdateTime > 0) {
|
|
||||||
var now = Date.now()
|
|
||||||
var interval = (now - root._prevUpdateTime) / 1000
|
|
||||||
if (interval > 0) {
|
|
||||||
var dlBytes = (rxBytes - root._prevRxBytes) / interval
|
|
||||||
var ulBytes = (txBytes - root._prevTxBytes) / interval
|
|
||||||
root.downloadSpeed = root.formatSpeed(dlBytes)
|
|
||||||
root.uploadSpeed = root.formatSpeed(ulBytes)
|
|
||||||
|
|
||||||
var dlHist = root.downloadHistory.slice()
|
|
||||||
dlHist.push(dlBytes)
|
|
||||||
if (dlHist.length > root._maxHistory) dlHist.shift()
|
|
||||||
root.downloadHistory = dlHist
|
|
||||||
|
|
||||||
var ulHist = root.uploadHistory.slice()
|
|
||||||
ulHist.push(ulBytes)
|
|
||||||
if (ulHist.length > root._maxHistory) ulHist.shift()
|
|
||||||
root.uploadHistory = ulHist
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
root.downloadSpeed = ""
|
|
||||||
root.uploadSpeed = ""
|
|
||||||
}
|
|
||||||
|
|
||||||
root._prevRxBytes = rxBytes
|
|
||||||
root._prevTxBytes = txBytes
|
|
||||||
root._prevUpdateTime = Date.now()
|
|
||||||
}
|
|
||||||
|
|
||||||
root.vpnExists = vpnAllSection.length > 0
|
|
||||||
if (vpnActiveSection) {
|
|
||||||
root.vpnConnected = true
|
|
||||||
var vpnLines = vpnActiveSection.split("\n")
|
|
||||||
if (vpnLines.length > 0) {
|
|
||||||
root.vpnName = vpnLines[0].split(":")[0] || ""
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
root.vpnConnected = false
|
|
||||||
root.vpnName = ""
|
|
||||||
root.vpnIp = ""
|
|
||||||
}
|
|
||||||
|
|
||||||
root.tailscaleExists = tailscaleExistsSection.indexOf("EXISTS") >= 0
|
|
||||||
if (tailscaleIpSection) {
|
|
||||||
root.tailscaleConnected = true
|
|
||||||
root.tailscaleIp = tailscaleIpSection
|
|
||||||
} else {
|
|
||||||
root.tailscaleConnected = false
|
|
||||||
root.tailscaleIp = ""
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
Component.onCompleted: {
|
Component.onCompleted: {
|
||||||
root.updateAll()
|
root.updateAll()
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -7,7 +7,7 @@ Item {
|
|||||||
property var values: []
|
property var values: []
|
||||||
property color lineColor: Colors.primary
|
property color lineColor: Colors.primary
|
||||||
property color fillColor: Qt.alpha(Colors.primary, 0.15)
|
property color fillColor: Qt.alpha(Colors.primary, 0.15)
|
||||||
property int pointCount: 60
|
property real windowMs: 60000
|
||||||
property real minWidth: 120
|
property real minWidth: 120
|
||||||
property real graphHeight: 30
|
property real graphHeight: 30
|
||||||
|
|
||||||
@@ -25,26 +25,34 @@ Item {
|
|||||||
var data = root.values
|
var data = root.values
|
||||||
if (!data || data.length < 2) return
|
if (!data || data.length < 2) return
|
||||||
|
|
||||||
var displayCount = Math.min(data.length, root.pointCount)
|
var now = Date.now()
|
||||||
var startIdx = data.length - displayCount
|
var windowStart = now - root.windowMs
|
||||||
var slice = data.slice(startIdx)
|
|
||||||
|
|
||||||
var maxVal = 0
|
|
||||||
for (var i = 0; i < slice.length; i++) {
|
|
||||||
if (slice[i] > maxVal) maxVal = slice[i]
|
|
||||||
}
|
|
||||||
if (maxVal === 0) maxVal = 1
|
|
||||||
|
|
||||||
var stepX = width / (slice.length - 1)
|
|
||||||
var padY = 2
|
var padY = 2
|
||||||
var drawH = height - padY * 2
|
var drawH = height - padY * 2
|
||||||
|
|
||||||
|
var maxVal = 0
|
||||||
|
for (var i = 0; i < data.length; i++) {
|
||||||
|
if (data[i].time >= windowStart && data[i].value > maxVal)
|
||||||
|
maxVal = data[i].value
|
||||||
|
}
|
||||||
|
if (maxVal === 0) maxVal = 1
|
||||||
|
|
||||||
ctx.beginPath()
|
ctx.beginPath()
|
||||||
ctx.moveTo(0, height - padY - (slice[0] / maxVal) * drawH)
|
var started = false
|
||||||
for (var j = 1; j < slice.length; j++) {
|
for (var j = 0; j < data.length; j++) {
|
||||||
var x = j * stepX
|
var pt = data[j]
|
||||||
var y = height - padY - (slice[j] / maxVal) * drawH
|
if (pt.time < windowStart) continue
|
||||||
ctx.lineTo(x, y)
|
|
||||||
|
var x = ((pt.time - windowStart) / root.windowMs) * width
|
||||||
|
var y = height - padY - (pt.value / maxVal) * drawH
|
||||||
|
|
||||||
|
if (!started) {
|
||||||
|
ctx.moveTo(x, y)
|
||||||
|
started = true
|
||||||
|
} else {
|
||||||
|
ctx.lineTo(x, y)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
ctx.strokeStyle = root.lineColor
|
ctx.strokeStyle = root.lineColor
|
||||||
ctx.lineWidth = 1.5
|
ctx.lineWidth = 1.5
|
||||||
|
|||||||
Reference in New Issue
Block a user