dashboard/app/collect/net.py

56 lines
1.6 KiB
Python

import glob
import re
import shutil
import socket
import subprocess
import time
from typing import Any
import psutil
_wifi_cache: dict[str, tuple[float, str | None]] = {}
_WIFI_TTL = 15.0
_SSID_RE = re.compile(r"SSID:\s+(\S.*)")
def _wifi_ifaces() -> set[str]:
return {p.split("/")[-2] for p in glob.glob("/sys/class/net/*/wireless")}
def _ssid(iface: str) -> str | None:
hit = _wifi_cache.get(iface)
now = time.monotonic()
if hit is not None and now - hit[0] < _WIFI_TTL:
return hit[1]
ssid: str | None = None
if shutil.which("iw"):
try:
out = subprocess.run(
["iw", "dev", iface, "link"], capture_output=True, text=True, timeout=3, check=False
).stdout
m = _SSID_RE.search(out)
if m:
ssid = m.group(1).strip().strip('"') or None
except (OSError, subprocess.SubprocessError):
pass
_wifi_cache[iface] = (now, ssid)
return ssid
def sample() -> dict[str, Any | None]:
addrs = psutil.net_if_addrs()
stats = psutil.net_if_stats()
wifi_set = _wifi_ifaces()
ifaces: list[dict[str, Any]] = []
wifi: dict[str, Any] | None = None
for name in sorted(addrs):
if name == "lo":
continue
st = stats.get(name)
if st is None or not bool(st.isup):
continue
ipv4 = [a.address for a in addrs[name] if a.family == socket.AF_INET]
ifaces.append({"name": name, "ipv4": ipv4})
if name in wifi_set and wifi is None:
wifi = {"iface": name, "ssid": _ssid(name)}
return {"net_ifaces": ifaces, "net_wifi": wifi}