feat: E1002 新增公网图片服务(恒定 URL 现渲染)+底部改为 BTC 时K

- server.py:三条恒定 URL 并行——/e1002.png 内存现渲染 PNG(主力)、
  /e1002.html 现代网页、/e1002-web.png 网页经无头 Chrome 截图后固化六色;
  天气/橘喵/BTC 按 WEATHER_TTL/MAO_TTL/BTC_TTL 走内存缓存,过期才实拉、
  失败沿用旧值;用量不读本机文件,改由 POST /push/usage(_gpt) 推送暂存
- push_client.py:本机把 Claude 用量快照推送到服务(X-Push-Token 鉴权)
- btc_api.py:OKX/Coinbase/Huobi 免费接口逐源兜底,时K 近 72 小时,涨绿跌红
- data.py 拆出 weather_fetch/mao_fetch/btc_fetch 与 usage_from_raw,
  统一按 TZ_NAME 时区;宽版只用点阵字体整数倍字号 12/24/36/48
- probe_server.py:验证平台对同一 URL 是否周期重抓(结论:Image 控件
  不重抓,HTML 控件重抓)

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
2026-09-10 18:55:47 +08:00
parent 6dc89b504e
commit 85b5113ba4
14 changed files with 1192 additions and 114 deletions

View File

@@ -32,6 +32,10 @@ _load_dotenv(os.path.join(BASE_DIR, ".env"))
# 不再走 Zectrix 公网 API + 设备 MAC改为直连局域网设备 mDNS host 的 HTTP 接口。
EPD_HOST = os.environ.get("EPD_HOST", "epd400a44.local")
# ---- 时区 ----
# 服务端可能部署在境外 VPS所有「现在时刻」与时间戳格式化统一按此时区data.TZ本机与服务端结果一致。
TZ_NAME = os.environ.get("TZ_NAME", "Asia/Shanghai")
# ---- 画布(喵喵固件 400x300 三色屏)----
WIDTH, HEIGHT = 400, 300
@@ -40,6 +44,26 @@ WIDTH, HEIGHT = 400, 300
E1002_WIDTH, E1002_HEIGHT = 800, 480
E1002_COLOR_STRIP = os.environ.get("E1002_COLOR_STRIP", "0") == "1" # 底部六色测试色条(真机校准用,默认关)
# ---- 图片服务server.py恒定 URL每次请求内存中现渲染 800x480 PNG供 SenseCraft HTML 控件周期抓取)----
# 服务端三路上游的内存缓存有效期(秒):收到请求先查缓存是否过期,过期才实拉并更新时间戳,否则直接用缓存渲染;
# 拉取失败沿用旧值且不更新时间戳(下次请求再试)。设 0 = 每次请求都实拉。天气/BTC 复用上面的 WEATHER_TTL/BTC_TTL。
MAO_TTL = int(os.environ.get("MAO_TTL", "60")) # 橘喵今日实时,默认 60 秒
# 真机验证结论Image 控件只在填写 URL 时抓一次快照、此后不再更新HTML 控件填同一图片 URL 则每周期重抓。
SERVER_HOST = os.environ.get("SERVER_HOST", "0.0.0.0")
SERVER_PORT = int(os.environ.get("SERVER_PORT", "8790"))
IMAGE_PATH = os.environ.get("IMAGE_PATH", "/e1002.png") # 方案一:恒定图片路径(每次请求内存渲染 PNG可改成难猜的路径防扫描
HTML_PATH = os.environ.get("HTML_PATH", "/e1002.html") # 方案二:恒定网页路径(每次请求生成 800x480 现代网页,平台截图)
# 方案三:服务端用无头 Chrome 把方案二的网页截成 800x480再按「最近色、不抖动」固化为六色 PNG需服务器装 Chrome/Chromium
HTML_PNG_PATH = os.environ.get("HTML_PNG_PATH", "/e1002-web.png")
CHROME_BIN = os.environ.get("CHROME_BIN", "") # 为空则按常见路径自动探测macOS Google Chrome / google-chrome / chromium
CHROME_EXTRA_ARGS = os.environ.get("CHROME_EXTRA_ARGS", "") # 例如 Linux 以 root 运行需 "--no-sandbox"
# 每通道二值化阈值0~255低于阈值归 0、否则归 255取 160而非 128偏向保留墨迹细笔画的浅灰边不至于被丢成白
HTML_PNG_THRESHOLD = int(os.environ.get("HTML_PNG_THRESHOLD", "160"))
PUSH_TOKEN = os.environ.get("PUSH_TOKEN", "") # 推送鉴权令牌X-Push-Token 头);为空则拒绝一切推送
PUSH_STATE_PATH = os.environ.get("PUSH_STATE_PATH", "") # 推送数据落盘路径(重启后恢复);为空=仅内存
# 本机推送端main.py --target push服务地址含协议与端口不含路径令牌同 PUSH_TOKEN
PUSH_URL = os.environ.get("PUSH_URL", "")
# ---- 资源路径 ----
FONT_PATH = os.path.join(BASE_DIR, "fonts", "fusion-pixel-12px-proportional-zh_hans.otf")
OUTPUT_DIR = os.path.join(BASE_DIR, "output")
@@ -67,11 +91,19 @@ QWEATHER_KEY = os.environ.get("QWEATHER_KEY", "")
WEATHER_LOCATION = os.environ.get("WEATHER_LOCATION", "118.840,31.953")
WEATHER_LOC_NAME = "南京·江宁"
# 天气缓存:业务数据每分钟刷新,但天气 15 分钟才重新拉一次cron 每次独立进程,缓存须落盘)。
WEATHER_TTL = int(os.environ.get("WEATHER_TTL", "900")) # 秒,默认 900=15 分钟
WEATHER_TTL = int(os.environ.get("WEATHER_TTL", "900")) # 秒,默认 900=15 分钟(本机文件缓存与 server.py 内存缓存共用)
WEATHER_CACHE_PATH = os.path.join(BASE_DIR, ".weather_cache.json")
# 近30天趋势缓存recent-days 每天只更新一次,按日期为 key 缓存,当天命中即不再请求。
TREND_CACHE_PATH = os.path.join(BASE_DIR, ".trend_cache.json")
# ---- 比特币 K 线(仅 800x480 宽版底部面板)----
# 免费公开接口无需密钥逐源兜底OKX → Coinbase → Huobi见 btc_api.py。当前为时 K、近 72 小时3 天);
# 每 5 分钟刷新(与 launchd 节律一致,最新一根随实时价推进)。拉取失败沿用旧缓存。缓存文件已在 .gitignore。
BTC_BAR = os.environ.get("BTC_BAR", "1H") # 周期:"1H" 时K / "1D" 日K
BTC_LIMIT = int(os.environ.get("BTC_LIMIT", "72")) # 展示最近 N 根(时 K 默认 72 = 近 3 天)
BTC_TTL = int(os.environ.get("BTC_TTL", "300")) # 秒,默认 5 分钟(本机文件缓存与 server.py 内存缓存共用)
BTC_CACHE_PATH = os.path.join(BASE_DIR, ".btc_cache.json")
# ---- Claude Code 用量Claude Usage 模块)数据源 ----
# 只读本地、**不发任何网络请求**(详见 usage_local.py——生产者/消费者模式:
# 生产者 = 打过补丁的 claude-statusline把每次从 Claude Code stdin 拿到的**权威实时**额度