# 墨水屏仪表盘推送（eink-push）

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

纯 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.log（git 忽略）
├── requirements.txt
└── README.md
```

## 安装

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

## 使用

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

环境变量可覆盖配置（详见 `config.py`）：

```bash
# 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`。

```cron
* * * * * /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天每日数据（按日期降序、不含今天）
- **天气**：接入真实和风天气（QWeather，`weather_api.py`），定位固定南京·秦淮。
  实时天气取 `now.text`/`now.temp`/`now.icon`，今日温区取 `/3d` 的 `daily[0]`。
  需配置 `QWEATHER_HOST` + `QWEATHER_KEY`（控制台-设置查看）。
- **日期 / 时间 / 上次更新**：真实系统时间。

> `data.py` 负责把响应映射成渲染结构；**任何接口失败都回退占位 `"--"`，不回退 mock**。

## 缓存机制

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

- **天气缓存** `.weather_cache.json`：TTL = `WEATHER_TTL`（默认 15 分钟），未过期不发请求。
- **趋势缓存** `.trend_cache.json`：按日期为 key，当天命中即不再调 `recent-days`，每天只拉一次。

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

## 设计说明

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