YANG JIANKUAN 3e0e6c73a1 refactor: 配置迁移至 .env,推送直连设备 MAC
- config.py 顶部新增零依赖 _load_dotenv(),凭证/参数统一从同目录 .env 读取,
  真实环境变量(cron/命令行注入)优先;移除所有明文 fallback
- 新增 .env.example 模板,.env 加入 .gitignore
- pusher.py 删除 get_devices(),resolve_device_id() 直接取 ZECTRIX_DEVICE_ID,
  不再调 GET /devices 列表
- jm_api.py 改为设备密钥授权(client_secret,免验证码),token 进程内缓存 + 401 重试
- 同步 CLAUDE.md / main.py 文档

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-29 15:25:36 +08:00
2026-06-29 09:43:20 +08:00
2026-06-29 09:43:20 +08:00
2026-06-29 09:43:20 +08:00
2026-06-29 09:43:20 +08:00
2026-06-29 09:43:20 +08:00

墨水屏仪表盘推送eink-push

将橘喵今日经营、近30天趋势、日期与天气合成为一张 400×300 纯黑白图片, 推送到 Zectrix 极趣云墨水屏设备(zectrix-s3-epaper-4.21bit

纯 Python + Pillow + Fusion Pixel 点阵字体,无浏览器依赖,适合后台/定时运行。

效果

  • 顶部:大号时钟 + 日期 天气(矢量手绘图标 + 温度 + 天气/温区)
  • 中部:橘喵今日实时经营(单量 / 流水 / 毛利,含环比、同比涨跌)
  • 底部近30天三指标趋势折线图同一图表实线 / 虚线 / 点线区分X 轴仅月/日)

目录结构

eink-push/
├── main.py          # 入口:取数据 → 渲染 → 推送
├── config.py        # 配置API Key、设备、字体、尺寸、推送参数、缓存路径
├── data.py          # 数据层:拉真实数据并映射,失败回退占位 "--"(不回退 mock含天气/趋势缓存
├── jm_api.py        # jm-devops 后端统计接口客户端(免登录 GET仅带 clientid 头)
├── weather_api.py   # 和风天气QWeather客户端实时天气 + 今日温区)
├── renderer.py      # 渲染层DashboardRenderer 生成 400×300 纯黑白图片
├── pusher.py        # 推送层:设备列表 / 推送图片(标准库 urllib
├── run.sh           # cron 包装脚本本机专用git 忽略)
├── fonts/           # Fusion Pixel 点阵字体OFL 协议)
├── output/          # 生成的图片与 cron.loggit 忽略)
├── requirements.txt
└── README.md

安装

pip install -r requirements.txt   # 仅 Pillow>=10.0;网络请求用标准库 urllib

使用

python3 main.py               # 渲染并推送(默认取设备列表第一个)
python3 main.py --render-only # 只渲染到 output/dashboard.png不推送本地调试渲染用

环境变量可覆盖配置(详见 config.py

# Zectrix 设备
ZECTRIX_API_KEY=zt_xxx ZECTRIX_DEVICE_ID=AA:BB:CC:DD:EE:FF python3 main.py
# jm-devops 后端 / 和风天气
JM_BASE_API=...  JM_CLIENT_ID=...
QWEATHER_HOST=...  QWEATHER_KEY=...  WEATHER_LOCATION=经度,纬度  WEATHER_TTL=900

定时刷新cron

设计为每分钟触发:业务数据每次都刷新,天气/趋势靠缓存避免高频打接口(见下「设计说明」)。 run.sh 是 cron 入口——cron 不继承登录环境,故脚本内 python 写死绝对路径、cd 到项目目录后追加日志到 output/cron.log

* * * * * /path/to/eink-push/run.sh

手动测试:sh run.sh

数据来源

  • 橘喵今日经营 + 近30天趋势:接入 jm-devops 后端统计接口(jm_api.py 免登录 GET仅带 clientid 头、无 token
    • GET /system/statistics/realtime —— 今日单量/流水/毛利 + 环比/同比
    • GET /system/statistics/recent-days?days=30 —— 近30天每日数据按日期降序、不含今天
  • 天气接入真实和风天气QWeatherweather_api.py),定位固定南京·秦淮。 实时天气取 now.text/now.temp/now.icon,今日温区取 /3ddaily[0]。 需配置 QWEATHER_HOST + QWEATHER_KEY(控制台-设置查看)。
  • 日期 / 时间 / 上次更新:真实系统时间。

data.py 负责把响应映射成渲染结构;任何接口失败都回退占位 "--",不回退 mock

缓存机制

为「每分钟触发、但天气/趋势无需高频刷新」而设的两套文件缓存cron 每次独立进程,必须落盘):

  • 天气缓存 .weather_cache.jsonTTL = WEATHER_TTL(默认 15 分钟),未过期不发请求。
  • 趋势缓存 .trend_cache.json:按日期为 key当天命中即不再调 recent-days,每天只拉一次。

两者拉取失败均沿用旧缓存避免趋势/天气区整片 "--",仍无缓存才回退占位。想强制重拉删掉对应缓存文件即可(均已 git 忽略)。

设计说明

  • 设备为 1bit 无真灰阶,灰阶只能抖动成网点(难看),故全程纯黑白,靠构图、字号、留白建立层级。
  • 字体用 Fusion Pixel 12px 点阵字体,按整数倍尺寸(12/24/48)渲染、关闭抗锯齿 → 像素锐利; 重点数据用「伪粗体」(偏移叠绘)加粗。
  • 天气图标为全矢量手绘(无图片资源):按和风 icon 代码归并为 8 类(晴/晴夜/多云/阴/雷/雨/雪/雾),用基本图元按 1bit 描边画出。
  • 趋势折线各指标按自身极值独立归一化,故只表达趋势形状,不可横向比绝对值。
  • 推送用 dither=false(硬阈值),纯黑白图最锐利。
Description
墨水屏内容推送
Readme 1.5 MiB
Languages
Python 100%