first commit

This commit is contained in:
douboer@gmail.com
2026-03-15 09:30:40 +08:00
commit 3d19c4d34f
145 changed files with 11623 additions and 0 deletions

View File

@@ -0,0 +1,449 @@
# Kindle Dashboard 分层时钟方案
## 1. 背景
当前仓库里有两部分:
- `calendar/`:负责渲染仪表盘网页与导出背景素材
- `dash/`:运行在 Kindle 上,负责拉图、刷屏、休眠与唤醒
最新设计稿来自 Figma
- 文件:`calendar`
- 节点:`6:2`
- 链接:`https://www.figma.com/design/3bXFNM5nM6mCq0TpL3nPYK/calendar?node-id=6-2&m=dev`
该节点 annotation 已明确:
- `阳历当天`
- `星期`
- `农历日`
- `时钟区域`
- `日历`
- `天气预报卡片`
- `书摘卡片`
本方案基于这些约束,采用“低频背景 + 本地时钟”的拆分方式:
- `calendar/``2 小时` 生成一次背景图
- 背景图直接写到:
- `/Users/gavin/kindle-dash/calendar/dist/kindlebg.png`
- Kindle 通过固定 HTTPS 地址拉取背景图:
- `https://shell.biboer.cn:20001/kindlebg.png`
- Kindle 端每分钟`不联网`
- Kindle 端只在本地重画时钟区域
## 2. 目标
这次改造不是单纯调样式,而是把页面拆成两类刷新节奏完全不同的素材:
1. 低频背景层
2. 高频时钟层
目标收益:
- 背景内容不再分钟级刷新
- 分钟级变化只限制在时钟区域
- 降低 Wi-Fi 唤醒次数
- 降低全屏刷新频率
- 减少墨水屏闪烁与残影
## 3. 分层边界
### 3.1 全屏背景层
背景层文件名固定为:
- `kindlebg.png`
背景层包含:
- 整体外层容器、圆角、阴影、背景渐变
- 日历卡片中的:
- 阳历当天数字
- 星期
- 农历日
- 下方月历区域
- 天气卡片全部内容
- 书摘卡片全部内容
背景层**不包含**
- 时钟表盘主体
- 时钟刻度
- 时针
- 分针
- 中心圆点
也就是说,背景图在时钟区域只保留布局占位,不直接承载任何分钟级内容。
### 3.2 静态表盘 patch
静态表盘 patch 为一个独立本地素材:
- `clock-face.png`
该素材包含:
- 圆形表盘主体
- 刻度
- 中心底座或中心圆盘
建议尺寸直接对齐 Figma 时钟区域:
- 节点:`24:74`
- 设计尺寸:`220 x 220`
这张图应当保存在 Kindle 本地,例如:
- `/mnt/us/dashboard/assets/clock-face.png`
它不需要分钟级联网拉取,只需要在部署时同步到设备,或者在更换设计稿时重新同步。
### 3.3 指针层
高频变化层只包含:
- 时针
- 分针
这里不建议包含秒针:
- 墨水屏收益低
- 刷新频率会显著上升
- 更容易产生残影
由于当前 `dash/` 链路本质是 `eips` 刷图,不是 SVG/Canvas 实时渲染,所以推荐使用`分层素材方案`
- `minute-hand/00.png``minute-hand/59.png`
- `hour-hand/000.png``hour-hand/719.png`
说明:
- 分针每分钟一个角度,共 `60`
- 时针如果要做到真正随分钟连续移动,需要 `12 * 60 = 720`
- 这些都是小尺寸 patch不是整屏图体积可控
如果后续确认 Kindle 端存在稳定的本地绘线工具,再考虑把指针改成算法绘制;当前版本不依赖这个前提。
## 4. 为什么不能只把表盘放进整页背景
这个问题必须单独说明。
如果:
- 表盘主体和刻度只存在于 `kindlebg.png`
- Kindle 每分钟只覆盖新的时针/分针
那么上一分钟留下的旧指针就无法被干净擦除。
因此 Kindle 端每分钟的正确流程应该是:
1. 先重画一张本地 `clock-face.png`
2. 再叠加新的时针素材
3. 再叠加新的分针素材
这等价于“先擦除,再重画”,并且整个流程不依赖网络。
所以本方案不是“背景图 + 两根指针”两层,而是:
1. `kindlebg.png`:全屏低频背景
2. `clock-face.png`:本地静态表盘 patch
3. `hour-hand/*.png + minute-hand/*.png`:本地高频指针素材
## 5. 数据刷新策略
### 5.1 背景层刷新
背景层刷新触发条件:
-`2 小时` 一次
- 跨天时立即刷新一次
- 天气接口异常恢复后可补刷一次
推荐调度:
- `00:00 / 02:00 / 04:00 / ... / 22:00`
背景层刷新输出:
- `/Users/gavin/kindle-dash/calendar/dist/kindlebg.png`
背景层对 Kindle 的访问地址固定为:
- `https://shell.biboer.cn:20001/kindlebg.png`
### 5.2 静态表盘 patch 刷新
静态表盘 patch 不参加分钟级调度。
建议刷新方式:
- 随部署同步一次
- Figma 设计或尺寸变化时重新导出并同步
### 5.3 指针层刷新
指针层刷新触发条件:
- 每分钟一次
指针层数据只依赖 Kindle 本地时间:
- 小时
- 分钟
分钟刷新不需要联网。
## 6. 网页渲染模式设计
为了让 `calendar/` 稳定产出背景与表盘素材,建议支持下面 3 种模式。
### 6.1 `full`
用途:
- 本地开发预览
- 对照 Figma 联调
输出内容:
- 背景层
- 表盘
- 指针预览
### 6.2 `background`
用途:
- 生成 `kindlebg.png`
输出内容:
- 只渲染背景层
- 时钟区域保留占位,但不绘制表盘与指针
### 6.3 `clock-face`
用途:
- 生成静态表盘 patch
输出内容:
- 只渲染表盘主体、刻度和中心底座
- 不绘制时针、分针
### 6.4 URL 约定
建议页面支持如下参数:
```text
/?mode=full
/?mode=background
/?mode=clock-face
```
## 7. 产物约定
推荐最终产出这几类文件:
```text
calendar/dist/kindlebg.png
calendar/dist/dashboard-manifest.json
kindle local:
/mnt/us/dashboard/assets/clock-face.png
/mnt/us/dashboard/assets/hour-hand/000.png ... 719.png
/mnt/us/dashboard/assets/minute-hand/00.png ... 59.png
```
`manifest` 建议至少包含这些字段:
```json
{
"background": {
"path": "kindlebg.png",
"url": "https://shell.biboer.cn:20001/kindlebg.png",
"updatedAt": "2026-03-15T10:00:00+08:00",
"refreshIntervalMinutes": 120
},
"clockRegion": {
"x": 313,
"y": 0,
"width": 220,
"height": 220
},
"clockFace": {
"path": "clock-face.png",
"managedOnKindle": true
},
"clockHands": {
"hourPattern": "assets/hour-hand/%03d.png",
"minutePattern": "assets/minute-hand/%02d.png",
"refreshIntervalMinutes": 1,
"networkRequired": false
}
}
```
说明:
- `x/y/width/height` 先按设计稿记录
- 真正接入 Kindle 时,要换算成最终截图分辨率下的实际像素值
## 8. Kindle 侧刷新策略
### 8.1 已确认的前提
`eips` 支持把 PNG/JPG 绘制到指定坐标,参数包含:
- `-g`
- `-x`
- `-y`
- `-f`
MobileRead Wiki 明确写了:
- `eips -g|-b image_path [-w waveform -f -x xpos -y ypos -v]`
- `-x``-y` 以像素为单位
来源:<https://wiki.mobileread.com/wiki/Eips>
因此,对 Kindle Voyage 而言,“在固定时钟区域重画小图”这个前提是成立的。
### 8.2 推荐流程
#### 启动或背景刷新时
1. 通过 HTTPS 拉取:
- `https://shell.biboer.cn:20001/kindlebg.png`
2. 保存为本地背景缓存
3. 使用全屏刷新显示背景图
4. 在时钟区域重画一次:
- `clock-face.png`
- 当前时针
- 当前分针
#### 每分钟刷新时
1. 读取 Kindle 本机时间
2. 计算:
- `minute_index = 00..59`
- `hour_index = ((hour % 12) * 60 + minute) = 000..719`
3. 在固定坐标先画:
- `clock-face.png`
4. 再画:
- `hour-hand/<hour_index>.png`
5. 再画:
- `minute-hand/<minute_index>.png`
6. 默认做局部/普通刷新
7.`10``15` 分钟对时钟区域补一次全刷,清理残影
### 8.3 功耗模型
这套方式的功耗来源拆成两类:
- 背景刷新:
- 每 2 小时联网一次
- 全屏刷新一次
- 时钟刷新:
- 每分钟本地刷一次小区域
- 不联网
相比“每分钟拉一张整屏背景图”,这会明显省电。
## 9. 代码改造建议
### 9.1 `calendar/`
建议改造点:
- 新增 `mode=background`
- 新增 `mode=clock-face`
- 时钟区域从日历/天气/书摘中完全拆开
- 增加一个定时生成任务,每 2 小时把背景图写到:
- `/Users/gavin/kindle-dash/calendar/dist/kindlebg.png`
- 生成 `dashboard-manifest.json`
### 9.2 `dash/`
建议改造点:
- `dash/src/local/fetch-dashboard.sh`
- 改成只拉取 `kindlebg.png`
- `dash/src/dash.sh`
- 从“单一刷新循环”改为“背景刷新 + 本地时钟刷新”双节奏
- 新增例如:
- `dash/src/local/render-clock.sh`
- `dash/src/local/render-clock-face.sh`
- `dash/src/local/clock-index.sh`
推荐新增配置项:
```sh
export BACKGROUND_URL="https://shell.biboer.cn:20001/kindlebg.png"
export BACKGROUND_REFRESH_SCHEDULE="0 */2 * * *"
export CLOCK_REGION_X=313
export CLOCK_REGION_Y=0
export CLOCK_REGION_WIDTH=220
export CLOCK_REGION_HEIGHT=220
export CLOCK_FULL_REFRESH_INTERVAL_MINUTES=15
```
## 10. 实施顺序
建议按下面顺序落地,避免一次改太多导致链路难排查。
### 阶段 1`calendar/` 分层输出
目标:
- `full/background/clock-face` 三种模式跑通
- 每 2 小时把背景图写到 `calendar/dist/kindlebg.png`
输出:
- 浏览器可预览
- `kindlebg.png` 可被 nginx 直接访问
### 阶段 2时钟静态素材准备
目标:
- 产出 `clock-face.png`
- 产出 `hour-hand``minute-hand` 素材库
输出:
- Kindle 本地时钟素材目录结构确定
### 阶段 3Kindle 本地分钟时钟
目标:
- Kindle 每分钟本地重画时钟
- 不联网
输出:
- 背景低频更新 + 时钟本地高频更新的完整链路
## 11. 推荐结论
当前最稳妥的推进顺序是:
1. 先让 `calendar/` 每 2 小时稳定生成:
- `/Users/gavin/kindle-dash/calendar/dist/kindlebg.png`
2. 再让 Kindle 只从:
- `https://shell.biboer.cn:20001/kindlebg.png`
拉背景图
3. 时钟区域完全本地化:
- 本地 `clock-face.png`
- 本地 `hour-hand/*.png`
- 本地 `minute-hand/*.png`
4. 分钟刷新时:
- 先重画表盘
- 再重画时针
- 再重画分针
也就是说,**背景是远端低频资源,时钟是本地高频资源,二者不要混在同一个刷新链路里**。