Ok, so maybe it's just me but i didn't found anything to do that so i try to build it myself.
Basically, it will make the unraid server to suscribe to /unraid/attach-device-usb and you can send message like :
{"domain": "Work", "vendorid": "320f", "pid": "5041", "dismount": true}
Domain is the vm, vendorId/productId, the vid/pid of your usb device (you can use lsusb to list), dismount:true means it will detach to existing vm before try to attach it.
To make it work, you need this plugin :
and a mqtt server.
Here my script :
#!/bin/bash
# Load MQTT credentials from the configuration file
source /boot/config/mqttcredentials
# Check that all necessary variables are defined
if [[ -z "$MQTT_HOST" || -z "$MQTT_PORT" || -z "$MQTT_USER" || -z "$MQTT_PASSWORD" || -z "$MQTT_CLIENT_ID" ]]; then
echo "Error: MQTT credentials are incomplete."
exit 1
fi
# Function to handle messages from the 'unraid/switchkeyboard' topic
handle_switchkeyboard() {
# Subscribe to the 'unraid/attach-device-usb' topic and print received messages with 'echo'
mosquitto_sub -h $MQTT_HOST -p $MQTT_PORT -u $MQTT_USER -P $MQTT_PASSWORD -t "unraid/attach-device-usb" | while read -r message; do
echo "Message received on 'unraid/attach-device-usb' : $message"
# Parse the received JSON message
domain=$(echo "$message" | jq -r '.domain')
vendorid=$(echo "$message" | jq -r '.vendorid')
pid=$(echo "$message" | jq -r '.pid')
dismount=$(echo "$message" | jq -r '.dismount')
# Check if the necessary information is present
if [[ -z "$domain" || -z "$vendorid" || -z "$pid" ]]; then
echo "Error: Missing information in MQTT message."
continue
fi
# Create a temporary XML file for the device
temp_file=$(mktemp /tmp/device_XXXX.xml)
cat <<EOF > "$temp_file"
<hostdev mode='subsystem' type='usb'>
<source>
<vendor id='0x$vendorid'/>
<product id='0x$pid'/>
</source>
</hostdev>
EOF
echo "Temporary file created: $temp_file"
# Check if dismount is set to true and process accordingly
if [[ "$dismount" == "true" ]]; then
# Regex to check if a device with the given vendorid and pid is already attached to a domain
device_attached_to_domain=""
for domain_name in $(virsh list --all --name); do
if [[ -n "$domain_name" ]]; then
# Use a regex to search for vendorid and pid in the XML configuration of the domain
device_attached=$(virsh dumpxml "$domain_name" | tr -d '\0' | \
grep -Pzo "(?s)<hostdev.*<vendor id='0x$vendorid'.*<product id='0x$pid'")
# If the device is found in the XML of this domain, mark it
if [[ -n "$device_attached" ]]; then
device_attached_to_domain="$domain_name"
break
fi
fi
done
# If the device is attached to a different domain, detach it
if [[ -n "$device_attached_to_domain" && "$device_attached_to_domain" != "$domain" ]]; then
echo "Device is already attached to $device_attached_to_domain. Detaching it from there."
virsh detach-device "$device_attached_to_domain" "$temp_file"
# Wait for 2 seconds before attaching the device to the target domain
sleep 2
fi
else
echo "Dismount is set to false or not provided. Skipping dismount operation."
fi
# Check if the device is currently attached to the target domain
device_in_use=$(virsh dumpxml "$domain" | tr -d '\0' | \
grep -Pzo "(?s)<hostdev.*<vendor id='0x$vendorid'.*<product id='0x$pid'")
if [[ -n "$device_in_use" ]]; then
echo "Error: Device is already in use by domain '$domain'."
else
# Attach the device to the destination domain
echo "Attaching device to virtual machine: $domain"
virsh attach-device "$domain" "$temp_file"
fi
# Remove the temporary file
rm -f "$temp_file"
done
}
# Launch the function to handle the switchkeyboard topic
handle_switchkeyboard
As a side note my windows vm i use a autohotscript like that, so i have key binding on my keyboard to switch beetween computers.
I use dismount:false on the initial code, so it will only attach if it's not currently attached on an running vm. (and i put it into startup with autologin activated)
commande := """C:\Program Files\mosquitto\mosquitto_pub.exe"" -h 192.168.1.25 -p 1883 -u xxxx -P xxxx -t ""unraid/attach-device-usb"" -m ""{\""domain\"": \""Work\"", \""vendorid\"": \""320f\"", \""pid\"": \""5041\"", \""dismount\"": false}"""
Run, % commande, , Hide
commande := """C:\Program Files\mosquitto\mosquitto_pub.exe"" -h 192.168.1.25 -p 1883 -u xxxx -P xxxx -t ""unraid/attach-device-usb"" -m ""{\""domain\"": \""Work\"", \""vendorid\"": \""1532\"", \""pid\"": \""0067\"", \""dismount\"": false}"""
Run, % commande, , Hide
F21::
Run, "C:\Program Files\mosquitto\mosquitto_pub.exe" -h 192.168.1.25 -p 1883 -u xxxx -P xxxx -t "dpswitch/bouton" -m "ON", , Hide
Return
F13::
commande := """C:\Program Files\mosquitto\mosquitto_pub.exe"" -h 192.168.1.25 -p 1883 -u xxxx -P xxxx -t ""unraid/attach-device-usb"" -m ""{\""domain\"": \""Work\"", \""vendorid\"": \""320f\"", \""pid\"": \""5041\"", \""dismount\"": true}"""
Run, % commande, , Hide
commande := """C:\Program Files\mosquitto\mosquitto_pub.exe"" -h 192.168.1.25 -p 1883 -u xxxx -P xxxx -t ""unraid/attach-device-usb"" -m ""{\""domain\"": \""Work\"", \""vendorid\"": \""1532\"", \""pid\"": \""0067\"", \""dismount\"": true}"""
Run, % commande, , Hide
Return
F14::
commande := """C:\Program Files\mosquitto\mosquitto_pub.exe"" -h 192.168.1.25 -p 1883 -u xxxx -P xxxx -t ""unraid/attach-device-usb"" -m ""{\""domain\"": \""Game\"", \""vendorid\"": \""320f\"", \""pid\"": \""5041\"", \""dismount\"": true}"""
Run, % commande, , Hide
commande := """C:\Program Files\mosquitto\mosquitto_pub.exe"" -h 192.168.1.25 -p 1883 -u xxxx -P xxxx -t ""unraid/attach-device-usb"" -m ""{\""domain\"": \""Game\"", \""vendorid\"": \""1532\"", \""pid\"": \""0067\"", \""dismount\"": true}"""
Run, % commande, , Hide
Return
If someone have something simmilar i could use on linux vm, i would be interested.