Control USB PC Fan With Shelly Outlet HTTP API

In this scenario, I have a Mele Quieter 3C that I wanted to prevent from throttling due to higher CPU temperatures. These mini pcs don’t have any fans for cooling as they are marketed as low noise devices. This pc is running my PLEX server on Ubuntu 24 and few other small apps. At time, it gets pretty hot. So, I decided to setup a usb powered fan, plugged into a Shelly Outlet Gen 4 and setup a script and a system service for temperature monitoring to trigger the outlet to turn on or off based on the temperature.

Below is the script for monitoring. I saved this file in my home folder. I recommend the same for you to make it easier to adjust the script. This script is meant for monitoring the localhost and 1 or more other servers running ubuntu with lm-sensors. So, if you want to monitor just the localhost, you still need to enter in something for the <user> and [host] when running the script. See Usage: in script below.

Create a file named temp_shelly_control.sh

nano temp_shelly_control.sh

Copy the block of code below and paste it into the editor, adjust the service ON_URL and OFF_URL ot match your specific Shelly Outlet and save the file.

#!/bin/bash
# Script: temp_shelly_control.sh
# Purpose: Monitor CPU temps (local + remote) and control Shelly outlet
# Usage: ./temp_shelly_control.sh <user> [host1 host2 ...]
# Edit the service file sudo nano /etc/systemd/system/temp_shelly_control.service


ON_URL="http://office.shelly.outlet/rpc/Switch.Set?id=0&on=true"
OFF_URL="http://office.shelly.outlet/rpc/Switch.Set?id=0&on=false"
CHECK_INTERVAL=10   # seconds between checks
ON_THRESHOLD=68     # °C
OFF_THRESHOLD=55    # °C

set -e

usage() {
    echo "Usage: $0 <user> [host1 host2 ...]"
    exit 1
}

# Function to get CPU temperature locally (integer °C)
get_local_cpu_temp() {
    if command -v sensors >/dev/null 2>&1; then
        sensors 2>/dev/null | grep -m 1 "Package id 0" | awk '{gsub(/\+|°C/,""); printf "%d\n", $4}' ||
        sensors 2>/dev/null | grep -m 1 "temp1" | awk '{gsub(/\+|°C/,""); printf "%d\n", $2}'
    elif [ -f /sys/class/thermal/thermal_zone0/temp ]; then
        awk '{printf "%d\n", $1/1000}' /sys/class/thermal/thermal_zone0/temp
    fi
}

# Function to get CPU temperature remotely (integer °C)
get_remote_cpu_temp() {
    local server="$1"
    ssh -o BatchMode=yes -o ConnectTimeout=10 "$USER@$server" '
        if command -v sensors >/dev/null 2>&1; then
            sensors 2>/dev/null | grep -m 1 "Package id 0" | awk "{gsub(/\\+|°C/,\"\"); printf \"%d\n\", \$4}" ||
            sensors 2>/dev/null | grep -m 1 "temp1" | awk "{gsub(/\\+|°C/,\"\"); printf \"%d\n\", \$2}"
        elif [ -f /sys/class/thermal/thermal_zone0/temp ]; then
            awk "{printf \"%d\n\", \$1/1000}" /sys/class/thermal/thermal_zone0/temp
        fi
    ' 2>/dev/null || echo "NaN"
}

# Validate arguments
if [ "$#" -lt 1 ]; then
    usage
fi

USER="$1"
shift
SERVERS=("$@")

# Track Shelly state (0 = OFF, 1 = ON)
SHELLY_STATE=0

while true; do
    TEMPS=()

    # Get local temp
    local_temp=$(get_local_cpu_temp)
    TEMPS+=("$local_temp")

    # Get remote temps
    for server in "${SERVERS[@]}"; do
        remote_temp=$(get_remote_cpu_temp "$server")
        TEMPS+=("$remote_temp")
    done

    # Debug output (optional)
    echo "Temps: ${TEMPS[*]}"

    # Determine if any temp exceeds ON threshold
    any_over=0
    all_below=1
    for t in "${TEMPS[@]}"; do
        if [ "$t" != "NaN" ]; then
            if [ "$t" -gt "$ON_THRESHOLD" ]; then
                any_over=1
            fi
            if [ "$t" -ge "$OFF_THRESHOLD" ]; then
                all_below=0
            fi
        fi
    done

    # Control Shelly outlet
    if [ "$any_over" -eq 1 ] && [ "$SHELLY_STATE" -eq 0 ]; then
        echo "🔥 Temp exceeded $ON_THRESHOLD°C — Turning Shelly ON"
        curl -s "$ON_URL" >/dev/null
        SHELLY_STATE=1
    elif [ "$all_below" -eq 1 ] && [ "$SHELLY_STATE" -eq 1 ]; then
        echo "❄️ Temps dropped below $OFF_THRESHOLD°C — Turning Shelly OFF"
        curl -s "$OFF_URL" >/dev/null
        SHELLY_STATE=0
    fi

    sleep "$CHECK_INTERVAL"
done

Next, create the service file…

sudo nano /etc/systemd/system/temp_shelly_control.service

Copy and paste the following code block into the editor, then save and close it. Be sure to adjust your ExecStart path to match where you put your script as well as the User and WorkingDirectory.

[Unit]
Description=CPU Temperature Monitor and Shelly Control
After=network.target

[Service]
Type=simple
ExecStart=/home/matt/Scripts/temp_shelly_control.sh matt landscape.nwd.bz
Restart=always
RestartSec=5
User=matt
# Optional: set a working directory
WorkingDirectory=/home/matt/Scripts
# Optional: log output to journal
StandardOutput=journal
StandardError=journal

[Install]
WantedBy=multi-user.target

Next, enable the system service and start it

sudo systemctl daemon-reload
sudo systemctl enable temp_shelly_control.service
sudo systemctl start temp_shelly_control.service

That’s it! Now, you can view the status in terminal.

sudo systemctl status temp_shelly_control.service

Leave a Comment

This site uses Akismet to reduce spam. Learn how your comment data is processed.