Files
noctalia-shell/Helpers/BluetoothUtils.js

128 lines
4.2 KiB
JavaScript

.pragma library
// Address helpers
var macFromDevice = (dev) => {
if (!dev) return "";
if (dev.address && dev.address.length > 0) return dev.address;
if (dev.nativePath && dev.nativePath.indexOf("/dev_") !== -1)
return dev.nativePath.split("dev_")[1].split("_").join(":");
return "";
};
var deviceKey = (dev) => {
if (!dev) return "";
if (dev.address && dev.address.length > 0) return dev.address.toUpperCase();
if (dev.nativePath && dev.nativePath.length > 0) return dev.nativePath;
if (dev.devicePath && dev.devicePath.length > 0) return dev.devicePath;
return (dev.name || dev.deviceName || "") + "|" + (dev.icon || "");
};
var dedupeDevices = (list) => {
if (!list || list.length === 0) return [];
var seen = ({});
var out = [];
for (var i = 0; i < list.length; ++i) {
var d = list[i];
if (!d) continue;
var k = deviceKey(d);
if (k && !seen[k]) { seen[k] = true; out.push(d); }
}
return out;
};
// RSSI parsing
var parseRssiOutput = (text) => {
try {
text = text || "";
var mParen = text.match(/\(\s*(-?\d+)\s*(?:d?b?m?)?\s*\)/i);
if (mParen && mParen.length > 1) return Number(mParen[1]);
var mDec = text.match(/RSSI:\s*(-?\d+)/i);
if (mDec && mDec.length > 1) return Number(mDec[1]);
var mHex = text.match(/RSSI:\s*0x([0-9a-fA-F]+)/i);
if (mHex && mHex.length > 1) {
var v = parseInt(mHex[1], 16);
if (v >= 0x80000000) v = v - 0x100000000; // 32-bit two's complement
else if (v >= 0x8000) v = v - 0x10000; // 16-bit
else if (v >= 0x80) v = v - 0x100; // 8-bit
return v;
}
} catch (e) {}
return null;
};
var dbmToPercent = (dbm) => {
if (dbm === null || dbm === undefined || isNaN(dbm)) return null;
// Clamp simple linear map roughly from -100..0 dBm to 0..100%
var pct = Math.round((Number(dbm) + 100) * 2);
if (isNaN(pct)) return null;
return Math.max(0, Math.min(100, pct));
};
// Signal helpers
var signalPercent = (device, cache, _version) => {
if (!device) return null;
try {
var addr = macFromDevice(device);
if (addr && cache && cache[addr] !== undefined) {
var cached = Number(cache[addr]) | 0;
return Math.max(0, Math.min(100, cached));
}
} catch (e) {}
var s = device && device.signalStrength;
if (s === undefined || s <= 0) return null;
var p = Number(s) | 0;
return Math.max(0, Math.min(100, p));
};
var signalIcon = (p) => {
if (p === null) return "antenna-bars-off";
if (p >= 80) return "antenna-bars-5";
if (p >= 60) return "antenna-bars-4";
if (p >= 40) return "antenna-bars-3";
if (p >= 20) return "antenna-bars-2";
return "antenna-bars-1";
};
// Icon mapping
var deviceIcon = (name, icon) => {
var s1 = String(name || "").toLowerCase();
var s2 = String(icon || "").toLowerCase();
// Prefer icon-based hints for display devices first to avoid "audio" catching TVs
var displayHints = ["display", "tv", "monitor", "projector", "screen", "chromecast", "cast"];
for (var dh = 0; dh < displayHints.length; dh++) {
if (s2.indexOf(displayHints[dh]) !== -1) return "bt-device-tv";
}
var tests = [
[["controller", "gamepad"], "bt-device-gamepad"],
[["microphone"], "bt-device-microphone"],
[["pod", "bud", "minor"], "bt-device-earbuds"],
[["headset", "arctis", "major"], "bt-device-headset"],
[["headphone"], "bt-device-headphones"],
[["mouse"], "bt-device-mouse"],
[["keyboard"], "bt-device-keyboard"],
[["watch"], "bt-device-watch"],
[["display", "tv", "monitor", "projector", "screen", "chromecast", "cast"], "bt-device-tv"],
[["speaker", "audio", "sound"], "bt-device-speaker"],
[["phone", "iphone", "android", "samsung"], "bt-device-phone"]
];
for (var i = 0; i < tests.length; i++) {
var keys = tests[i][0];
var out = tests[i][1];
for (var j = 0; j < keys.length; j++) {
var k = keys[j];
if (s1.indexOf(k) !== -1 || s2.indexOf(k) !== -1) return out;
}
}
return "bt-device-generic";
};
// Battery percent helper
var batteryPercent = (device) => {
if (!device || !device.batteryAvailable || device.battery === undefined) return null;
var val = Math.round(Number(device.battery) * 100);
if (isNaN(val)) return null;
return Math.max(0, Math.min(100, val));
};