使用 Bark + VPS 实现家庭网络断网 / 恢复提醒

一、实现效果

本方案可以实现类似 Apple TV 家庭中枢离线提醒的效果:

  • 家里网络正常时,家里设备定时向 VPS 上报状态
  • VPS 超过指定时间没有收到上报,就通过 Bark 推送“家庭网络可能离线”
  • 家里网络恢复后,VPS 再次收到上报,推送“家庭网络已恢复”
  • RouterOS 外网恢复时,也可以推送当前公网 IP
  • RouterOS 检测到公网 IP 变化时,也可以单独推送提醒

最终通知效果类似:

家庭网络可能离线
已超过 3 分钟未收到家里网络连通数据

恢复时:

家庭网络已恢复
已重新收到家里网络连通数据

RouterOS 外网恢复时:

路由器通知

外网恢复
IP:1.2.3.4

二、整体原理

家里断网后,家里设备已经无法主动发送通知,所以不能只靠家里的设备判断断网。

正确做法是:

家里设备定时访问 VPS
        ↓
VPS 记录最后一次上报时间
        ↓
VPS 每分钟检查一次
        ↓
超过 3 分钟没收到上报
        ↓
VPS 调用 Bark 推送离线通知
        ↓
家里恢复后重新上报
        ↓
VPS 调用 Bark 推送恢复通知

这种方式的关键是:Bark 服务部署在 VPS 上,家里断网时 VPS 仍然在线,可以继续给 iPhone 推送通知。


三、目录结构

本文以 OpenLiteSpeed Docker 站点为例。

宿主机目录:

/data/ols/html/wordpress/home-status

容器内目录:

/usr/local/lsws/Example/html/wordpress/home-status

最终目录结构:

/data/ols/html/wordpress/home-status
├── heartbeat.php
├── check_home.php
└── data
    ├── home.last
    └── home.status

说明:

heartbeat.php   接收家里设备上报
check_home.php  VPS 定时检查是否离线
home.last       最后一次上报时间
home.status     当前状态:online / offline

四、VPS 端:heartbeat.php

heartbeat.php 用来接收家里设备上报,并写入最后上报时间。

编辑文件:

nano /data/ols/html/wordpress/home-status/heartbeat.php

内容如下:

<?php
// heartbeat.php

// 自定义 Token,建议改成复杂一点
$allowToken = '你的自定义Token';

$token = $_GET['token'] ?? '';

if ($token !== $allowToken) {
    http_response_code(403);
    exit('Forbidden');
}

$device = $_GET['device'] ?? 'home';
$device = preg_replace('/[^a-zA-Z0-9_-]/', '', $device);

if ($device === '') {
    $device = 'home';
}

$dataDir = __DIR__ . '/data';

if (!is_dir($dataDir)) {
    if (!mkdir($dataDir, 0755, true)) {
        http_response_code(500);
        exit('mkdir failed');
    }
}

$heartbeatFile = $dataDir . '/' . $device . '.last';

$result = file_put_contents($heartbeatFile, time());

if ($result === false) {
    http_response_code(500);
    exit('write failed');
}

echo "OK " . $device . " " . date('Y-m-d H:i:s');

测试不带 Token:

curl -i "https://你的域名/home-status/heartbeat.php?device=home"

正常应返回:

HTTP/2 403
Forbidden

测试带 Token:

curl -i "https://你的域名/home-status/heartbeat.php?device=home&token=你的自定义Token"

正常应返回:

HTTP/2 200
OK home 2026-06-23 10:51:02

五、VPS 端:check_home.php

check_home.php 用来定时检查 home.last 是否超时。

编辑文件:

nano /data/ols/html/wordpress/home-status/check_home.php

内容如下:

<?php
// check_home.php
// 只允许命令行执行,浏览器访问会显示 Forbidden

if (php_sapi_name() !== 'cli') {
    http_response_code(403);
    exit('Forbidden');
}

$device = 'home';

// 超过多少秒没收到上报就判断为离线
$offlineSeconds = 180; // 3分钟

// Bark 推送地址
// 如果使用官方 Bark:
$barkServer = 'https://api.day.app';

// 如果使用自建 Bark,改成自己的 Bark 域名,例如:
// $barkServer = 'https://你的Bark域名';

// Bark Key
$barkKey = '你的BarkKey';

$dataDir = __DIR__ . '/data';
$heartbeatFile = $dataDir . '/' . $device . '.last';
$statusFile = $dataDir . '/' . $device . '.status';

if (!is_dir($dataDir)) {
    mkdir($dataDir, 0755, true);
}

$now = time();
$last = file_exists($heartbeatFile) ? intval(trim(file_get_contents($heartbeatFile))) : 0;
$oldStatus = file_exists($statusFile) ? trim(file_get_contents($statusFile)) : 'unknown';

$isOnline = ($last > 0 && ($now - $last) <= $offlineSeconds);
$newStatus = $isOnline ? 'online' : 'offline';

// 状态没变化,不重复推送
if ($newStatus === $oldStatus) {
    echo "No change: {$newStatus}\n";
    exit;
}

