repositories / dotfiles
dotfiles
bugabingas dorkfiles
owned by admin
quickshell/nuguland/network.js
Raw// network.js — pure iwd D-Bus (busctl --json) shaping. NO I/O.
// Dual-mode: node `require` (tests) + QML import ("network.js" as Network).
// Same pattern as audio.js / brightness.js / clip.js. No `.pragma library`.
//
// iwd exposes a full D-Bus API (net.connman.iwd). `busctl --json=short` emits
// structured JSON, so there is NO text/ANSI parsing — we JSON.parse two calls:
// ObjectManager.GetManagedObjects -> devices, stations, networks, known nets
// Station.GetOrderedNetworks -> [path, signalStrength] strongest-first
// Network names arrive already decoded; signal is int dBm*100 (e.g. -6800).
// --- busctl JSON helpers ---------------------------------------------------
// busctl --json wraps a reply as { "type": "...", "data": [ <value> ] }.
// Unwrap to the single payload value. Accepts a raw string or a parsed object.
function unwrap(raw) {
if (raw === undefined || raw === null) return null;
var obj = typeof raw === "string" ? JSON.parse(raw) : raw;
if (obj && Object.prototype.hasOwnProperty.call(obj, "data")
&& Array.isArray(obj.data)) {
return obj.data.length > 0 ? obj.data[0] : null;
}
return obj;
}
// A D-Bus property value is { "type": "x", "data": <value> }. Pull data or fb.
function prop(iface, name, fb) {
if (iface && iface[name] && iface[name].data !== undefined) return iface[name].data;
return fb;
}
// --- Signal -----------------------------------------------------------------
// signalBars(centiDbm) — iwd reports signal as dBm*100. Map to 0..4 bars using
// the same thresholds iwctl itself renders. Non-finite => 0.
function signalBars(centiDbm) {
var v = Number(centiDbm);
if (!isFinite(v)) return 0;
if (v >= -6000) return 4;
if (v >= -6700) return 3;
if (v >= -7400) return 2;
return 1;
}
// dbmFromCenti(centiDbm) — iwd's dBm*100 -> rounded dBm. Non-finite => null.
function dbmFromCenti(centiDbm) {
var v = Number(centiDbm);
if (!isFinite(v)) return null;
return Math.round(v / 100);
}
// --- Parsers (JSON -> plain data) ------------------------------------------
// parseManaged(rawGmo) -> {
// devicePath, device:{name,powered,mode}|null,
// station:{state,scanning,connectedNetwork}|null,
// networks: { <path>: {ssid,security,connected,known} },
// knownBySsid: { <ssid>: <knownNetworkPath> } }
// Picks the FIRST station-mode device.
function parseManaged(rawGmo) {
var d = unwrap(rawGmo) || {};
var out = {
devicePath: "",
device: null,
station: null,
networks: {},
knownBySsid: {}
};
for (var path in d) {
if (!Object.prototype.hasOwnProperty.call(d, path)) continue;
var ifaces = d[path];
var dev = ifaces["net.connman.iwd.Device"];
if (dev && prop(dev, "Mode", "") === "station" && out.devicePath === "") {
out.devicePath = path;
out.device = {
name: prop(dev, "Name", ""),
powered: !!prop(dev, "Powered", false),
mode: "station"
};
var st = ifaces["net.connman.iwd.Station"];
if (st) {
out.station = {
state: prop(st, "State", ""),
scanning: !!prop(st, "Scanning", false),
connectedNetwork: prop(st, "ConnectedNetwork", "")
};
}
}
var net = ifaces["net.connman.iwd.Network"];
if (net) {
out.networks[path] = {
ssid: prop(net, "Name", ""),
security: prop(net, "Type", ""),
connected: !!prop(net, "Connected", false),
// iwd exposes a KnownNetwork property ONLY when the net is known.
known: Object.prototype.hasOwnProperty.call(net, "KnownNetwork")
};
}
var kn = ifaces["net.connman.iwd.KnownNetwork"];
if (kn) {
var kssid = prop(kn, "Name", "");
if (kssid !== "") out.knownBySsid[kssid] = path;
}
}
return out;
}
// parseOrdered(rawGon) -> [{ path, signal:centiDbm }] in iwd order (the API
// already lists the connected network first, then by descending strength).
function parseOrdered(rawGon) {
var arr = unwrap(rawGon);
var out = [];
if (!Array.isArray(arr)) return out;
for (var i = 0; i < arr.length; i++) {
var pair = arr[i];
if (!Array.isArray(pair) || pair.length < 2) continue;
out.push({ path: pair[0], signal: pair[1] });
}
return out;
}
// networkRows(managed, ordered) -> view rows, in iwd order, joining the signal
// (from ordered) with name/type/known/connected (from managed). Carries the
// D-Bus object `path` so the service can call Connect on the right object.
// [{ id, ssid, security, signal:0..4, connected, known, needsPassphrase, path }]
function networkRows(managed, ordered) {
var nets = (managed && managed.networks) ? managed.networks : {};
var list = Array.isArray(ordered) ? ordered : [];
var rows = [];
var used = {};
function identity(net) {
return String(net.ssid || "") + "\u0000" + String(net.security || "");
}
function append(path, net, rawSignal, atFront) {
if (!net || used[path]) return;
var row = {
id: networkId(net.ssid),
ssid: net.ssid,
security: net.security,
signal: rawSignal === null ? 0 : signalBars(rawSignal),
signalDbm: rawSignal === null ? null : dbmFromCenti(rawSignal),
connected: net.connected,
known: net.known,
needsPassphrase: needsPassphrase(net.security, net.known),
path: path
};
used[path] = true;
if (atFront) rows.unshift(row); else rows.push(row);
}
for (var i = 0; i < list.length; i++) {
var orderedRow = list[i];
var path = orderedRow.path;
var net = nets[path];
if (!net && (orderedRow.ssid || orderedRow.security)) {
var wanted = identity(orderedRow);
for (var candidate in nets) {
if (!used[candidate] && identity(nets[candidate]) === wanted) {
path = candidate;
net = nets[candidate];
break;
}
}
}
append(path, net, orderedRow.signal, false);
}
// ObjectManager and Station snapshots can race. Retain unmatched managed
// rows rather than flashing an empty model; a missing connected row leads.
for (var managedPath in nets)
if (!used[managedPath] && nets[managedPath].connected)
append(managedPath, nets[managedPath], null, true);
for (var fallbackPath in nets)
if (!used[fallbackPath])
append(fallbackPath, nets[fallbackPath], null, false);
return rows;
}
// --- Shaping / view helpers ------------------------------------------------
// needsPassphrase(security, known) — secured AND iwd has no stored secret.
function needsPassphrase(security, known) {
return security !== "open" && !known;
}
// signalRole(bars) — theme token; weak/medium/strong.
function signalRole(bars) {
if (bars <= 1) return "ui.minor";
if (bars <= 3) return "ui.accent";
return "content.accent";
}
// --- Nerd Font md-wifi_strength glyphs (astral codepoints). Built from
// codepoints to avoid hand-written surrogate pairs. ---
function fromCodePoint(n) {
if (n < 0x10000) return String.fromCharCode(n);
var c = n - 0x10000;
return String.fromCharCode(0xD800 + (c >> 10), 0xDC00 + (c & 0x3FF));
}
var WIFI_GLYPHS = [
fromCodePoint(0xF092F), // 0: wifi-strength-outline
fromCodePoint(0xF091F), // 1
fromCodePoint(0xF0922), // 2
fromCodePoint(0xF0925), // 3
fromCodePoint(0xF0928) // 4
];
// signalGlyph(bars) — nerd-font wifi glyph by strength.
function signalGlyph(bars) {
var b = bars;
if (b < 0) b = 0;
if (b > 4) b = 4;
return WIFI_GLYPHS[b];
}
// securityLabel(sec) — human label for the security token.
function securityLabel(sec) {
if (sec === "open") return "open";
if (sec === "psk") return "wpa";
if (sec === "8021x") return "enterprise";
return sec ? sec : "";
}
// networkId(ssid) — stable id (ssid itself; never empty).
function networkId(ssid) {
var s = (ssid === undefined || ssid === null) ? "" : String(ssid);
return s.length > 0 ? s : "hidden-network";
}
// --- Connection detail (StationDiagnostic.GetDiagnostics) ------------------
// Only the CONNECTED network has these; iwd exposes no per-BSS detail for
// other scanned networks. The expand drawer shows bars/security for any row
// and these extra fields only for the connected one.
// bandLabel(freqMHz) — Wi-Fi band from centre frequency. "" if unknown.
function bandLabel(freqMHz) {
var f = Number(freqMHz);
if (!isFinite(f) || f <= 0) return "";
if (f >= 5925) return "6 GHz";
if (f >= 4900) return "5 GHz";
if (f >= 2400) return "2.4 GHz";
return "";
}
// rateLabel(raw) — iwd Rx/TxBitrate is in units of 100 kbit/s. -> "135 Mbps".
// "" for missing/zero so the view can omit it.
function rateLabel(raw) {
var v = Number(raw);
if (!isFinite(v) || v <= 0) return "";
return Math.round(v / 10) + " Mbps";
}
// connectionDetail(rawDiag) -> { bssid, band, channel, rssi, rxRate, txRate,
// mode } | null. rawDiag is the StationDiagnostic.GetDiagnostics a{sv} reply.
// Returns null when there is no connected BSS (disconnected / no diagnostics).
function connectionDetail(rawDiag) {
var d = unwrap(rawDiag);
if (!d || typeof d !== "object") return null;
var bssid = prop(d, "ConnectedBss", "");
if (!bssid) return null;
return {
bssid: String(bssid),
band: bandLabel(prop(d, "Frequency", 0)),
channel: Number(prop(d, "Channel", 0)) || null,
rssi: Number(prop(d, "RSSI", 0)) || null,
rxRate: rateLabel(prop(d, "RxBitrate", 0)),
txRate: rateLabel(prop(d, "TxBitrate", 0)),
mode: String(prop(d, "RxMode", "") || "")
};
}
if (typeof module !== 'undefined' && module.exports) {
module.exports = {
unwrap: unwrap,
prop: prop,
signalBars: signalBars,
dbmFromCenti: dbmFromCenti,
parseManaged: parseManaged,
parseOrdered: parseOrdered,
networkRows: networkRows,
needsPassphrase: needsPassphrase,
signalRole: signalRole,
signalGlyph: signalGlyph,
securityLabel: securityLabel,
networkId: networkId,
bandLabel: bandLabel,
rateLabel: rateLabel,
connectionDetail: connectionDetail
};
}