一个可以查询Uclean洗衣机状态的系统
  • Python 36%
  • JavaScript 29.3%
  • CSS 17.8%
  • HTML 16.6%
  • Dockerfile 0.3%
查找文件
moeouo a4db3dcf98 更新README.md
Signed-off-by: moeouo <cheng@moe-mail.com>
2026-05-23 01:06:01 +08:00
.forgejo/workflows Release 1.0.0 2026-05-23 00:48:33 +08:00
resources Release 1.0.0 2026-05-23 00:48:33 +08:00
tests Release 1.0.0 2026-05-23 01:02:42 +08:00
web Release 1.0.0 2026-05-23 01:02:42 +08:00
.dockerignore Release 1.0.0 2026-05-23 00:48:33 +08:00
.env.example Release 1.0.0 2026-05-23 00:48:33 +08:00
.gitignore Release 1.0.0 2026-05-23 00:48:33 +08:00
docker-compose.yml Release 1.0.0 2026-05-23 00:48:33 +08:00
Dockerfile Release 1.0.0 2026-05-23 00:48:33 +08:00
main.py Release 1.0.0 2026-05-23 00:48:33 +08:00
pyproject.toml Release 1.0.0 2026-05-23 00:48:33 +08:00
README.md 更新README.md 2026-05-23 01:06:01 +08:00
requirements.txt Release 1.0.0 2026-05-23 00:48:33 +08:00
util.py Release 1.0.0 2026-05-23 00:48:33 +08:00
uv.lock Release 1.0.0 2026-05-23 00:48:33 +08:00

UClean

UClean 是一个面向宿舍、楼层或小型公共空间的 U 净洗衣机状态看板。它保留最有用的能力:按房间查看洗衣机是否空闲、自动刷新状态、在空闲时提醒,并提供一个受保护的后台用于维护登录 token。

当前版本:1.0.0

功能

  • 房间首页:输入房间 Code 或从已配置房间进入状态页。
  • 状态页:展示空闲、运行中、异常、更新时间、数据源和下次刷新时间。
  • 自动刷新:前端轮询状态接口,后端按房间限频,避免频繁请求 U 净 API。
  • 空闲提醒:用户主动开启跟踪后,检测到洗衣机空闲会播放提示音并弹出通知。
  • 后台维护:管理员可通过手机号验证码更新 token,也可手动写入 token。
  • 安全基础:后台口令、CSRF 校验、敏感操作二次解锁、接口限流、审计日志、基础安全响应头。
  • PWA:支持添加到主屏幕,静态资源离线缓存。

技术栈

  • Python 3.12
  • Flask 3
  • Redis
  • 原生 Jinja 模板、CSS、JavaScript
  • Docker / Docker Compose

目录

.
├── main.py                  # Flask 应用与路由
├── util.py                  # 配置读写与 HTTP 请求封装
├── config.json              # 本地房间与 token 配置
├── web/templates/           # 页面模板
├── web/static/              # CSS、JS、PWA 资源
├── tests/                   # pytest 测试
├── Dockerfile
├── docker-compose.yml
├── pyproject.toml
└── requirements.txt

快速开始

1. 安装依赖

推荐使用 uv:

uv sync --group test

也可以使用 pip:

pip install -r requirements.txt
pip install pytest

2. 配置房间

创建或编辑 config.json

{
  "token": "",
  "groups": {
    "703": {
      "title": "703",
      "washer": {
        "name": "703 洗衣机",
        "qrCode": "https://q.ujing.com.cn/ucqrc/index.html?cd=..."
      }
    }
  }
}

说明:

  • groups 的 key 是访问路径里的房间 Code,例如 703 对应 /703
  • title 是页面展示名称。
  • washer.name 是洗衣机展示名称。
  • washer.qrCode 填 U 净二维码内容字符串,不需要解析二维码图片。
  • 初次部署时 token 可以留空,之后在 /admin 里登录并自动写入。

3. 启动 Redis

本机 Redis:

redis-server

Docker:

docker run --name uclean-redis -p 6379:6379 -d redis:7

4. 启动服务

uv run python main.py

或:

python main.py

