session-tools/bin/power-cycle

89 lines
3 KiB
Text
Raw Normal View History

#!/bin/sh
# power-cycle <host> — off, wait POWER_CYCLE_OFF_SECS (default 5), on
# power-cycle off|on|status <host> — explicit single action
# power-cycle list — show configured host -> plug mappings
#
# Targets Shelly Gen2 plugs (Plus Plug US/S, etc.) via their local HTTP RPC.
# No cloud, no account. The host must be reachable on the same network/VPN.
#
# Config: ~/.config/power-cycle/plugs.conf
# one entry per line: <host> <plug-base-url>
# blank lines and lines starting with # are ignored.
# Example:
# apricot http://10.0.0.117
# plum http://10.0.0.119
#
# Env:
# POWER_CYCLE_OFF_SECS seconds to stay off during a cycle (default 5)
# POWER_CYCLE_TIMEOUT per-request curl timeout in seconds (default 5)
set -eu
config="${XDG_CONFIG_HOME:-$HOME/.config}/power-cycle/plugs.conf"
off_secs=${POWER_CYCLE_OFF_SECS:-5}
http_timeout=${POWER_CYCLE_TIMEOUT:-5}
die() { echo "power-cycle: $*" >&2; exit 1; }
usage() {
sed -n '2,/^$/p' "$0" | sed 's/^# \{0,1\}//'
exit 2
}
lookup_plug() {
# echoes the plug base URL for $1, or exits non-zero with a hint.
host=$1
[ -f "$config" ] || die "no config at $config — create it (see 'power-cycle' help)"
url=$(awk -v h="$host" '
/^[[:space:]]*(#|$)/ { next }
$1 == h { print $2; found=1; exit }
END { exit !found }
' "$config") || die "no plug configured for '$host' in $config"
[ -n "$url" ] || die "empty plug URL for '$host' in $config"
printf %s "$url"
}
shelly_set() {
# shelly_set <base-url> <true|false>
base=$1; state=$2
curl --fail --silent --show-error --max-time "$http_timeout" \
"$base/rpc/Switch.Set?id=0&on=$state" >/dev/null \
|| die "plug $base unreachable — for modem outages, fall back to BLE (SwitchBot app)"
}
shelly_status() {
# echoes "on" or "off"
base=$1
body=$(curl --fail --silent --show-error --max-time "$http_timeout" \
"$base/rpc/Switch.GetStatus?id=0") \
|| die "plug $base unreachable"
case "$body" in
*'"output":true'*) echo on ;;
*'"output":false'*) echo off ;;
*) die "unrecognized Shelly response: $body" ;;
esac
}
cmd_list() {
[ -f "$config" ] || die "no config at $config"
awk '/^[[:space:]]*(#|$)/ { next } { printf " %-15s %s\n", $1, $2 }' "$config"
}
case "${1:-}" in
''|-h|--help|help) usage ;;
list) cmd_list ;;
off) [ $# -eq 2 ] || usage; shelly_set "$(lookup_plug "$2")" false ;;
on) [ $# -eq 2 ] || usage; shelly_set "$(lookup_plug "$2")" true ;;
status) [ $# -eq 2 ] || usage; shelly_status "$(lookup_plug "$2")" ;;
*)
[ $# -eq 1 ] || usage
host=$1
base=$(lookup_plug "$host")
echo "cycling $host: off -> ${off_secs}s -> on"
shelly_set "$base" false
sleep "$off_secs"
shelly_set "$base" true
echo "done. give the host ~30-90s to finish booting."
;;
esac