Skip to content

Instantly share code, notes, and snippets.

@xarantolus
Created January 8, 2026 11:35
Show Gist options
  • Select an option

  • Save xarantolus/9cf096a0b8095d3fe0c09ce30275ed1b to your computer and use it in GitHub Desktop.

Select an option

Save xarantolus/9cf096a0b8095d3fe0c09ce30275ed1b to your computer and use it in GitHub Desktop.
Uptime monitor with restart using a Wiz v2 smart plug
#!/bin/bash
set -eoux pipefail
# Note: Ensure that in the Wiz v2 apps' settings under security, local communication is allowed
# Then in the same network, put this script onto another Linux machine, and add a crontab entry like this for every 5 minutes:
# */5 * * * * bash /home/pi/gs/watchdog.sh > /dev/null 2>&1
# --- CONFIGURATION ---
TARGET_HOST="<host that should be monitored and can be restarted using the Wiz Smart Plug>"
PLUG_HOST="wiz-<some name>"
LOG_FILE="$(dirname "$0")/watchdog.log"
LOCK_FILE="/tmp/gs-watchdog.lock"
UDP_PORT=38899
# Backoff delays in seconds (30s, 2m, 5m)
RETRY_DELAYS=(30 120 300)
# How long to wait for the host to come online after power on
BOOT_TIMEOUT=60
# --- FUNCTIONS ---
log_msg() {
local msg="[$(date '+%Y-%m-%d %H:%M:%S')] $1"
echo "$msg" >> "$LOG_FILE"
echo "$msg"
}
set_plug_state() {
local state="$1" # true (ON) or false (OFF)
local max_retries=5
local count=0
local payload="{\"method\":\"setPilot\",\"params\":{\"state\":$state}}"
until [ $count -ge $max_retries ]; do
# Send command, capture stdout, silence stderr
result=$(echo "$payload" | nc -u -w 2 "$PLUG_HOST" "$UDP_PORT" 2>/dev/null)
# Check specifically for "success":true in the JSON response
if echo "$result" | grep -q '"success":true'; then
return 0
fi
log_msg "WARN: Retry $((count+1))/$max_retries for state $state. Response was: '$result'"
((count++))
sleep 2
done
log_msg "ERROR: Failed to set plug state to $state after $max_retries attempts."
return 1
}
cleanup() {
rm -f "$LOCK_FILE"
}
check_connection() {
ping -c 1 -W 2 "$TARGET_HOST" > /dev/null 2>&1
}
wait_for_recovery() {
local end_time=$((SECONDS + BOOT_TIMEOUT))
while [ $SECONDS -lt $end_time ]; do
if check_connection; then
return 0
fi
sleep 2
done
return 1
}
# --- EXECUTION ---
# 1. Lock Mechanism
if [ -e "$LOCK_FILE" ]; then
# Optional: Check if PID in lockfile is actually running to handle stale locks
# For now, we assume simple existence check as requested.
echo "Lock file exists. Exiting."
exit 0
fi
echo $$ > "$LOCK_FILE"
trap cleanup EXIT
# 2. Initial Check
if check_connection; then
# Target is up, nothing to do
exit 0
fi
log_msg "ALERT: $TARGET_HOST unreachable. Starting recovery sequence."
# 3. Recovery Loop
for delay in "${RETRY_DELAYS[@]}"; do
log_msg "Power cycling $PLUG_HOST. Waiting ${delay}s before turning ON..."
# Turn OFF
set_plug_state false
# Wait the backoff duration
sleep "$delay"
# Turn ON
set_plug_state true
log_msg "Power ON sent. Waiting up to ${BOOT_TIMEOUT}s for boot..."
# Check if back online
if wait_for_recovery; then
log_msg "SUCCESS: $TARGET_HOST is back online."
exit 0
else
log_msg "FAILURE: Host did not recover within ${BOOT_TIMEOUT}s."
fi
done
log_msg "CRITICAL: All recovery attempts failed. Leaving plug ON."
# Ensure plug is ON before giving up
set_plug_state true
exit 1
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment