Skip to content
View in the app

A better way to browse. Learn more.

Unraid

A full-screen app on your home screen with push notifications, badges and more.

To install this app on iOS and iPadOS
  1. Tap the Share icon in Safari
  2. Scroll the menu and tap Add to Home Screen.
  3. Tap Add in the top-right corner.
To install this app on Android
  1. Tap the 3-dot menu (⋮) in the top-right corner of the browser.
  2. Tap Add to Home screen or Install app.
  3. Confirm by tapping Install.

MQTT usb attach/detach script to make some sort of build-in kvm

Featured Replies

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.

  • 1 month later...
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.

Guest
Reply to this topic...

Account

Navigation

Search

Search

Configure browser push notifications

Chrome (Android)
  1. Tap the lock icon next to the address bar.
  2. Tap Permissions → Notifications.
  3. Adjust your preference.
Chrome (Desktop)
  1. Click the padlock icon in the address bar.
  2. Select Site settings.
  3. Find Notifications and adjust your preference.