用旧苹果手机当nas监控副屏(一)
现在玩nas的人越来越多,nas一般没有屏幕,无法随时监控运行状态,于是就有了图灵智显,这是一个硬件(屏幕+有线连接)+软件的实现方法。这里使用远古时代的iphone5s当屏幕,配合软件实现监控nas状态。
iphone5s的浏览器已经不能解析复杂的js文件,本文使用shell脚本采集系统状态数据,再用简单的html文件定时刷新来显示状态信息。
状态数据采集脚本
/usr/local/bin/status.sh:
#!/bin/bash
# nas_status_final.sh - 最终简化版
INTERFACE="enp4s0"
JSON_FILE="/var/www/html/status.json"
NET_FILE="/tmp/net_cache.txt"
# 指定要监控的磁盘挂载点(可以根据需要修改)
MOUNT_POINT="/vol1/docker/overlay2/bfe25ec30f56cc56436cad793bedce52eaf4b0ce409409a65c64a4742e98bdb5/merged" # 默认监控根目录,可以改为 "/home"、"/data" 等
# 格式化速度(KB/s和MB/s)
format_speed_simple() {
local bytes_per_sec=$1
# 小于1KB/s显示为0
if [ -z "$bytes_per_sec" ] || [ $bytes_per_sec -lt 1024 ]; then
echo "0 KB/s"
return
fi
# 转换为KB/s(整数部分)
local kb_sec=$((bytes_per_sec / 1024))
# 如果大于1024KB/s,转换为MB/s
if [ $kb_sec -ge 1024 ]; then
local mb_sec=$((bytes_per_sec / 1048576))
local remainder=$(( (bytes_per_sec % 1048576) * 100 / 1048576 ))
echo "${mb_sec}.${remainder} MB/s"
else
local remainder=$(( (bytes_per_sec % 1024) * 100 / 1024 ))
echo "${kb_sec}.${remainder} KB/s"
fi
}
# 将uptime转换为中文
convert_uptime_to_chinese() {
local uptime_str=$(uptime -p | sed 's/up //')
# 替换英文单位为中文
echo "$uptime_str" | sed \
-e 's/ days/天/g' \
-e 's/ hours/小时/g' \
-e 's/ minutes/分/g' \
-e 's/ weeks/周/g'
}
# 计算网络
get_network_stats() {
# 当前值
read rx_now tx_now <<< $(grep "$INTERFACE:" /proc/net/dev 2>/dev/null | awk '{print $2, $10}' || echo "0 0")
now=$(date +%s)
# 上次值
if [ -f "$NET_FILE" ]; then
read rx_last tx_last time_last < "$NET_FILE"
else
rx_last=$rx_now
tx_last=$tx_now
time_last=$now
fi
# 保存
echo "$rx_now $tx_now $now" > "$NET_FILE"
# 时间差
time_diff=$((now - time_last))
[ $time_diff -eq 0 ] && time_diff=1
# 计算速度
rx_speed=$(( (rx_now - rx_last) / time_diff ))
tx_speed=$(( (tx_now - tx_last) / time_diff ))
[ $rx_speed -lt 0 ] && rx_speed=0
[ $tx_speed -lt 0 ] && tx_speed=0
# 格式化
download=$(format_speed_simple $rx_speed)
upload=$(format_speed_simple $tx_speed)
# 总流量(GB,保留2位小数)
total_down=$(printf "%.2f" $(echo "$rx_now / 1073741824" | bc -l 2>/dev/null || echo "0"))
total_up=$(printf "%.2f" $(echo "$tx_now / 1073741824" | bc -l 2>/dev/null || echo "0"))
# 返回
echo "$download"
echo "$upload"
echo "$total_down"
echo "$total_up"
}
# 获取指定挂载点的磁盘信息
get_disk_info() {
local mount_point=$1
# 使用df命令获取指定挂载点的信息
df_output=$(df -h "$mount_point" 2>/dev/null | tail -1)
if [ -n "$df_output" ]; then
# 从df输出中提取信息
usage=$(echo "$df_output" | awk '{print $5}')
total=$(echo "$df_output" | awk '{print $2}')
used=$(echo "$df_output" | awk '{print $3}')
free=$(echo "$df_output" | awk '{print $4}')
# 如果没有可用空间信息(旧版本df),计算它
if [ -z "$free" ] || [ "$free" = "-" ]; then
total_bytes=$(df -B1 "$mount_point" 2>/dev/null | tail -1 | awk '{print $2}')
used_bytes=$(df -B1 "$mount_point" 2>/dev/null | tail -1 | awk '{print $3}')
free_bytes=$((total_bytes - used_bytes))
# 转换为人类可读格式
free=$(echo "$free_bytes" | awk '{
if ($1 >= 1099511627776) printf "%.1fT", $1/1099511627776
else if ($1 >= 1073741824) printf "%.1fG", $1/1073741824
else if ($1 >= 1048576) printf "%.1fM", $1/1048576
else printf "%.1fK", $1/1024
}')
fi
else
# 如果指定的挂载点不存在,使用根目录
df_output=$(df -h / | tail -1)
usage=$(echo "$df_output" | awk '{print $5}')
total=$(echo "$df_output" | awk '{print $2}')
used=$(echo "$df_output" | awk '{print $3}')
free=$(echo "$df_output" | awk '{print $4}')
fi
echo "$usage"
echo "$total"
echo "$used"
echo "$free"
}
# 主程序
{
# 获取网络数据
net_data=$(get_network_stats)
download_speed=$(echo "$net_data" | head -1)
upload_speed=$(echo "$net_data" | head -2 | tail -1)
total_down=$(echo "$net_data" | head -3 | tail -1)
total_up=$(echo "$net_data" | tail -1)
# 获取CPU使用率
cpu=$(top -bn1 | grep "Cpu(s)" | awk '{printf "%.1f%%", $2 + $4}')
# 获取内存信息
read mem_usage mem_used mem_total <<< $(free -m | awk 'NR==2{
used_mb=$3; total_mb=$2
printf "%.1f%% %d %d", used_mb*100/total_mb, used_mb, total_mb
}')
# 获取指定挂载点的磁盘信息
disk_data=$(get_disk_info "$MOUNT_POINT")
disk_usage=$(echo "$disk_data" | head -1)
disk_total=$(echo "$disk_data" | head -2 | tail -1)
disk_used=$(echo "$disk_data" | head -3 | tail -1)
disk_free=$(echo "$disk_data" | tail -1)
# 获取中文运行时间
uptime_chinese=$(convert_uptime_to_chinese)
# 获取系统负载(去掉首尾空格)
system_load=$(uptime | awk -F'load average:' '{print $2}' | sed 's/^ *//;s/ *$//')
# 在JSON中添加监控的挂载点信息
cat > "$JSON_FILE" << EOF
{
"cpu": "$cpu",
"memory": {
"usage": "$mem_usage",
"used": "${mem_used}MB",
"total": "${mem_total}MB"
},
"disk": {
"mount_point": "$MOUNT_POINT",
"usage": "$disk_usage",
"total": "$disk_total",
"used": "$disk_used",
"available": "$disk_free"
},
"network": {
"download_speed": "$download_speed",
"upload_speed": "$upload_speed",
"total_download": "${total_down} GB",
"total_upload": "${total_up} GB",
"interface": "$INTERFACE"
},
"system": {
"uptime": "$uptime_chinese",
"load": "$system_load",
"time": "$(date '+%Y-%m-%d %H:%M:%S')"
}
}
EOF
}
docker cp /var/www/html/status.json php://var/www/html/
INTERFACE:网卡名,用ifconfig获取。
MOUNT_POINT:值是"/“就是整个磁盘,nas有添加的存贮空间,用df -h获取相应路径。
最后一句,nas上的web服务在docker里,这里把状态数据复制到docker里。
chmod +x /usr/local/bin/status.sh
运行脚本得到的json样式:
{
"cpu": "29.4%",
"memory": {
"usage": "57.0%",
"used": "2179MB",
"total": "3825MB"
},
"disk": {
"mount_point": "/vol1/docker/overlay2/bfe25ec30f56cc56436cad793bedce52eaf4b0ce409409a65c64a4742e98bdb5/merged",
"usage": "48%",
"total": "221G",
"used": "104G",
"available": "117G"
},
"network": {
"download_speed": "1.50 KB/s",
"upload_speed": "1.83 KB/s",
"total_download": "7.75GB",
"total_upload": "6.23GB",
"interface": "enp4s0"
},
"system": {
"uptime": "3天, 11小时, 28分",
"load": "0.19, 0.23, 0.26",
"time": "2026-01-28 11:12:41"
}
}
status.html
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>NAS状态监控</title>
<meta http-equiv="refresh" content="10">
<style>
body { font-family: sans-serif; margin: 20px; font-size: 16px; }
.section {
border: 1px solid #ccc;
margin: 10px 0;
padding: 10px;
border-radius: 5px;
}
.title {
font-weight: bold;
color: #333;
margin-bottom: 10px;
}
.row {
display: flex;
justify-content: space-between;
margin: 5px 0;
}
.time {
text-align: center;
color: #666;
font-size: 12px;
margin-top: 20px;
}
</style>
</head>
<body>
<div class="section">
<div id="cpu">CPU: 加载中...</div>
<div id="memory">内存: 加载中...</div>
<div id="disk">磁盘: 加载中...</div>
<div id="network">网络: 加载中...</div>
<div id="system">系统: 加载中...</div>
</div>
<div class="time" id="timestamp">最后更新: 加载中...</div>
<script>
// 兼容老iOS的简单请求
function loadStatus() {
var xhr = new XMLHttpRequest();
xhr.open('GET', 'status.json?' + new Date().getTime(), true);
xhr.onreadystatechange = function() {
if (xhr.readyState === 4) {
if (xhr.status === 200) {
try {
var data = JSON.parse(xhr.responseText);
updateDisplay(data);
} catch(e) {
document.getElementById('cpu').innerHTML = '数据解析错误';
console.error('JSON解析错误:', e);
}
} else {
document.getElementById('cpu').innerHTML = '连接失败: ' + xhr.status;
}
}
};
xhr.send();
}
function updateDisplay(data) {
// CPU
document.getElementById('cpu').innerHTML =
'<div class="row"><span>CPU使用率:</span><span>' + data.cpu + '</span></div>';
// 内存
document.getElementById('memory').innerHTML =
'<div class="row"><span>内存使用:</span><span>' + data.memory.usage +
' (' + data.memory.used + '/' + data.memory.total + ')</span></div>';
// 磁盘
document.getElementById('disk').innerHTML =
'<div class="row"><span>磁盘使用:</span><span>' + data.disk.usage +
' (' + data.disk.used + '/' + data.disk.total + ')</span></div>' +
'<div class="row"><span>可用空间:</span><span>' + data.disk.available + '</span></div>';
// 网络 - 这里特别注意解析
var net = data.network;
document.getElementById('network').innerHTML =
'<div class="row"><span>下载速度:</span><span>' + net.download_speed + '</span></div>' +
'<div class="row"><span>上传速度:</span><span>' + net.upload_speed + '</span></div>' +
'<div class="row"><span>总下载:</span><span>' + net.total_download + '</span></div>' +
'<div class="row"><span>总上传:</span><span>' + net.total_upload + ' </span></div>' +
'<div class="row"><span>接口:</span><span>' + net.interface + '</span></div>';
// 系统信息
document.getElementById('system').innerHTML =
'<div class="row"><span>运行时间:</span><span>' + data.system.uptime + '</span></div>' +
'<div class="row"><span>系统负载:</span><span>' + data.system.load + '</span></div>';
// 更新时间
document.getElementById('timestamp').innerHTML =
'最后更新: ' + data.system.time + ' (每10秒自动刷新)';
// 调试信息(可选)
console.log('网络数据:', data.network);
}
// 页面加载时运行
window.onload = function() {
loadStatus();
// 每10秒自动刷新(配合meta标签)
setInterval(loadStatus, 10000);
};
</script>
</body>
</html>
文件和status.json在同一路径,放在web服务目录下。
定时刷新脚本数据
在crond里添加定时刷新。
crontab -e:在最后添加以下语句
* * * * * /usr/local/bin/status.sh
* * * * * sleep 10; /usr/local/bin/status.sh
* * * * * sleep 20; /usr/local/bin/status.sh
* * * * * sleep 30; /usr/local/bin/status.sh
* * * * * sleep 40; /usr/local/bin/status.sh
* * * * * sleep 50; /usr/local/bin/nas_status.sh
每10秒刷新一次。
效果图

屏幕已经发黄,里面因为拆机换后盖和电池进灰,但作为一个监控副屏,还是能吊打单片机+显示屏做的副屏,充分发挥了一个不锈钢脸盆的价值。
html中的<meta http-equiv="refresh" content="10">会造成页面闪烁,删掉就好了。
网页特效
由于iphone5s上的ios12.5.8不支持网页背景图,不能做成比较炫酷的效果。style语句只包含新增部分。
渐变色效果:
body {
background: linear-gradient(135deg, #ffffff 0%, #ff1493 100%);
}
.section {
background-color: rgba(255, 255, 255, 0.40);
}
动态渐变色效果:
body {
background: linear-gradient(-45deg, #ee7752, #e73c7e, #23a6d5, #23d5ab);
background-size: 400% 400%;
animation: gradient 15s ease infinite;
}
@keyframes gradient {
0% { background-position: 0% 50%; }
50% { background-position: 100% 50%; }
100% { background-position: 0% 50%; }
}
以上脚本和网页代码均由deepseek生成,加少量人工修改。