feat: 版式重构为三段 + 接入 Claude Usage(只读本地) + 定位江宁
将仪表盘从「四区(时钟/天气/今日实时/30天趋势)」重构为上中下三段: - A 顶部:大时钟+日期 / 天气(定位改南京·江宁) - B 中部主角 Claude Usage:5h/7d 用量进度条 + 大号百分比 + 时间维度对比 pace (pace=用量%−时间%,超前↑红/节余↓黑,参考 claude-statusline);用量≥阈值该条转红预警 - C 底部 橘喵今日实时:三列版式对齐主分支(名F12/值F24粗/环比同比偏移 +18/+34/+64/+82) - 近30天趋势图隐藏(_trend_chart 及依赖保留、未调用,可挂回) 用量只读本地、不发网络请求:新增 usage_local.py 读取 claude-statusline 落盘的 /tmp/claude/statusline-usage-cache.json(utilization + resets_at)。utilization 可能滞后, 但 resets_at 为绝对时间,故 pace 每次按当前时间现算、始终准确;文件缺失回退 "--"。 其他: - 天气定位 秦淮→江宁(config 默认 118.840,31.953) - 进度条边框随条色(红条即红框);行内元素按墨迹竖直中心对齐(_mid) - 新增 F13/F16 字号(非整数倍,fontmode=1 保持纯色) - 全图仍严格三色(黑/白/红)、无灰阶不抖动;底部留 ~11px 下边距 - 同步更新 CLAUDE.md / README.md Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
53
data.py
53
data.py
@@ -13,10 +13,12 @@ from datetime import datetime
|
||||
import config
|
||||
import jm_api
|
||||
import weather_api
|
||||
import usage_local
|
||||
|
||||
WEEK_CN = ["周一", "周二", "周三", "周四", "周五", "周六", "周日"]
|
||||
|
||||
TREND_DAYS = 30 # 近 N 天趋势
|
||||
FIVE_HOUR, SEVEN_DAY = 5 * 3600, 7 * 86400 # 两个滚动窗口长度(秒),用于算「时间已流逝%」
|
||||
|
||||
|
||||
def _read_weather_cache():
|
||||
@@ -166,6 +168,56 @@ def _mao(now):
|
||||
return {"upd": upd, "cols": cols, "trend": trend}
|
||||
|
||||
|
||||
# ---------- Claude Code 用量 ----------
|
||||
def _parse_iso_ts(s):
|
||||
"""ISO 时间串 → epoch 秒;兼容结尾 'Z'。失败返回 None。"""
|
||||
if not s:
|
||||
return None
|
||||
try:
|
||||
return datetime.fromisoformat(s.replace("Z", "+00:00")).timestamp()
|
||||
except Exception:
|
||||
return None
|
||||
|
||||
|
||||
def _window_time_pct(resets_at, window, now):
|
||||
"""该滚动窗口「已流逝时间百分比」= (window − 距重置剩余) / window ×100。失败返回 None。"""
|
||||
reset = _parse_iso_ts(resets_at)
|
||||
if reset is None:
|
||||
return None
|
||||
remaining = max(0, min(window, reset - now.timestamp()))
|
||||
return int(round((window - remaining) / window * 100))
|
||||
|
||||
|
||||
def _usage(now):
|
||||
"""Claude Code 用量(5h / 7d)——只读本地 statusline 缓存(见 usage_local.py),**不发任何请求**。
|
||||
|
||||
time_pct(时间已流逝%) 与 pace 每次按当前时间**现算**(resets_at 为绝对时间,始终准确);
|
||||
utilization 取自本地缓存、可能滞后。文件缺失/损坏 → 回退占位 pct=None(renderer 显示 "--")。
|
||||
结构契约(renderer 依赖):{title, warn, bars:[{k, pct(0~100 或 None), time_pct(0~100)?}]};
|
||||
pct ≥ warn 时该条进度条与百分比转红;pace = pct − time_pct(>0 超前↑ / <0 节余↓)。
|
||||
"""
|
||||
wins = {}
|
||||
try:
|
||||
raw = usage_local.get_usage()
|
||||
for key, label, win in (("five_hour", "5h", FIVE_HOUR), ("seven_day", "7d", SEVEN_DAY)):
|
||||
seg = raw.get(key) or {}
|
||||
wins[label] = {"pct": int(round(float(seg.get("utilization", 0)))),
|
||||
"resets_at": seg.get("resets_at"), "win": win}
|
||||
except Exception as e:
|
||||
print(f"[警告] Claude 用量读取失败:{e}")
|
||||
|
||||
bars = []
|
||||
for label in ("5h", "7d"):
|
||||
w = wins.get(label) or {}
|
||||
pct = w.get("pct")
|
||||
bar = {"k": label, "pct": pct}
|
||||
tp = _window_time_pct(w.get("resets_at"), w.get("win"), now)
|
||||
if pct is not None and tp is not None:
|
||||
bar["time_pct"] = tp
|
||||
bars.append(bar)
|
||||
return {"title": "Claude Usage", "warn": config.USAGE_WARN, "bars": bars}
|
||||
|
||||
|
||||
def get_dashboard_data(now=None):
|
||||
"""返回渲染所需的完整数据字典。"""
|
||||
now = now or datetime.now()
|
||||
@@ -177,4 +229,5 @@ def get_dashboard_data(now=None):
|
||||
},
|
||||
"weather": _weather(now),
|
||||
"mao": _mao(now),
|
||||
"usage": _usage(now),
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user