// 状态变化,写入新状态
file_put_contents($statusFile, $newStatus);

if ($newStatus === 'offline') {
    $title = '家庭网络可能离线';
    $body = '已超过 ' . intval($offlineSeconds / 60) . ' 分钟未收到家里网络连通数据';
    $sound = 'alarm';
} else {
    $title = '家庭网络已恢复';
    $body = '已重新收到家里网络连通数据';
    $sound = 'bell';
}

$url = rtrim($barkServer, '/') . '/' . $barkKey . '/'
    . rawurlencode($title) . '/'
    . rawurlencode($body)
    . '?group=' . rawurlencode('家庭网络')
    . '&sound=' . rawurlencode($sound);

$result = @file_get_contents($url);

echo date('Y-m-d H:i:s') . " {$device} {$oldStatus} -> {$newStatus}\n";
echo $result . "\n";

六、修复 data 目录权限

因为 OpenLiteSpeed Docker 容器里 PHP 通常以 nobody:nogroup 运行,所以需要给 data 目录写权限。

在 VPS 宿主机执行:

cd /data/ols/html/wordpress/home-status

mkdir -p data
chown -R nobody:nogroup data
chmod -R 755 data

如果宿主机用户组不同,也可以使用 UID/GID:

chown -R 65534:65534 /data/ols/html/wordpress/home-status/data
chmod -R 755 /data/ols/html/wordpress/home-status/data

确认:

ls -la /data/ols/html/wordpress/home-status/data

正常应看到:

home.last
home.status

七、Docker 环境下测试 check_home.php

由于 PHP 在 OpenLiteSpeed Docker 容器中,所以测试 check_home.php 要通过 docker exec 执行。

执行:

docker exec -it ols php /usr/local/lsws/Example/html/wordpress/home-status/check_home.php

正常输出可能是:

No change: online

或者状态变化时:

2026-06-23 10:34:06 home offline -> online
{"code":200,"message":"success","timestamp":1782210846}

说明判断和 Bark 推送正常。


八、VPS 添加定时检测任务

VPS 上每分钟执行一次 check_home.php

在 VPS 宿主机执行:

crontab -e

添加:

* * * * * docker exec ols php /usr/local/lsws/Example/html/wordpress/home-status/check_home.php >/dev/null 2>&1

或者一条命令添加:

(crontab -l 2>/dev/null; echo '* * * * * docker exec ols php /usr/local/lsws/Example/html/wordpress/home-status/check_home.php >/dev/null 2>&1') | crontab -

查看:

crontab -l

应看到:

* * * * * docker exec ols php /usr/local/lsws/Example/html/wordpress/home-status/check_home.php >/dev/null 2>&1

九、家里设备添加定时上报

这一步要在家里的常开设备上执行,例如:

Ubuntu
iStoreOS
OpenWrt
NAS
RouterOS

如果是 Linux / Ubuntu,执行:

crontab -e

添加:

* * * * * curl -fsS "https://你的域名/home-status/heartbeat.php?device=home&token=你的自定义Token" >/dev/null 2>&1

也可以一条命令添加:

(crontab -l 2>/dev/null; echo '* * * * * curl -fsS "https://你的域名/home-status/heartbeat.php?device=home&token=你的自定义Token" >/dev/null 2>&1') | crontab -

查看:

crontab -l

十、OpenWrt / iStoreOS 上添加定时上报

如果家里设备是 OpenWrt / iStoreOS,添加后建议重启 cron。

编辑:

crontab -e

加入:

* * * * * curl -fsS "https://你的域名/home-status/heartbeat.php?device=home&token=你的自定义Token" >/dev/null 2>&1

重启 cron:

/etc/init.d/cron restart
/etc/init.d/cron enable

查看日志:

logread | grep cron

十一、检查当前状态

在 VPS 上执行:

cat /data/ols/html/wordpress/home-status/data/home.status
cat /data/ols/html/wordpress/home-status/data/home.last
date +%s

例如:

online
1782212101
1782212123

差值:

1782212123 - 1782212101 = 22 秒

小于 180 秒,说明家里设备正常上报,状态是在线。


十二、测试断网提醒

可以先不要真的断网,而是临时停掉家里设备的上报任务。

在家里设备执行:

crontab -e

把这一行前面加 #

# * * * * * curl -fsS "https://你的域名/home-status/heartbeat.php?device=home&token=你的自定义Token" >/dev/null 2>&1

等待 3~4 分钟,应该收到 Bark 推送:

家庭网络可能离线
已超过 3 分钟未收到家里网络连通数据

恢复 cron 后,等待 1 分钟左右,会收到:

家庭网络已恢复
已重新收到家里网络连通数据

十三、RouterOS 外网恢复通知当前 IP

如果 RouterOS 的 address-list 里已经有自动更新的公网 IP,例如:

auto-wan-ip

可以直接从 address-list 取 IP,并通过 Bark 推送。

脚本如下:

:local ids [/ip firewall address-list find list="auto-wan-ip"]

