OpenHarmony 北向监控系统的云端服务端,负责接收设备传感器数据(支持 WebSocket 和 TCP 双协议)、存储历史记录、推送报警通知、为 Web 前端提供实时数据与远程控制 API。
Tip
本项目是智能家居安防系统 (https://github.com/LiHuaJZOAQ/GuardSystem) 的一个组成部分
下面是安防系统的其他组成项目
-
设备端Openharmony南向项目(https://github.com/LiHuaJZOAQ/GuardSys)
-
设备端Openharmony北向项目(https://github.com/LiHuaJZOAQ/GuardSysAPP)
GuardSysServer/
├── server/ # 后端 (Express + Socket.io + SQLite + ws)
│ ├── index.js # 服务端入口(单文件)
│ ├── test.js # 集成测试
│ ├── railway.json # Railway 部署配置
│ └── package.json
├── web/ # 前端 (Vue 3 + Vite)
│ ├── src/
│ │ ├── App.vue # 根组件 (<router-view>)
│ │ ├── main.js # 入口 + 路由
│ │ ├── components/
│ │ │ ├── Login.vue # 登录/注册页
│ │ │ └── Dashboard.vue # 仪表盘(实时数据 + 控制面板 + 图表)
│ │ └── views/
│ │ └── ActivityLog.vue # 活动日志页
│ ├── index.html
│ ├── vite.config.js # Vite 配置(含开发代理)
│ └── package.json
├── docs/ # 文档
│ ├── appintegration.md # 鸿蒙 APP 对接说明
│ ├── test.md # 测试说明
│ └── spec/ # 设计文档
├── .github/workflows/
│ └── test.yml # CI: 服务端集成测试
└── README.md
设备通过 WebSocket(推荐)或 TCP 连接服务器,JSON 消息以 WebSocket 帧(或 TCP \n 换行符)传输。
| 协议 | 路径/端口 | 说明 |
|---|---|---|
| WebSocket | wss://host/device(443)或 ws://host:port/device |
推荐,共享 HTTP 端口,无需额外暴露端口 |
| TCP | 端口 3001 | 兼容旧设备,功能相同 |
设备连接后应先发送 register 消息,将 MAC/序列号注册为永久设备 ID:
{"type":"register","mac":"1234567890"}- 注册后该 MAC 作为
device_id持久化到数据库 - 若未发送 register,则回退使用
IP:端口作为临时 ID
{"type":"report","temp":"25.3","humi":"60.1","smoke":12.34,"ir":true,"alarm":0,"rssi":-34}| 字段 | 类型 | 说明 |
|---|---|---|
type |
string | 固定 "report" |
temp |
string | 温度(如 "25.3") |
humi |
string | 湿度(如 "60.1") |
smoke |
number | 烟雾浓度(ppm) |
ir |
bool | 红外有人 true/false |
alarm |
number | 报警模式:0=正常,1=警告,2=报警 |
rssi |
number | WiFi 信号强度(可选) |
服务器应答:{"success":true}
{"type":"arm","value":1}value=1布防:红外有人自动触发设备端报警value=0撤防:红外仅记录日志
服务器存储布防状态并推送给 Web 前端(Socket.io 事件 device:arm)。
通过 REST API POST /api/devices/:id/control 下发:
{"type":"cmd","action":"setAlarm","value":1}| action | value | 说明 |
|---|---|---|
setAlarm |
0/1/2 | 设置报警模式 |
collect |
(无) | 触发设备立即采集一次传感器数据 |
arm |
0/1 | 远程布防/撤防 |
设备每 30s 发送心跳保持连接:
{"type":"ping"}服务器回复 {"type":"pong"} 并更新 last_seen。设备超过 5 分钟未上报数据则自动标记离线。
传感器数据入库时自动计算 alarm_reason,跟随报警等级输出对应原因。
| 报警等级 | 条件 | alarm_reason |
|---|---|---|
| 报警 (2) | smoke >= 200 |
烟雾过高 |
ir==true && (smoke>=100 || temp>=40) |
人员未撤离 | |
humi >= 80 |
湿度过高 | |
humi <= 20 |
湿度过低 | |
| 以上均不匹配 | 入侵报警 | |
| 警告 (1) | smoke >= 100 |
烟雾偏高 |
temp >= 40 |
温度过高 | |
humi >= 80 |
湿度过高 | |
humi <= 20 |
湿度过低 | |
| 正常 (0) | smoke >= 50 || temp >= 35 |
数值偏高 |
humi >= 70 || humi <= 30 |
湿度异常 | |
ir == true |
有人活动 |
当满足以下任一条件时,通过 ServerChan 推送微信通知(一天一台设备仅推一次):
smoke >= 200temp >= 40humi >= 80或humi <= 20alarm >= 2
需要配置环境变量 SCKEY。
SQLite 数据库 guardsys.db,包含以下表:
| 表名 | 说明 |
|---|---|
users |
用户账号(默认 admin / admin123) |
devices |
设备信息(含在线状态、布防状态) |
sensor_logs |
传感器历史记录(保留 7 天) |
push_logs |
推送日志(防重复) |
所有 REST API 统一前缀 /api,认证接口除外均需 Authorization: Bearer <token> 请求头。认证失败返回 401 Unauthorized 或 403 Forbidden。
| 方法 | 路径 | 认证 | 说明 |
|---|---|---|---|
| POST | /api/auth/register |
— | 注册账号 |
| POST | /api/auth/login |
— | 登录获取 JWT |
| GET | /api/auth/verify |
Bearer | 验证 token |
| GET | /api/devices |
Bearer | 设备列表 |
| GET | /api/devices/:id/latest |
Bearer | 设备最新数据 |
| GET | /api/devices/:id/history?hours=24 |
Bearer | 设备历史数据 |
| POST | /api/devices/:id/control |
Bearer | 设备控制指令 |
| GET | /api/logs?limit=50&deviceId= |
Bearer | 传感器日志 |
注册新用户。
// Request
{"username":"admin","password":"240be518..."}
// Response 200
{"success":true}
// Response 400
{"error":"用户名已存在"}登录获取 JWT token(有效期 24 小时)。
// Request
{"username":"admin","password":"240be518..."}
// Response 200
{"token":"eyJhbGciOiJIUzI1NiIs...","username":"admin"}
// Response 401
{"error":"用户名或密码错误"}验证当前 token 是否有效。
// Response 200
{"username":"admin"}
// Response 401
{"error":"未登录"}获取所有设备列表,按最后在线时间倒序。
// Response 200
[
{
"id": "1234567890",
"name": "Device-1234567890",
"connected_at": "2026-06-14T08:00:00.000Z",
"last_seen": "2026-06-14T08:05:00.000Z",
"online": 1,
"armed": 1
}
]
id为设备注册时发送的 MAC/序列号(示例1234567890),非 IP:端口。未注册设备仍使用 IP:端口。
获取指定设备最新一条传感器数据。
// Response 200
{
"id": 128,
"device_id": "1234567890",
"temp": 25.3,
"humi": 60.1,
"smoke": 45,
"ir": 0,
"alarm": 0,
"rssi": -65,
"alarm_reason": null,
"created_at": "2026-06-14T08:05:00.000Z"
}查询设备历史数据,默认最近 24 小时,最多 1000 条。支持 hours 参数调整时间范围。
// Response 200
[{"id":128,"temp":25.3,...},{"id":127,"temp":25.1,...}]向设备发送控制指令。设备在线时通过 WebSocket/TCP 转发,同时通过 Socket.io 广播 device:command 事件。
// Request
{"action":"setAlarm","value":1}
// Response 200
{"success":true}
// Response 400
{"error":"Device offline"}| action | value | 说明 |
|---|---|---|
setAlarm |
0 / 1 / 2 | 设置报警模式:正常 / 警告 / 报警 |
collect |
不传 | 触发设备立即采集一次传感器数据 |
arm |
0 / 1 | 远程撤防 / 布防 |
查询传感器日志记录,支持分页和设备过滤。
| 参数 | 默认 | 说明 |
|---|---|---|
limit |
50 | 返回条数上限 |
deviceId |
全部 | 按设备 ID 过滤 |
// Response 200
[{"id":128,"device_id":"...","temp":25.3,"alarm":0,"alarm_reason":null,...}]Web 前端通过 Socket.io 连接(需传入 auth: { token })接收实时推送:
// 前端连接示例
const socket = io({ auth: { token: 'eyJhbGciOiJ...' } })| 事件 | 方向 | payload | 说明 |
|---|---|---|---|
sensor:data |
服务 → 前端 | {deviceId, temp, humi, smoke, ir, alarm, rssi, alarmReason, timestamp} |
传感器实时数据 |
device:online |
服务 → 前端 | {deviceId, online: bool} |
设备上线/离线 |
device:command |
服务 → 前端 | {deviceId, action, value} |
控制指令已下发 |
device:arm |
服务 → 前端 | {deviceId, armed: bool} |
布防/撤防状态变化 |
南向设备(开发板) --> WebSocket /device --> Railway服务器 (端口 3000)
|
v
Socket.io
|
v
Web 前端 (Vue 3)
Railway 统一暴露端口 3000(
$PORT),WebSocket 设备服务器和 HTTP 前端共享同一端口。不再需要额外暴露 TCP 3001 端口。
- 注册 Railway 账号: https://railway.app (使用 GitHub 登录)
- 创建新项目: "New Project" -> "Deploy from GitHub repo"
- 选择 GuardSysServer 仓库
- 在 Railway 控制台添加环境变量:
SCKEY: Server酱的 SCKEY(可选,用于报警推送)PORT: 3000TCP_PORT: 3001
- 部署完成后,获取 Railway 分配的域名
Railway 部署时会自动安装依赖并构建前端(
postinstall触发web/的npm install && npm run build),访问 Railway 域名即可同时使用 API 和前端页面,无需单独部署前端。
- 注册 Vercel 账号: https://vercel.com (使用 GitHub 登录)
- 导入 GitHub 仓库
- 配置:
- Root Directory:
web - Build Command:
npm run build - Output Directory:
dist
- Root Directory:
- 部署完成后获取前端域名
修改 GuardSysAPP 中的服务器地址为 Railway 域名:
在 SettingsPage.ets 中:
@State serverHost: string = 'guardsysserver.up.railway.app'; // Railway 域名
@State serverPort: string = '443'; // 443=wss://, 其他=ws://设备通过 WebSocket 连接 wss://域名/device,不再使用 TCP 3001 端口。
- 访问 https://sct.ftqq.com/
- 注册账号并获取 SCKEY
- 在 Railway 后台添加环境变量
SCKEY
cd server
npm install
npm run devcd web
npm install
npm run dev- 实时监控: 页面自动显示温湿度、烟雾、红外状态,支持 ms 级轮询间隔设置
- 设备控制: 选择设备后可设置报警模式、远程布防/撤防
- 报警推送: 当烟雾>=200 或 温度>=40 时自动推送微信通知
- 历史数据: 显示 24 小时历史曲线
- 设备无法连接: 确认服务器域名正确,Railway 运行正常
- 前端无法连接: 确认已登录且 token 未过期
- 数据不更新: 检查设备端是否开机运行,WiFi 是否连接成功;Web 端已自动每 10s 刷新设备列表,每
刷新频率(ms)轮询最新数据 - 推送失败: 检查 SCKEY 是否正确配置