Turn Off USB devices along with the monitor
When the lights go out
In this article [1] I cover how to auto-suspend USB devices after a period of inactivity. I thought I also wanted them to turn off automatically when I turn off the monitor they are connected to. So let’s get to it.
USB physical addresses
As we saw in the previous article, you can list devices with lsusb
. This will give you logical bus/port numbers, which change and are assigned by the kernel.
lsusb
[...]
Bus 001 Device 016: ID 0b05:1877 ASUSTek Computer, Inc. ROG Gladius II Origin
Bus 001 Device 034: ID 0b05:18f8 ASUSTek Computer, Inc. ROG STRIX SCOPE
Bus 001 Device 033: ID 043e:9a39 LG Electronics USA, Inc. 27UP850
For specific purposes later, we need to use the actual physical bus/port numbers.
We can get the entire USB topology with :
lsusb -t
/: Bus 001.Port 001: Dev 001, Class=root_hub, Driver=xhci_hcd/16p, 480M
|__ Port 006: Dev 002, If 0, Class=Hub, Driver=hub/4p, 480M
|__ Port 001: Dev 004, If 0, Class=Human Interface Device, Driver=usbhid, 12M
|__ Port 003: Dev 005, If 0, Class=Human Interface Device, Driver=usbhid, 12M
|__ Port 011: Dev 006, If 0, Class=Vendor Specific Class, Driver=[none], 12M
|__ Port 011: Dev 006, If 2, Class=Human Interface Device, Driver=usbhid, 12M
|__ Port 013: Dev 105, If 0, Class=Hub, Driver=hub/3p, 480M
|__ Port 001: Dev 106, If 0, Class=Hub, Driver=hub/5p, 480M
|__ Port 002: Dev 108, If 0, Class=Hub, Driver=hub/2p, 480M
|__ Port 001: Dev 111, If 0, Class=Human Interface Device, Driver=usbhid, 12M
|__ Port 001: Dev 111, If 1, Class=Human Interface Device, Driver=usbhid, 12M
|__ Port 001: Dev 111, If 2, Class=Human Interface Device, Driver=usbhid, 12M
|__ Port 002: Dev 114, If 0, Class=Audio, Driver=snd-usb-audio, 480M
|__ Port 002: Dev 114, If 1, Class=Audio, Driver=snd-usb-audio, 480M
|__ Port 002: Dev 114, If 2, Class=Audio, Driver=snd-usb-audio, 480M
|__ Port 002: Dev 114, If 3, Class=Human Interface Device, Driver=usbhid, 480M
|__ Port 003: Dev 110, If 0, Class=Human Interface Device, Driver=usbhid, 12M
|__ Port 005: Dev 113, If 0, Class=Human Interface Device, Driver=usbhid, 480M
|__ Port 002: Dev 082, If 0, Class=Hub, Driver=hub/5p, 480M
|__ Port 001: Dev 084, If 0, Class=Human Interface Device, Driver=usbhid, 12M
|__ Port 001: Dev 084, If 1, Class=Human Interface Device, Driver=usbhid, 12M
|__ Port 001: Dev 084, If 2, Class=Human Interface Device, Driver=usbhid, 12M
|__ Port 001: Dev 084, If 3, Class=Human Interface Device, Driver=usbhid, 12M
|__ Port 002: Dev 085, If 0, Class=Human Interface Device, Driver=usbhid, 12M
|__ Port 002: Dev 085, If 1, Class=Human Interface Device, Driver=usbhid, 12M
|__ Port 002: Dev 085, If 2, Class=Human Interface Device, Driver=usbhid, 12M
|__ Port 003: Dev 083, If 0, Class=Human Interface Device, Driver=usbhid, 12M
|__ Port 014: Dev 008, If 0, Class=Wireless, Driver=btusb, 12M
|__ Port 014: Dev 008, If 1, Class=Wireless, Driver=btusb, 12M
/: Bus 002.Port 001: Dev 001, Class=root_hub, Driver=xhci_hcd/10p, 10000M
These are the actual fixed physical addresses on your hardware, and they don’t change. I can recognize a few of mine, but just because I’m already familiar with them. Otherwise, it can be somewhat complicated to map logical addresses to physical ones.
An easier way is to retrieve the logical bus/device numbers from the earlier lsusb
output and use them with udevadm info
. For my mouse, remember, this was bus 001
and device 016
:
Bus 001 Device 016: ID 0b05:1877 ASUSTek Computer, Inc. ROG Gladius II Origin
udevadm info --name=/dev/bus/usb/001/016
This will give you a lot of information including the DEVPATH
:
P: /devices/pci0000:00/0000:00:14.0/usb1/1-13/1-13.2/1-13.2.2
M: 1-13.2.2
R: 2
J: c189:15
U: usb
T: usb_device
D: c 189:15
N: bus/usb/001/016
L: 0
V: usb
E: DEVPATH=/devices/pci0000:00/0000:00:14.0/usb1/1-13/1-13.2/1-13.2.2
This is a bit cumbersome, but the full physical path DEVPATH
for the bus/hub/port my mouse is using is (you have to append /sys/ to it):
/sys/devices/pci0000:00/0000:00:14.0/usb1/1-13/1-13.2/1-13.2.2
If you were wondering about what /sys is:
sysfs is a RAM-based filesystem initially based on ramfs. It provides a means to export kernel data structures, their attributes, and the linkages between them to userspace.
/sys/devices This is a directory that contains a filesystem representation of the kernel device tree, which is a hierarchy of device structures within the kernel.
man5/sysfs [3]
Sysfs is not a real file system, but a pseudo-file system. These files are exposed by the kernel; they are not actual files on your disk.
In any case, you can read/write on some of them. So if we go to that path and check its contents, we can see:
1-13.2.2:1.0 bDeviceClass bNumInterfaces dev idVendor product subsystem
1-13.2.2:1.1 bDeviceProtocol bcdDevice devnum ltm_capable quirks tx_lanes
1-13.2.2:1.2 bDeviceSubClass bmAttributes devpath manufacturer removable uevent
authorized bMaxPacketSize0 busnum driver maxchild remove urbnum
avoid_reset_quirk bMaxPower configuration ep_00 port rx_lanes version
bConfigurationValue bNumConfigurations descriptors idProduct power speed
So… if all is good, we should get the earlier values from lsusb 0b05:1877
for the idProduct and idVendor file contents… right?
Bus 001 Device 016: ID 0b05:1877 ASUSTek Computer, Inc. ROG Gladius II Origin
And fair enough:
$ cd /sys/devices/pci0000:00/0000:00:14.0/usb1/1-13/1-13.2/1-13.2.2
$ cat idVendor
0b05
$ cat idProduct
1877
That’s all we need for now! Let’s move on with udev
.
Powering on and off the USB devices
First I need to find the events the monitor triggers when I clicked the power off/power on buttons. We can monitor udev events with:
udevadm monitor
Listens to the kernel uevents and events sent out by a udev rule and prints the devpath of the event to the console.
Coming back to the lsusb
output, my monitor USB hub can be identified by:
Bus 001 Device 033: ID 043e:9a39 LG Electronics USA, Inc. 27UP850
Therefore we should look for 043E:9A39
.
In my case, when I turn my monitor off with the power off button, I can see this remove
event:
UDEV [7881.000289] remove /devices/pci0000:00/0000:00:14.0/usb1/1-13/1-13.2/1-13.2.3/1-13.2.3:1.0/0003:043E:9A39.03AD
And when I turn it back on:
KERNEL[12827.720214] add /devices/pci0000:00/0000:00:14.0/usb1/1-13/1-13.2/1-13.2.3/1-13.2.3:1.0/0003:043E:9A39.03EB (hid)
To turn USB devices or hubs on and off, we have a few choices.
First thing I tried was to unbind/bind the mouse/keyboard, but it didn’t turn off the RGB lights when I did, it just unbinds the driver. The keyboard/mouse won’t work, but the power is on.
You can do that by typing the bus/port numbers into this file:
echo '1-13.2.2' > /sys/bus/usb/drivers/usb/unbind
Then I tried to use the “remove” file with value 1:
echo '1' > /sys/devices/pci0000:00/0000:00:14.0/usb1/1-13/1-13.2/1-13.2.2/remove
It effectively kills the device, including the lights. The directory in the subsystem disappears after I remove it. Not surprisingly, I just don’t know how to bring it back to life once it’s gone.
If your hub supports per-port power (i.e, turning off individual ports), you can use uhubctl. mvp/uhubctl [4]
$ paru -Ss uhubctl
aur/uhubctl 2.6.0-1 [+11 ~0.30] [Installed]
control USB per-port power switching on PPPS smart USB hubs
List the hubs available in your system:
uhubctl
Current status for hub 1-6 [05e3:0610 USB2.0 Hub, USB 2.00, 4 ports, ppps]
Port 1: 0103 power enable connect [1b1c:0c0b Corsair Lighting Node PRO 1AE00005C8207AAFA3C2465F021C00F5]
Port 2: 0100 power
Port 3: 0103 power enable connect [1b1c:0c22 Corsair Inc. H150iRGBPROXT]
Port 4: 0100 power
Current status for hub 1-13.2 [0bda:5411 Generic USB2.1 Hub, USB 2.10, 5 ports, ppps]
Port 1: 0103 power enable connect [0b05:18f8 ASUSTeK ROG STRIX SCOPE]
Port 2: 0103 power enable connect [0b05:1877 ASUS ROG Gladius II Origin]
Port 3: 0103 power enable connect [043e:9a39 LG Electronics Inc. LG Monitor Controls 405NTNHJE279]
Port 4: 0100 power
Port 5: 0100 power
Current status for hub 1-13.1.2 [05e3:0608 USB2.0 Hub, USB 2.00, 2 ports, ppps]
Port 1: 0103 power enable connect [03f0:02b5 HP, Inc HyperX QuadCast 2 S Controller C1V441039W]
Port 2: 0503 power highspeed enable connect [03f0:0d84 HP, Inc HyperX QuadCast 2 S C1V441039W]
Current status for hub 1-13.1 [0bda:5411 Generic USB2.1 Hub, USB 2.10, 5 ports, ppps]
Port 1: 0100 power
Port 2: 0503 power highspeed enable connect [05e3:0608 USB2.0 Hub, USB 2.00, 2 ports, ppps]
Port 3: 0103 power enable connect [0b05:186e]
Port 4: 0100 power
Port 5: 0503 power highspeed enable connect [0bda:1100 Realtek USB2.0 HID]
Current status for hub 1-13 [05e3:0610 USB2.0 Hub, USB 2.00, 3 ports, ppps]
Port 1: 0503 power highspeed enable connect [0bda:5411 Generic USB2.1 Hub, USB 2.10, 5 ports, ppps]
Port 2: 0503 power highspeed enable connect [0bda:5411 Generic USB2.1 Hub, USB 2.10, 5 ports, ppps]
Port 3: 0100 power
As you can see, the hub my keyboard/mouse is using is hub 1-13.2
Unfortunately, as I mentioned earlier, my hub does not support turning off individual ports. When I turn off any port, it turns off all of them, so I can’t use it.
If your USB hub supports it, you can turn it on and off with:
uhubctl -a on -l 1-13.2 -p 1
uhubctl -a off -l 1-13.2 -p 1
Where 1-13.2
is the USB hub we got above from uhubctl
output, and 1
is just port 1 of the hub.
Again, be careful with this; you may turn off your USB devices, including your keyboard, so have another one around or use -a cycle
instead.
So… what do we do then?
We can instead instruct the kernel to turn off the USB port where the HUB is connected (where the cable is plugged into the motherboard):
Description: This file controls the state of a USB port, including Vbus power output (but only on hubs that support power switching – most hubs don’t support it). If a port is disabled, the port is unusable: Devices attached to the port will not be detected, initialized, or enumerated.
All we have to do is echo the value 1 to the port file disable
:
/sys/bus/usb/devices/.../<hub_interface>/port<X>/disable
This file controls the state of a USB port, including
Vbus power output (but only on hubs that support
power switching -- most hubs don't support it). If
a port is disabled, the port is unusable: Devices
attached to the port will not be detected, initialized,
or enumerated.
echo 1 > /sys/devices/pci0000:00/0000:00:14.0/usb1/1-13/1-13.2/1-13.2:1.0/1-13.2-port1/disable
And then to bring it back we echo 0:
echo 0 > /sys/devices/pci0000:00/0000:00:14.0/usb1/1-13/1-13.2/1-13.2:1.0/1-13.2-port1/disable
This turns the keyboard/mouse on and off, including RGB lights!
Now, for the udev rules, we can write a small bash program that takes care of doing the echos:
#!/bin/bash
USB_PORT="/sys/devices/pci0000:00/0000:00:14.0/usb1/1-13/1-13.2/1-13.2:1.0/1-13.2-port1/disable"
case "$1" in
off) echo 1 | tee "$USB_PORT" ;;
on) echo 0 | tee "$USB_PORT" ;;
*) echo "Usage: $0 {on|off}" ;;
esac
I’m hardcoding the USB_PORT
variable because I don’t plan to change the cables, but it could be easily replaced or included as an additional argument.
Write it to /usr/local/bin/usb.sh
or your name of choice. Give it execute permissions for root only:
chmod 0744 /usr/local/bin/usb.sh
Writing the new udev rules
Let’s use that script for the udev rules:
# Remove USB devices connected to the monitor when the USB HUB is removed.
ACTION=="remove", SUBSYSTEM=="usb", ENV{PRODUCT}=="43e/9a39/*", RUN+="/usr/local/bin/usb.sh off"
# Bring back the USB devices connected to the monitor
ACTION=="add", SUBSYSTEM=="usb", ATTR{idVendor}=="043e", ATTR{idProduct}=="9a39", RUN+="/usr/local/bin/usb.sh on"
The matchers are different because, as I figured out the hard way, some attributes are not available for specific events such as remove
, so if something is not triggering, it’s worth checking.
If you want to troubleshoot such a thing, here’s an upgraded script with logging capabilities:
#!/bin/bash
LOGFILE="/tmp/usb_control.log"
USB_PORT="/sys/devices/pci0000:00/0000:00:14.0/usb1/1-13/1-13.2/1-13.2:1.0/1-13.2-port1/disable"
# Function to log with timestamp
log_message() {
echo "[$(date '+%Y-%m-%d %H:%M:%S')] $1" >> "$LOGFILE"
}
# Log script execution start
log_message "=== USB Control Script Started ==="
log_message "Command: $0 $*"
log_message "User: $(whoami)"
log_message "PID: $$"
# Log all environment variables
log_message "--- Environment Variables ---"
env | sort >> "$LOGFILE"
log_message "--- End Environment Variables ---"
case "$1" in
off)
log_message "Turning USB port OFF"
if echo 1 | tee "$USB_PORT" >> "$LOGFILE" 2>&1; then
log_message "SUCCESS: USB port disabled"
else
log_message "ERROR: Failed to disable USB port (exit code: $?)"
fi
;;
on)
log_message "Turning USB port ON"
if echo 0 | tee "$USB_PORT" >> "$LOGFILE" 2>&1; then
log_message "SUCCESS: USB port enabled"
else
log_message "ERROR: Failed to enable USB port (exit code: $?)"
fi
;;
*)
log_message "ERROR: Invalid argument '$1'"
log_message "Usage: $0 {on|off}"
echo "Usage: $0 {on|off}"
exit 1
;;
esac
log_message "=== USB Control Script Finished ==="
log_message ""
This will log the process environment to /tmp/usb_control.log
for each action.
Here’s the remove
output:
[2025-08-05 18:27:29] === USB Control Script Started ===
[2025-08-05 18:27:29] Command: /usr/local/bin/usb.sh off
[2025-08-05 18:27:29] User: root
[2025-08-05 18:27:29] PID: 264052
[2025-08-05 18:27:29] --- Environment Variables ---
ACTION=remove
BUSNUM=001
DEVNAME=/dev/bus/usb/001/074
DEVNUM=074
DEVPATH=/devices/pci0000:00/0000:00:14.0/usb1/1-13/1-13.2/1-13.2.3
DEVTYPE=usb_device
ID_PATH=pci-0000:00:14.0-usb-0:13.2.3
ID_PATH_TAG=pci-0000_00_14_0-usb-0_13_2_3
ID_PATH_WITH_USB_REVISION=pci-0000:00:14.0-usbv2-0:13.2.3
MAJOR=189
MINOR=73
PRODUCT=43e/9a39/417
PWD=/
SEQNUM=6756
SHLVL=1
SUBSYSTEM=usb
TYPE=239/2/1
UDEV_DATABASE_VERSION=1
USEC_INITIALIZED=33503583770
_=/usr/bin/env
[2025-08-05 18:27:29] --- End Environment Variables ---
[2025-08-05 18:27:29] Turning USB port OFF
1
[2025-08-05 18:27:29] SUCCESS: USB port disabled
[2025-08-05 18:27:29] === USB Control Script Finished ===
And here’s add
output:
[2025-08-05 18:27:39] === USB Control Script Started ===
[2025-08-05 18:27:39] Command: /usr/local/bin/usb.sh on
[2025-08-05 18:27:39] User: root
[2025-08-05 18:27:39] PID: 264156
[2025-08-05 18:27:39] --- Environment Variables ---
ACTION=add
BUSNUM=001
DEVNAME=/dev/bus/usb/001/077
DEVNUM=077
DEVPATH=/devices/pci0000:00/0000:00:14.0/usb1/1-13/1-13.2/1-13.2.3
DEVTYPE=usb_device
DRIVER=usb
ID_BUS=usb
ID_MODEL=LG_Monitor_Controls
ID_MODEL_ENC=LG\x20Monitor\x20Controls
ID_MODEL_FROM_DATABASE=27UP850 - WK.AEUDCSN - External Monitor 4K
ID_MODEL_ID=9a39
ID_PATH=pci-0000:00:14.0-usb-0:13.2.3
ID_PATH_TAG=pci-0000_00_14_0-usb-0_13_2_3
ID_PATH_WITH_USB_REVISION=pci-0000:00:14.0-usbv2-0:13.2.3
ID_PROCESSING=1
ID_REVISION=0417
ID_SERIAL=LG_Electronics_Inc._LG_Monitor_Controls_405NTNHJE279
ID_SERIAL_SHORT=405NTNHJE279
ID_USB_INTERFACES=:030000:
ID_USB_MODEL=LG_Monitor_Controls
ID_USB_MODEL_ENC=LG\x20Monitor\x20Controls
ID_USB_MODEL_ID=9a39
ID_USB_REVISION=0417
ID_USB_SERIAL=LG_Electronics_Inc._LG_Monitor_Controls_405NTNHJE279
ID_USB_SERIAL_SHORT=405NTNHJE279
ID_USB_VENDOR=LG_Electronics_Inc.
ID_USB_VENDOR_ENC=LG\x20Electronics\x20Inc.
ID_USB_VENDOR_ID=043e
ID_VENDOR=LG_Electronics_Inc.
ID_VENDOR_ENC=LG\x20Electronics\x20Inc.
ID_VENDOR_FROM_DATABASE=LG Electronics USA, Inc.
ID_VENDOR_ID=043e
MAJOR=189
MINOR=76
PRODUCT=43e/9a39/417
PWD=/
SEQNUM=6827
SHLVL=1
SUBSYSTEM=usb
TYPE=239/2/1
UDEV_DATABASE_VERSION=1
USEC_INITIALIZED=33529430900
_=/usr/bin/env
[2025-08-05 18:27:39] --- End Environment Variables ---
[2025-08-05 18:27:39] Turning USB port ON
0
[2025-08-05 18:27:39] SUCCESS: USB port enabled
[2025-08-05 18:27:39] === USB Control Script Finished ===
As you can see the environments are indeed different.
And that’s all folks, I hope you liked it.