默认地址:

  • 首页:http://127.0.0.1:5000/
  • 房间页:http://127.0.0.1:5000/703
  • 后台:http://127.0.0.1:5000/admin
  • 健康检查:http://127.0.0.1:5000/healthz

后台设置

后台默认关闭。设置管理员口令后,/admin 才会开放。

开发环境可以直接设置明文口令:

export ADMIN_PASSWORD="换成一个足够长的口令"
export SECRET_KEY="换成一个随机长字符串"

生产环境推荐使用哈希口令:

python -c "from werkzeug.security import generate_password_hash; print(generate_password_hash('你的管理员口令'))"
export ADMIN_PASSWORD_HASH="上一步输出的完整哈希"
unset ADMIN_PASSWORD

后台支持三类操作:

  • 检查当前 token 是否可请求 U 净 API。
  • 通过手机号验证码登录并更新 token。
  • 手动写入已获取到的 token。

涉及短信发送、登录和 token 写入的操作需要先在后台二次解锁。

环境变量

变量 默认值 说明
REDIS_URL redis://127.0.0.1:6379/0 Redis 连接地址
MIN_FETCH_INTERVAL_SECONDS 60 每个房间请求 U 净 API 的最小间隔
DATA_TTL_SECONDS 3600 状态缓存保留时间
ADMIN_PASSWORD 管理员明文口令,开发环境可用
ADMIN_PASSWORD_HASH 管理员哈希口令,优先级高于 ADMIN_PASSWORD
SECRET_KEY 随机生成 Flask 会话签名密钥,生产环境必须固定设置
SECURE_COOKIES 0 HTTPS 部署时建议设为 1
SESSION_MAX_AGE_SECONDS 21600 管理员会话最长有效期
ADMIN_REAUTH_SECONDS 300 敏感操作解锁有效期
ADMIN_RATE_LIMIT_WINDOW_SECONDS 300 后台限流窗口
ADMIN_PASSWORD_LIMIT 10 管理员口令尝试次数上限
ADMIN_SET_TOKEN_LIMIT 20 手动写入 token 次数上限
ADMIN_CHECK_TOKEN_LIMIT 30 检查 token 次数上限
ADMIN_SEND_CAPTCHA_LIMIT 3 单手机号发送验证码次数上限
ADMIN_LOGIN_LIMIT 5 单手机号登录次数上限
ADMIN_IP_SEND_CAPTCHA_LIMIT 5 单 IP 发送验证码次数上限
ADMIN_IP_LOGIN_LIMIT 10 单 IP 登录次数上限
ADMIN_LOCKOUT_MAX_SECONDS 900 管理员口令错误后的最大锁定秒数
ADMIN_LOCKOUT_RESET_SECONDS 3600 管理员失败计数重置时间
TRUST_PROXY 0 反向代理后方部署时,设为 1 才信任 X-Forwarded-For
INSECURE_SKIP_TLS_VERIFY 0 仅排障使用,设为 1 会跳过请求 U 净 API 时的 TLS 校验

Docker Compose 部署

准备环境变量:

export ADMIN_PASSWORD_HASH="你的管理员口令哈希"
export SECRET_KEY="随机长字符串"

启动:

docker compose up -d --build

查看:

docker compose ps

停止:

docker compose down

API

GET /api/meta

返回系统账号连接状态。

GET /api/<code>/washers

读取指定房间的洗衣机状态。接口优先使用 Redis 缓存,并按房间限制刷新频率。

常见返回码:

  • 0:成功。
  • 2:尚未配置 token。
  • 3:token 已失效。
  • 4:房间配置有误。
  • 5:U 净接口异常。
  • 7:Redis 不可用。

GET /api/<code>/washers?force=1

尝试强制刷新。即使传入 force=1,仍会受后端限频保护。

GET /healthz

健康检查。Redis 可用时返回 200,不可用时返回 500

测试

uv run pytest

或:

pytest

PWA

  • iOS Safari:打开站点后,通过分享菜单添加到主屏幕。
  • Android Chrome:打开站点后,通过浏览器菜单安装应用或添加到主屏幕。

静态资源由 web/static/sw.js 缓存。更新前端资源后需要提升 CACHE_NAME,否则老用户可能继续看到旧样式。