March 25, 20251 yr 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.
April 28, 20251 yr On 3/25/2025 at 10:11 PM, Nixx said: 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. Awesome! That is an interesting use case!
Join the conversation
You can post now and register later. If you have an account, sign in now to post with your account.
Note: Your post will require moderator approval before it will be visible.