Fix: Icons now map correctly to device

This commit is contained in:
2026-08-27 11:45:14 +01:00
parent 622afb2100
commit 3f4e3514a2

View File

@@ -73,27 +73,52 @@ Item {
p["device.icon-name"] || "", p["device.product.name"] || ""].join(" ").toLowerCase() p["device.icon-name"] || "", p["device.product.name"] || ""].join(" ").toLowerCase()
} }
// Quickshell's Pipewire API only exposes a node's own properties, not
// its parent Device — so device.api (unlike device.form-factor or the
// richer device.icon-name, which only live on the Device object) is the
// one reliable Bluetooth signal actually present here: it's set on
// every node PipeWire hands off to a bluez5-backed sink/source. Checking
// it directly beats sniffing the word "bluetooth" out of node names,
// which usually just say "bluez_output..." and never match.
function isBluetoothNode(node) {
var p = (node && node.properties) || {}
return p["device.api"] === "bluez5"
}
// PipeWire's own device.form-factor (which would call ACCENTUM a
// "headset" outright) isn't reachable from here for the same reason —
// it lives on the Device object, not the node. And some devices (a
// generic USB DAC like an EKSA headset) have no form-factor anywhere in
// PipeWire at all, headset or not, so no property-based check can catch
// them. This override list is the deliberate manual escape hatch for
// exactly that case — add a distinctive substring from the product name
// (device.product.name/description, matched case-insensitively via
// deviceBlob) whenever a new no-metadata device shows up misclassified.
readonly property var headphoneNameOverrides: ["e900plus"]
function isHeadphoneNode(node) { function isHeadphoneNode(node) {
var blob = deviceBlob(node) var blob = deviceBlob(node)
return blob.indexOf("headphone") !== -1 || blob.indexOf("headset") !== -1 if (blob.indexOf("headphone") !== -1 || blob.indexOf("headset") !== -1
|| blob.indexOf("earbud") !== -1 || blob.indexOf("earphone") !== -1 || blob.indexOf("earbud") !== -1 || blob.indexOf("earphone") !== -1
|| blob.indexOf("airpod") !== -1 || blob.indexOf("airpod") !== -1)
return true
return root.headphoneNameOverrides.some(function(needle) { return blob.indexOf(needle) !== -1 })
} }
function sinkIcon(node) { function sinkIcon(node) {
if (!node) return "speaker" if (!node) return "speaker"
if (isHeadphoneNode(node)) return "headphones" if (isHeadphoneNode(node)) return "headphones"
if (isBluetoothNode(node)) return "bluetooth_audio"
var blob = deviceBlob(node) var blob = deviceBlob(node)
if (blob.indexOf("bluetooth") !== -1) return "bluetooth_audio"
if (blob.indexOf("hdmi") !== -1 || blob.indexOf("display") !== -1) return "tv" if (blob.indexOf("hdmi") !== -1 || blob.indexOf("display") !== -1) return "tv"
return "speaker" return "speaker"
} }
function sourceIcon(node) { function sourceIcon(node) {
if (!node) return "mic" if (!node) return "mic"
if (isHeadphoneNode(node)) return "headphones"
if (isBluetoothNode(node)) return "bluetooth_audio"
var blob = deviceBlob(node) var blob = deviceBlob(node)
if (blob.indexOf("headset") !== -1) return "headphones"
if (blob.indexOf("bluetooth") !== -1) return "bluetooth_audio"
if (blob.indexOf("webcam") !== -1 || blob.indexOf("camera") !== -1) return "videocam" if (blob.indexOf("webcam") !== -1 || blob.indexOf("camera") !== -1) return "videocam"
return "mic" return "mic"
} }