USB Hub Power Control

I bought a Rosonway RSH-A104 USB hub to plug into the router, so I can plug in the external USB hard drive and external USB AX3000M WiFi adapter, and so I can programmatically control power to the 92mm USB cooling fan sitting on top of the router.

The fan is plugged into Port 1 (the port nearest the USB cable end of the hub). The AX3000M is plugged into Port 3, the hard drive is plugged into Port 4.

This requires the uhubctl package be installed.

Run uhubctl to enumerate your hubs, then put the correct HUB_ID and PORT numbers in the script below. Change the temperature range to your liking. This doesn’t just measure CPU temperature, it also measures the WiFi chip temperatures, and triggers on the highest reading.

Paste this into a Terminal window logged into the OpenWRT One router via ssh, then press Enter:

cat << 'EOF' > /etc/fan_control.sh
#!/bin/sh

# CONFIGURATION
HUB_ID="1-1"
PORT="1"
TEMP_HIGH=55      # Turn fan ON if ANY sensor reaches 55°C
TEMP_LOW=50       # Turn fan OFF only when ALL sensors drop below 50°C
CHECK_INTERVAL=30 # Check every 30 seconds

# Helper function to locate hwmon paths
get_wifi_temp_path() {
    for path in /sys/class/ieee80211/$1/hwmon*/temp1_input; do
        if [ -f "$path" ]; then
            echo "$path"
            return
        fi
    done
}

# Resolve Wi-Fi paths once at startup
PHY0_PATH=$(get_wifi_temp_path "phy0")
PHY1_PATH=$(get_wifi_temp_path "phy1")

CURRENT_STATE=-1

while true; do
    # 1. Read CPU
    read -r RAW_CPU < /sys/class/thermal/thermal_zone0/temp
    MAX_TEMP=$((RAW_CPU / 1000))

    # 2. Read 2.4GHz WiFi (if path exists)
    if [ -n "$PHY0_PATH" ]; then
        read -r RAW_WIFI0 < "$PHY0_PATH"
        WIFI0_C=$((RAW_WIFI0 / 1000))
        [ "$WIFI0_C" -gt "$MAX_TEMP" ] && MAX_TEMP=$WIFI0_C
    fi

    # 3. Read 5GHz WiFi (if path exists)
    if [ -n "$PHY1_PATH" ]; then
        read -r RAW_WIFI1 < "$PHY1_PATH"
        WIFI1_C=$((RAW_WIFI1 / 1000))
        [ "$WIFI1_C" -gt "$MAX_TEMP" ] && MAX_TEMP=$WIFI1_C
    fi

    # Evaluate against the peak observed temperature
    if [ "$MAX_TEMP" -ge "$TEMP_HIGH" ] && [ "$CURRENT_STATE" -ne 1 ]; then
        uhubctl -l "$HUB_ID" -p "$PORT" -a 1 > /dev/null 2>&1
        CURRENT_STATE=1
        logger -t usb_fan "Peak temp reached ${MAX_TEMP}°C. Fan turned ON."
    elif [ "$MAX_TEMP" -le "$TEMP_LOW" ] && [ "$CURRENT_STATE" -ne 0 ]; then
        uhubctl -l "$HUB_ID" -p "$PORT" -a 0 > /dev/null 2>&1
        CURRENT_STATE=0
        logger -t usb_fan "All sensors cooled to ${MAX_TEMP}°C. Fan turned OFF."
    fi

    sleep "$CHECK_INTERVAL"
done
EOF

Create a service:

vi /etc/init.d/usb_fan

Press i to enter editing mode, then enter the following code:

#!/bin/sh /etc/rc.common

START=99
STOP=10

USE_PROCD=1

start_service() {
    procd_open_instance
    procd_set_param command /etc/fan_control.sh
    procd_set_param respawn # Automatically restarts the script if it ever crashes
    procd_close_instance
}

… then press Esc to exit editing mode, and press :wq to save and exit.

Issue the commands:

chmod +x /etc/init.d/usb_fan
/etc/init.d/usb_fan enable
/etc/init.d/usb_fan start

Make it all impervious to firmware updates: In the LuCI interface: System >> Backup / Flash Firmware >> Configuration (ie: /etc/sysupgrade.conf), paste:

/etc/init.d/usb_fan
/etc/fan_control.sh

… into the list, then click the “Save” button.