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

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

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

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

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

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

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

2026.01.02
现已使用电能计量模块来判断市电状态,也是使用串口连接的模块,程序见:im1281b_home_power
另外给机顶盒端脚本添加了邮件发送功能。


连接好后,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
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
#!/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 /root/.local/ping_wol_open.lock -c "bash /root/.local/ping_wol_open.sh"

gateway='192.168.1.1'
server='192.168.1.5'
log_file='/var/log/ping_eth_restart.log'
count_file='/tmp/ping_eth_restart.count'
post_mail='[email protected]'
count_max=2
retry_count=0


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

date_time() {
date "+%Y/%m/%d %H:%M:%S"
}

send_mail() {
(echo -e "$2\n时间:`date_time`" | mail -s "$1" -r $post_mail $post_mail)&
}

if (($(up_time) < 120)); then
sleep 30
send_mail "server监测脚本已启动" "server监测脚本已启动"
fi

# create/load count file
if [ -f $count_file ]; then
count=$(cat $count_file)
else
echo -n 0 > $count_file
count=0
fi

count_0() {
if ((count != 0)); then
echo -n 0 > $count_file
fi
}

log_w() {
echo "[`date_time`] $*" >> $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() {
# gateway test
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
# if exec count > count_max, no retry to start the server anymore.
if ((count > count_max)); then
log_w "count > count_max, no retry anymore."
exit
fi

sleep 20 # 防止误判

if ! ping_server; then
# wakeonlan -i 192.168.1.255 $server_mac
open_server
retry_count=1
log_w "Waking up server..."
send_mail "启动服务器中" "正在启动服务器..."
sleep 60
fi

# Retry
while ! ping_server; do
if ((retry_count >= 3)); then
log_w "Server maybe down..."
((count++))
echo -n $count > $count_file
(echo -e "服务器无法连接,请联系管理员处理!!!\n时间:`date_time`" | mail -s '服务器无法连接' -r $post_mail $post_mail [email protected])&
exit 1
fi

if ((retry_count == 2)); then
sleep 60
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 -i 192.168.1.255 $server_mac
open_server
((retry_count++))
log_w "Server maybe off, retrying...($retry_count)"
sleep 60
done

log_w "Server is stared."
send_mail "服务器已启动" "服务器已启动"
#retry_count=0
count_0
else
count_0
fi
}

if [[ $(cat /usr/share/test-web/index.html) == "ciallo-114514-status-ok" ]]; then
main_func
else
log_w "市电异常,退出进程"
fi

服务器端脚本(机顶盒需开启 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` == "ciallo-114514-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