前情回顾:博主已经买了个 UPS来保证家里断电后,电脑还能有足够的时间来处理未完成的任务并关机,从而保证数据还有硬件的安全。来电断电报警器也是为此准备的:断电后报警器发出信息,接着我可以远程关机,但避免不了我没法及时操作的情况,于是便有了此文。

博主已有一台 x86 的电脑和一台 ARM 的机顶盒,机顶盒已装好 Armbian,不接 UPS;电脑装了 ArchLinux 且开启了 WOL,接UPS。于是就可以写个脚本,让电脑在发现了机顶盒不在线(即断电)的一定时间后自动关机;当电脑关机且来电后,机顶盒开机,然后通过 WOL 唤醒电脑。

2024.02.09
经过十多天的测试发现,开启 WOL 后似乎会造成电脑没法完全关机,受限于个人能力和精力,只好放弃 WOL,转用 USB 串口继电器。。。
继电器连接着电脑的开关机线,然后机顶盒控制 USB 串口继电器吸合/断开,以此来操控电脑的开关机,详情请看图片&脚本:

2024.03.05
额……现在发现似乎是新 bios 的问题来着,有空再回退 bios 版本试试了。不过好在现在有可以远程控制的继电器。

2024.05.12
似乎确实是 bios 的问题,回退到上一个版本的就好了。现在依旧在使用继电器方案开关机,然后让 Copilot 优化了下代码。

2024.05.24
脚本更新:机顶盒端脚本同样使用 crontab 定时执行。

2024.06.10
脚本更新:机顶盒端脚本增加网关状态判断,如果网关 ping 不通则不动作。
起因:可能是因为上上个星期修改了路由器的配置,然后昨天光猫重新拨号后,路由器无法正确配置网络地址,接着电脑自动关机,机顶盒的脚本也没法正常工作,全部机器都失联宕机了。。。
后续打算改用现成的电能计量模块来判断市电状态。


连接好后,Armbian里可以看到连接的 CH340:

然后接线


机顶盒端脚本:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
#!/usr/bin/bash
set -o nounset
set -o pipefail

# 脚本参考:
# - https://blog.csdn.net/ZJXCSDN_/article/details/115396258
# - https://post.smzdm.com/p/awze04lg/

# crontab: */3 * * * * flock -xn /path/to/ping_wol_open.lock -c "bash /path/to/ping_wol_open.sh"
readonly gateway='192.168.0.1'
readonly server='192.168.1.5'
#readonly server_mac="11:45:14:19:19:81" # 修改为要唤醒的机器mac
readonly log_file='/var/log/wol_wake.log'
retry_count=0

up_time() {
cat /proc/uptime | cut -d. -f1
}

if (($(up_time) < 60)); then
sleep 60
fi

log_w() {
echo "[$(date '+%Y/%m/%d %H:%M:%S')] $*" >> $log_file
}

ping_server() {
ping -c 2 $server > /dev/null
}

open_server() {
echo -ne '\xa0\x01\x01\xa2' > /dev/ttyUSB0 # 开启继电器
sleep 0.2
echo -ne '\xa0\x01\x00\xa1' > /dev/ttyUSB0 # 关闭继电器
}

main_func() {
# 测试网关,网关有问题则不动作,让服务器端自动关机
ping -c 2 $gateway &> /dev/null
if [ $? -ne 0 ]; then
sleep 120
ping -c 5 $gateway &> /dev/null
if [ $? -ne 0 ]; then
log_w "网关无法连接,退出进程"
exit
fi
fi

if ! ping_server; then
sleep 20 # 防止误判

if ! ping_server; then
#wakeonlan $server_mac
open_server
retry_count=1
log_w "Waking up server..."
sleep 60
fi

# Retry
while ! ping_server; do
if ((retry_count >= 3)); then
log_w "Server maybe down..."
exit 1
fi

if ((retry_count == 2)); then
log_w "Server maybe stuck, try force shutdown..."
echo -ne '\xa0\x01\x01\xa2' > /dev/ttyUSB0 # 开启继电器
sleep 5 # 根据你主板的强制关机时间来调整
echo -ne '\xa0\x01\x00\xa1' > /dev/ttyUSB0 # 关闭继电器
sleep 10
fi

#wakeonlan $server_mac
open_server
((retry_count++))
log_w "Server maybe off, retrying...($retry_count)"
sleep 60
done

log_w "Server is stared."
fi
}

main_func

服务器端脚本(机顶盒需开启 http 服务,然后 index.html 里的内容为 “status-ok”):

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
#!/usr/bin/bash
set -o nounset
set -o pipefail

# 脚本参考:
# - https://blog.csdn.net/ZJXCSDN_/article/details/115396258
# - https://post.smzdm.com/p/awze04lg/

# crontab: * * * * * flock -xn /path/to/http_shutdown.lock -c "bash /path/to/http_shutdown.sh"

readonly server='192.168.1.6' # index.html: status-ok
readonly log_file='/var/log/http_shutdown.log'
retry_count=0

log_w() {
echo "[$(date '+%Y/%m/%d %H:%M:%S')] $*" >> $log_file
}

check_box_status() {
[[ `curl -A 'Manjaro' -s -m 1 $server` == "status-ok" ]]
}

main_func() {
if ! check_box_status; then
# 防止误判
sleep 30
if check_box_status; then
exit
fi

shutdown 10
log_w "Power failure detected. System will shutdown in 10 minutes if power not restored."

# Retry
while true; do
if ! check_box_status; then
((retry_count++))
if ((retry_count > 25)); then poweroff; fi # 感觉没啥作用,但为了保险还是加了这一行
log_w "Power maybe off, retrying...($retry_count)"
sleep 30
else
shutdown -c
log_w "Power restored. Shutdown Canceled."
retry_count=0
break
fi
done
fi
}

main_func