Lite 版:upcyan/aircat-server-lite — 轻量级数据采集
Web 版:upcyan/aircat-server-web — 数据存储 + Web 可视化界面,支持 SQLite/DuckDB 双引擎切换
支持架构:
linux/amd64·linux/arm64
本项目基于 fenggenet/PhicommM1_Server 修改而来,在原项目基础上增加了 Docker 容器化部署、SQLite 存储、Web 可视化界面、设备亮度控制等功能。
基于 Docker 和 Python 的斐讯悟空(Phicomm AirCat)M1 设备数据采集服务器,提供两个版本:
| 版本 | 说明 | 适用场景 |
|---|---|---|
| Lite | 仅采集数据并输出日志 | 轻量部署、二次开发 |
| SQLite | 采集数据存入 SQLite + Web 界面展示 | 开箱即用、数据可视化 |
- 监听 TCP Socket 端口,接收 M1 设备上报的环境数据
- 解析设备数据:湿度、温度、PM2.5、甲醛(HCHO)
- Docker 容器化部署,一键启动
- 支持多客户端并发连接
- 自动断线重连机制
- 多架构镜像支持(x86 / ARM64)
- 日志级别和日志文件可通过环境变量控制
- M1 设备屏幕亮度控制(固定亮度 / 定时开关屏)
- 数据自动存入 SQLite 数据库,支持持久化
- 内置 Web 管理界面,实时查看各项数据
- 历史数据折线图(ECharts),支持点击图例隐藏/显示各项数据
- 时间范围切换:1小时 / 6小时 / 24小时 / 7天
- 实时数据自动刷新(5秒更新卡片,60秒更新图表)
- 数据量限制与自动清理(可配置最大记录数和保存天数)
- Web 设置面板(认证、数据管理、调试设置)
- 可选用户名密码认证(默认不启用)
- 支持 Docker 命令行重置用户名密码
- 语言: Python 3.14
- 框架: 原生 Socket + http.server(SQLite 版)
- 数据库: SQLite(SQLite 版)
- 前端: ECharts 5(SQLite 版)
- 容器: Docker / Docker Compose
- CI/CD: GitHub Actions 自动构建双镜像
aircat-svr-py/
├── aircat-server-lite.py # Lite 版主程序
├── aircat-server-web.py # Web 版主程序(Socket + Web + SQLite/DuckDB)
├── aircat-server-py/
│ └── templates/
│ └── web.html # Web 版界面(自包含,内联 CSS/JS)
├── docker-yaml/
│ ├── docker-lite/
│ │ └── docker-compose.yml # Lite 版 Docker Compose
│ └── docker-web/
│ │ └── docker-compose.yml # Web 版 Docker Compose
├── .github/
│ └── workflows/
│ └── docker-build.yml # GitHub Actions 自动构建双镜像
├── lite.Dockerfile # Lite 版 Docker 镜像
├── web.Dockerfile # Web 版 Docker 镜像
├── storage_backends.py # 存储引擎抽象层(SQLite/DuckDB)
├── VERSION # 版本号
└── README.md # 项目说明文档
# 拉取镜像
docker pull upcyan/aircat-server-lite:latest
# 运行容器
docker run -d \
--name aircat-server-lite \
-p 9000:9000 \
-e TZ=Asia/Shanghai \
-e LOG_LEVEL=DEBUG \
-e LOG_FILE=false \
--restart always \
upcyan/aircat-server-lite:latest
# 查看日志
docker logs -f aircat-server-liteservices:
aircat-server-lite:
image: upcyan/aircat-server-lite:latest
pull_policy: always
container_name: aircat-server-lite
ports:
- "9000:9000"
environment:
- TZ=Asia/Shanghai
- LOG_LEVEL=DEBUG
- LOG_FILE=false
restart: always
logging:
driver: "json-file"
options:
max-size: "10m"
max-file: "3"# 拉取镜像
docker pull upcyan/aircat-server-web:latest
# 运行容器(建议设置管理认证)
docker run -d \
--name aircat-server-web \
-p 9000:9000 \
-p 8080:8080 \
-e TZ=Asia/Shanghai \
-e LOG_LEVEL=DEBUG \
-e LOG_FILE=false \
-e DB_PATH=/data/aircat.db \
-e WEB_PORT=8080 \
-e AUTH_USER=admin \
-e AUTH_PASS=请替换为强密码 \
-v ./data:/data \
--restart always \
upcyan/aircat-server-web:latest
# 查看日志
docker logs -f aircat-server-web通过环境变量配置或恢复管理认证:
docker run -d \
--name aircat-server-web \
-p 9000:9000 \
-p 8080:8080 \
-e TZ=Asia/Shanghai \
-e AUTH_USER=admin \
-e AUTH_PASS=yourpassword \
-v ./data:/data \
--restart always \
upcyan/aircat-server-web:latest# 重置用户名
docker exec -it aircat-server-web resetname
# 重置密码
docker exec -it aircat-server-web resetpasswdservices:
aircat-server-web:
image: upcyan/aircat-server-web:latest
pull_policy: always
container_name: aircat-server-web
ports:
- "9000:9000"
- "8080:8080"
environment:
- TZ=Asia/Shanghai
- LOG_LEVEL=DEBUG
- LOG_FILE=false
- DB_PATH=/data/aircat.db
- WEB_PORT=8080
- AUTH_USER=admin
- AUTH_PASS=请替换为强密码
volumes:
- ./data:/data
restart: always
logging:
driver: "json-file"
options:
max-size: "10m"
max-file: "3"启动后访问 http://服务器IP:8080 即可查看 Web 界面。
点击页面右上角齿轮图标打开设置面板,支持:
- 认证设置:启用/关闭登录认证,设置用户名和密码
- 数据管理:设置最大记录数(超出自动覆盖)、保存天数(超期自动清理)、手动清理全部数据
- 调试设置:切换日志级别(DEBUG/INFO/WARNING/ERROR)、开启/关闭日志文件
- 设备控制:设置 M1 屏幕亮度(不控制/息屏/微亮/较暗/较亮/正常)、定时开关屏(白天/夜晚亮度和时间)
# Lite 版
docker build -t aircat-server-lite -f lite.Dockerfile .
# SQLite 版
docker build -t aircat-server-web -f web.Dockerfile .# Lite 版
python aircat-server-lite.py
# SQLite 版
python aircat-server-web.py| 配置项 | 默认值 | 说明 |
|---|---|---|
| 监听端口 | 9000 | TCP Socket 端口 |
| 采集间隔 | 5 秒 | 设备数据采集频率 |
| 接收缓冲区 | 4096 字节 | Socket 接收缓冲区大小 |
| 接收超时 | 10 秒 | 数据接收超时时间 |
| 最大重试次数 | 3 次 | 超时后最大重试次数 |
| 环境变量 | 默认值 | 可选值 | 说明 |
|---|---|---|---|
LOG_LEVEL |
DEBUG |
DEBUG / INFO / WARNING / ERROR |
控制台日志级别 |
LOG_FILE |
false |
true / false |
是否写入日志文件 |
MAX_FRAME_BYTES |
65536 |
正整数 | 单个 M1 响应帧最大字节数 |
MAX_FRAME_SECONDS |
10 |
正整数 | 单个 M1 响应帧总接收时限 |
MAX_DEVICE_CLIENTS |
32 |
正整数 | 最大并发设备连接数 |
| 环境变量 | 默认值 | 可选值 | 说明 |
|---|---|---|---|
M1_BRIGHTNESS |
-1 |
-1 / 0 / 25 / 50 / 75 / 100 |
屏幕亮度,-1=不控制,0=息屏,100=最亮 |
M1_TIMER_ENABLED |
false |
true / false |
启用定时开关屏 |
M1_TIMER_DAY_BRIGHTNESS |
100 |
0 / 25 / 50 / 75 / 100 |
白天屏幕亮度 |
M1_TIMER_NIGHT_BRIGHTNESS |
0 |
0 / 25 / 50 / 75 / 100 |
夜晚屏幕亮度 |
M1_TIMER_DAY_START |
07:00 |
HH:MM |
白天开始时间 |
M1_TIMER_NIGHT_START |
23:00 |
HH:MM |
夜晚开始时间 |
M1_BRIGHTNESS优先级高于定时设置。当M1_BRIGHTNESS >= 0时使用固定亮度,忽略定时设置。Lite 版通过环境变量配置,SQLite 版通过 Web 设置面板配置(也可通过环境变量初始化)。
| 环境变量 | 默认值 | 说明 |
|---|---|---|
DB_PATH |
/data/aircat.db |
数据库文件路径(sqlite 用 .db,duckdb 用 .duckdb) |
DB_ENGINE |
sqlite |
存储引擎:sqlite / duckdb,首次启动后可在 Web 设置里切换 |
WEB_PORT |
8080 |
Web 界面端口 |
AUTH_USER |
admin(仅设置密码时) |
设置管理用户名 |
AUTH_PASS |
(空) | 设置后启用认证;也可修复已有数据库的无认证状态 |
MAX_HTTP_BODY_BYTES |
16384 |
JSON 请求体最大字节数 |
MAX_HTTP_WORKERS |
16 |
Web 请求最大并发处理数 |
认证开关同时控制读取与管理写操作:关闭时无需登录即可保存设置、清理数据或切换存储,所有能访问服务的人都可以操作;开启时必须登录。仅在受信任的网络关闭认证。设置
AUTH_PASS并重启可为首次或已有数据库启用认证。
Web 版支持 SQLite 和 DuckDB 两种存储引擎,默认 SQLite:
- SQLite:轻量级嵌入式数据库,适合中小数据量(<50万条),零配置
- DuckDB:列存分析型数据库,聚合查询更快,适合大数据量和长时间跨度分析
在 Web 设置面板 → 存储引擎区域可一键切换,切换时会自动迁移全部本地数据(含设置、原始数据、聚合数据),旧库文件保留作为备份。切换后需重启容器使采集端使用新引擎。
- 调试排查:
LOG_LEVEL=DEBUG+LOG_FILE=false(默认),通过docker logs -f查看所有日志 - 生产环境:
LOG_LEVEL=INFO+LOG_FILE=false,仅输出重要信息 - 持久化日志:
LOG_LEVEL=DEBUG+LOG_FILE=true,挂载./logs:/logs目录保存日志文件 - 数据持久化(SQLite 版):挂载
./data:/data目录,数据库文件持久保存到宿主机
服务器接收 M1 设备上报的 JSON 数据,包含以下字段:
| 字段 | 类型 | 说明 |
|---|---|---|
| humidity | float | 湿度(%) |
| temperature | float | 温度(°C) |
| value | int | PM2.5 值(μg/m³) |
| hcho | float | 甲醛浓度(mg/m³) |
CREATE TABLE sensor_data (
id INTEGER PRIMARY KEY AUTOINCREMENT,
timestamp DATETIME DEFAULT CURRENT_TIMESTAMP,
humidity REAL, -- 湿度(%)
temperature REAL, -- 温度(°C)
pm25 INTEGER, -- PM2.5(μg/m³)
hcho REAL, -- 甲醛(mg/m³)
client_ip TEXT -- 设备 IP 地址
);| 接口 | 方法 | 认证 | 说明 |
|---|---|---|---|
/ |
GET | 可选 | Web 界面页面 |
/api/latest |
GET | 可选 | 获取最新一条数据记录 |
/api/history?hours=24 |
GET | 可选 | 获取指定小时数内的历史数据 |
/api/settings |
GET | 需要 | 获取当前设置 |
/api/settings |
POST | 启用认证时需要 | 更新设置(最大记录数、保存天数、认证、日志等) |
/api/cleanup |
POST | 启用认证时需要 | 清理全部传感器数据 |
/api/login |
POST | 不需要 | 登录认证,返回 token |
通过 Web 设置面板或 API 配置,所有设置持久化在 SQLite 数据库中:
| 设置项 | 默认值 | 说明 |
|---|---|---|
max_records |
10000 | 最大记录数,超出后自动删除最早记录 |
retention_days |
30 | 保存天数,超期记录自动清理 |
auth_enabled |
0 | 是否启用登录认证(0=关闭,1=开启) |
auth_user |
(空) | 登录用户名 |
log_level |
DEBUG |
日志级别(可通过 Web 面板动态修改) |
log_file |
0 | 是否写入日志文件(0=关闭,1=开启) |
数据清理由后台线程每 5 分钟自动执行一次,同时每次插入数据后也会检查。
| 仓库 | 说明 |
|---|---|
| upcyan/aircat-server-lite | 斐讯悟空 M1 轻量级数据采集服务器 |
| upcyan/aircat-server-web | 斐讯悟空 M1 数据采集服务器(SQLite/DuckDB + Web 界面) |
- 支持架构:amd64 / arm64
- 自动构建:每次推送到 main 分支自动构建两个镜像并递增版本号
- 绿联 NAS 更新:请启用 UGOS Docker 的“更新检测”,并使用
:latest标签跟随更新。Compose 的pull_policy: always仅影响部署时拉取镜像,不会开启 NAS 的后台更新检测。版本标签和sha-...标签用于固定版本,不会跟随latest更新。 - 镜像格式兼容:发布时显式使用 Docker Schema 2 清单 / manifest list 和 gzip 层,关闭 provenance、SBOM,并在发布后校验两个架构的实际清单。仅关闭 provenance 仍可能输出 OCI index,不能保证旧版 NAS 检测器兼容;多架构支持保持不变。
绿联官方说明确认:在“管理”启用检测后,项目列表提示镜像更新;执行更新时拉取镜像并重建容器。该文档没有公开检查周期、使用的仓库 API、缓存策略或摘要比较算法。不能断言 UGOS 根据 APP_VERSION、版本标签大小或某个 OCI label 判断更新,也不能把“其他容器能检测”当作本镜像构建失败的证据。
Docker 的多架构标签指向 manifest list,各架构还有独立的 manifest 和 config 摘要;这些摘要不能跨层直接比较。参见 Docker 摘要说明。本项目既往已验证 latest 实际变化且 NAS 能手动拉取新版本;将 OCI index 改为 Docker manifest list 后仍未获得用户端更新提示,因此格式转换并未证明解决问题。
发布流程新增以下可选兼容标签,用于排除多架构清单解析这一因素(需本次流程首次成功发布后才能使用):
| 标签 | 内容 |
|---|---|
latest |
保持 amd64 / arm64 多架构自动选择 |
latest-arm64 |
直接指向 ARM64 的单镜像 Docker Schema 2 清单 |
latest-amd64 |
直接指向 AMD64 的单镜像 Docker Schema 2 清单 |
<版本>-arm64 / <版本>-amd64 |
对应发布版本的单架构清单 |
这些标签复用同一构建产物,不重新编译,不改变数据挂载。通过 imagetools create --prefer-index=false复制子清单,并在发布后校验标签内容与源清单一致,避免单架构又被包成索引。这是诊断性兼容方案,不是已经实机证实的 UGOS 修复;现有 latest 用户不会自动切换到兼容标签。
在 NAS SSH 终端用 docker image inspect "$(docker inspect aircat-server-web --format '{{.Image}}')" --format '{{.Architecture}}' 确认当前镜像架构,再在绿联原项目中只改 image 一行,例如 ARM64 使用 image: upcyan/aircat-server-web:latest-arm64。保留原服务名、项目名、端口、数据挂载和其余设置;不要另建同名容器。
已知旧部署的项目名为 aircat-svr-lite、服务名为 aircat-server-sqlite,配置路径为 /volume2/DockerFiles/yamlfiles/aircat-server-lite/docker-compose.yaml。文件夹名不等于 Compose 项目名。优先在绿联原项目界面修改和部署,避免仅由命令行创建的项目与管理界面记录不一致;是否存在这种不同步仍须在 NAS 上核实。
首次切换标签并重新部署后只是建立新的比较基线,需下一次该标签更新才能验证自动提示。测试期间不要提前手动拉取新镜像,否则会改变本地比较状态。如果单架构标签仍无提示,应采集 UGOS / Docker 应用版本、同一项目的检测开关及检查请求返回结果,不能继续宣称镜像格式就是根因。分享日志时隐藏登录令牌和密码。
此修复需推送到 main 并等待 GitHub Actions 成功发布新镜像后才生效,本地修改不会改变 Docker Hub 上的镜像。
-
确认容器使用
upcyan/aircat-server-web:latest或upcyan/aircat-server-lite:latest,没有固定版本号或@sha256:...摘要。 -
在 UGOS 中手动拉取该镜像并重新创建容器(保留原来的端口、环境变量和数据目录挂载)。仅重启容器不会切换到新镜像。
-
若使用仓库提供的 Compose 文件,可在对应项目目录执行下面的命令;轻量版将服务名改为
aircat-server-lite。不要删除数据目录或卷。docker compose pull aircat-server-web docker compose up -d aircat-server-web
-
若拉取失败,先检查 NAS 到 Docker Hub 的连接、认证及限流;使用镜像代理时还需检查代理缓存是否同步。若可以拉取到新镜像却没有更新提示,请记录 UGOS / Docker 应用版本、完整镜像标签和检测日志,进一步确认检测器兼容性。
服务端监听 0.0.0.0:9000,WAN 断开不会停止本地监听;网络恢复后,终端重新建立 TCP 连接即可立即继续接受轮询。Web 版会关闭同一设备 IP 的旧半开连接,两个版本都启用了 keepalive、连接上限、完整帧上限和总接收时限。若终端仍显示 WiFi 叉号,请确认路由器的 DNS 劫持/静态解析在断网期间仍把原厂服务域名指向 NAS,并确保 NAS 使用固定局域网 IP;终端固件自身的重连周期无法由服务端强制改变。
| 端口 | 版本 | 说明 |
|---|---|---|
| 9000 | 通用 | TCP Socket 服务端口,接收 M1 设备连接 |
| 8080 | SQLite 版 | Web 界面端口,浏览器访问查看数据 |
MIT License