:if ([:len $ids] = 0) do={
    :log warning "auto-wan-ip is empty"
    :return
}

:local pubip [/ip firewall address-list get [:pick $ids 0] address]

/tool fetch url="https://你的Bark域名/你的BarkKey" \
http-method=post \
http-header-field="Content-Type: application/x-www-form-urlencoded" \
http-data=("title=%E8%B7%AF%E7%94%B1%E5%99%A8%E9%80%9A%E7%9F%A5&body=%E5%A4%96%E7%BD%91%E6%81%A2%E5%A4%8D%0AIP%EF%BC%9A" . $pubip . "&group=%E5%AE%B6%E5%BA%AD%E7%BD%91%E7%BB%9C") \
keep-result=no

通知效果:

路由器通知

外网恢复
IP:1.2.3.4

其中:

group=%E5%AE%B6%E5%BA%AD%E7%BD%91%E7%BB%9C

对应 Bark 分组:

家庭网络

十四、RouterOS 公网 IP 变动通知

如果 auto-wan-ip 会自动更新,只需要做一个通知脚本,定时比较当前 IP 和上一次 IP。

建议新建 RouterOS 脚本:

check-wan-ip-change

脚本内容:

:local ids [/ip firewall address-list find list="auto-wan-ip"]

:if ([:len $ids] = 0) do={
    :log warning "auto-wan-ip is empty"
    :return
}

:local pubip [/ip firewall address-list get [:pick $ids 0] address]

:local envName "lastWanIP"
:local envId [/system script environment find name=$envName]

:if ([:len $envId] = 0) do={
    /system script environment add name=$envName value=$pubip
    :log info ("Init lastWanIP=" . $pubip)
    :return
}

:local oldip [/system script environment get $envId value]

:if ($pubip != $oldip) do={
    /system script environment set $envId value=$pubip

    /tool fetch url="https://你的Bark域名/你的BarkKey" \
    http-method=post \
    http-header-field="Content-Type: application/x-www-form-urlencoded" \
    http-data=("title=%E8%B7%AF%E7%94%B1%E5%99%A8%E9%80%9A%E7%9F%A5&body=IP%E5%B7%B2%E5%8F%98%E6%9B%B4%0A%E5%8E%9FIP%EF%BC%9A" . $oldip . "%0A%E6%96%B0IP%EF%BC%9A" . $pubip . "&group=%E5%AE%B6%E5%BA%AD%E7%BD%91%E7%BB%9C") \
    keep-result=no

    :log warning ("WAN IP changed: " . $oldip . " -> " . $pubip)
} else={
    :log info ("WAN IP no change: " . $pubip)
}

添加定时任务,每 5 分钟检测一次:

/system scheduler add name=check-wan-ip-change interval=5m on-event="/system script run check-wan-ip-change"

通知效果:

路由器通知

IP已变更
原IP:1.2.3.4
新IP:5.6.7.8

十五、最终效果

完成后,整体链路如下:

家里设备
每分钟访问 heartbeat.php
        ↓
VPS 记录 home.last
        ↓
VPS 每分钟执行 check_home.php
        ↓
超过 3 分钟未收到上报
        ↓
Bark 推送家庭网络可能离线
        ↓
恢复上报后
        ↓
Bark 推送家庭网络已恢复

同时 RouterOS 可以补充:

外网恢复 → 推送当前 IP
公网 IP 变化 → 推送原 IP / 新 IP

最终 Bark 通知统一归类到:

家庭网络

十六、常见问题

1. heartbeat.php 返回 Forbidden

说明没有带 Token,或者 Token 不正确。

正确地址:

https://你的域名/home-status/heartbeat.php?device=home&token=你的自定义Token

2. heartbeat.php 返回 OK,但没有生成文件

一般是 data 目录权限问题。

执行:

chown -R nobody:nogroup /data/ols/html/wordpress/home-status/data
chmod -R 755 /data/ols/html/wordpress/home-status/data

Docker 环境也可以用 UID/GID:

chown -R 65534:65534 /data/ols/html/wordpress/home-status/data

3. check_home.php 浏览器访问 Forbidden

这是正常的。

check_home.php 只允许命令行执行,不建议通过浏览器访问。

正确测试方式:

docker exec -it ols php /usr/local/lsws/Example/html/wordpress/home-status/check_home.php

4. 一直收到离线通知

检查家里设备的定时任务是否正常:

crontab -l

手动测试:

curl -fsS "https://你的域名/home-status/heartbeat.php?device=home&token=你的自定义Token"

5. 离线后没有重复通知

这是正常设计。

home.status 用来避免重复推送:

online -> offline   推送离线
offline -> offline  不推送
offline -> online   推送恢复
online -> online    不推送

十七、安全建议

建议把 Token 改复杂一点,例如不要使用:

abc123

可以改成类似:

HomeNet_2026_xxxxxx

同时不要公开 heartbeat.php 的完整地址,避免别人伪造上报导致断网提醒失效。

另外,分享文章时请替换以下敏感信息:

你的域名
你的Bark域名
你的BarkKey
你的自定义Token