update at 2026-03-17 10:37:27

This commit is contained in:
douboer@gmail.com
2026-03-17 10:37:27 +08:00
parent e5becf63cf
commit 192eb1b8d1
44 changed files with 5208 additions and 403 deletions

410
bootstrap-new-kindle.sh Normal file
View File

@@ -0,0 +1,410 @@
#!/usr/bin/env sh
set -eu
ROOT_DIR="$(CDPATH= cd -- "$(dirname "$0")" && pwd)"
WATCHTHIS_DIR="$ROOT_DIR/dash/staging/watchthis"
POST_JAILBREAK_ROOT="$ROOT_DIR/dash/staging/post-jailbreak-root"
SSH_HELPERS_DIR="$ROOT_DIR/scripts/kindle"
SYNC_SCRIPT="$ROOT_DIR/scripts/sync-layered-clock-to-kindle.sh"
THEMES_JSON="$ROOT_DIR/calendar/config/themes.json"
MODE="all"
VOLUME_PATH="/Volumes/Kindle"
HOST_TARGET="kindle"
THEME_ID=""
ORIENTATION=""
SHOW_BACKGROUND=true
START_DASHBOARD=false
print_usage() {
cat <<'EOF'
用法:
sh bootstrap-new-kindle.sh [模式] [选项]
模式:
all 默认。能预置 USB 存储就先预置;能连 SSH 就继续做 SSH 后半段
prepare-storage 只做 USB 存储预置
post-ssh 只做 SSH 打通后的自动化收尾
选项:
-v, --volume <path> Kindle 挂载目录,默认 /Volumes/Kindle
-k, --kindle <host> Kindle SSH 主机名,默认 kindle
-t, --theme <theme-id> SSH 阶段切换到指定主题;默认使用 themes.json 的默认主题
-o, --orientation <value> SSH 阶段切换到指定方向;默认使用 themes.json 的默认方向
--no-background SSH 阶段不同步后立即切主题出图
--start-dashboard SSH 阶段额外后台启动 dashboard 主循环
-h, --help 查看帮助
说明:
这不是 100% 零交互刷机脚本。
受 Kindle 本机流程限制,下面这些步骤仍然必须人工完成:
1. 进入 demo mode / WatchThis 流程
2. 在正确时机执行 Sideload Content
3. 触发 Get Started 完成越狱
4. 搜索 ;log mrpi 安装 KUAL / MRPI / USBNetwork
5. 首次进 KTerm 执行 sh /mnt/us/ssh-force-dropbear-22.sh
推荐操作顺序:
阶段 A先做 USB 预置
1. 用 USB 线把 Kindle 接到 Mac。
2. 确认 Finder 里已经出现 Kindle默认挂载目录是 /Volumes/Kindle。
3. 执行:
sh bootstrap-new-kindle.sh prepare-storage
4. 预期结果:
- Kindle 根目录出现 Update_hotfix_watchthis_custom.bin
- Kindle 根目录出现 ssh-force-dropbear-22.sh 等脚本
- Kindle 根目录出现 dashboard/、extensions/、mrpackages/
- Kindle 根目录出现 .demo/KV-5.13.6.zip、.demo/demo.json、.demo/goodreads/
5. 安全弹出 Kindle。
阶段 B在 Kindle 上完成 WatchThis 和越狱
1. 恢复出厂。
2. 语言只选 English (United Kingdom)。
3. 到 Wi-Fi 页面时,不要真的联网;先按 WatchThis 文档进入 demo mode。
4. 第一次出现 Add Content / Sideload Content 时,只点 Done不要接 USB。
5. 遇到 misconfiguration / Configure Device 时,执行隐藏手势回到可操作界面。
6. 再次进入 ;demo -> Sideload Content这一次才是真正导入 payload 的时机。
因为阶段 A 已经预置好 .demo 内容,这里不需要再从 Mac 手工拷文件。
7. 退出 demo menu 后,进入 Help & User Guides -> Get Started 触发越狱。
8. 越狱成功后Kindle 根目录应出现 mkk、libkh、rp。
阶段 C安装 KUAL / MRPI / USBNetwork / dashboard
1. 在 Kindle 首页搜索:
;log mrpi
2. 等 MRPI 安装完成。
3. 预期结果:
- 首页出现 KUAL
- KUAL 菜单里有 Rename OTA Binaries
- KUAL 菜单里有 kindle-dash
4. 先在 KUAL 中执行:
Rename OTA Binaries -> Rename
阶段 D打通 Wi-Fi 和 SSH
1. 让 Kindle 连到和 Mac 同一个主 Wi-Fi不要用 Guest 网络。
2. 打开 KTerm。
3. 在 KTerm 执行:
sh /mnt/us/ssh-force-dropbear-22.sh
4. 回到 Mac先测试
ssh kindle
5. 如果这一步还不通,不要继续盲试,先回看:
dash/docs/kindle-voyage-5.13.6-dual-ssh-playbook-zh.md
阶段 ESSH 打通后自动同步并出图
1. 执行:
sh bootstrap-new-kindle.sh post-ssh -t simple -o portrait
2. 预期结果:
- 自动同步 dashboard 运行时和主题包
- Kindle 立即切到 simple / portrait
- 屏幕马上显示背景和时钟
3. 如果还希望后台常驻 dashboard 主循环,再执行:
sh bootstrap-new-kindle.sh post-ssh -t simple -o portrait --start-dashboard
最省事的直接用法:
1. USB 挂载时先运行:
sh bootstrap-new-kindle.sh all -t simple -o portrait
这会先做完能做的 USB 预置。
2. 等你在 Kindle 上完成越狱、MRPI、Wi-Fi、KTerm 拉起 SSH 后,
再重新运行同一条命令:
sh bootstrap-new-kindle.sh all -t simple -o portrait
这时它会自动继续做 SSH 后半段。
最容易做错的地方:
1. 第一次 Add Content / Sideload Content 只能点 Done不能在这一步导 payload。
2. 真正导 payload 的时机,是隐藏手势返回后,再次进入 ;demo -> Sideload Content。
3. 首次 SSH 最稳的入口是 KTerm不是 USB 直连盲试。
4. post-ssh 阶段如果不带 -t/-o会退回 themes.json 里的默认主题和方向。
示例:
sh bootstrap-new-kindle.sh prepare-storage
sh bootstrap-new-kindle.sh all -t simple -o portrait
sh bootstrap-new-kindle.sh post-ssh -k kindle --start-dashboard
EOF
}
log_step() {
printf '\n[%s] %s\n' "$1" "$2"
}
require_path() {
target_path=$1
label=$2
if [ ! -e "$target_path" ]; then
echo "缺少${label}: $target_path" >&2
exit 1
fi
}
kindle_volume_available() {
[ -d "$VOLUME_PATH" ]
}
ssh_reachable() {
ssh -o BatchMode=yes -o ConnectTimeout=5 "$HOST_TARGET" true >/dev/null 2>&1
}
resolve_theme_selection() {
python3 - "$THEMES_JSON" "$THEME_ID" "$ORIENTATION" <<'PY'
import json
import pathlib
import sys
path = pathlib.Path(sys.argv[1])
requested_theme = sys.argv[2]
requested_orientation = sys.argv[3]
data = json.loads(path.read_text())
themes = {theme["id"]: theme for theme in data.get("themes", [])}
theme_id = requested_theme or data.get("defaultThemeId", "")
if theme_id not in themes:
raise SystemExit(f"未知主题: {theme_id}")
orientations = list(themes[theme_id].get("variants", {}).keys())
if not orientations:
raise SystemExit(f"主题 {theme_id} 没有任何方向配置")
orientation = requested_orientation or data.get("defaultOrientation", "")
if orientation not in orientations:
orientation = orientations[0]
print(f"THEME_ID={theme_id}")
print(f"ORIENTATION={orientation}")
PY
}
load_theme_selection() {
resolved_output="$(resolve_theme_selection)"
RESOLVED_THEME_ID=""
RESOLVED_ORIENTATION=""
while IFS='=' read -r key value; do
case "$key" in
THEME_ID)
RESOLVED_THEME_ID=$value
;;
ORIENTATION)
RESOLVED_ORIENTATION=$value
;;
esac
done <<EOF
$resolved_output
EOF
if [ -z "$RESOLVED_THEME_ID" ] || [ -z "$RESOLVED_ORIENTATION" ]; then
echo "无法解析主题和方向。" >&2
exit 1
fi
}
copy_watchthis_payload() {
require_path "$WATCHTHIS_DIR/KV-5.13.6/KV-5.13.6.zip" "WatchThis payload"
require_path "$WATCHTHIS_DIR/KV-5.13.6/demo.json" "WatchThis demo.json"
require_path "$WATCHTHIS_DIR/Update_hotfix_watchthis_custom.bin" "WatchThis hotfix"
mkdir -p "$VOLUME_PATH/.demo/goodreads"
cp "$WATCHTHIS_DIR/KV-5.13.6/KV-5.13.6.zip" "$VOLUME_PATH/.demo/"
cp "$WATCHTHIS_DIR/KV-5.13.6/demo.json" "$VOLUME_PATH/.demo/"
cp "$WATCHTHIS_DIR/Update_hotfix_watchthis_custom.bin" "$VOLUME_PATH/"
}
copy_post_jailbreak_bundle() {
require_path "$POST_JAILBREAK_ROOT/extensions" "post-jailbreak extensions"
require_path "$POST_JAILBREAK_ROOT/mrpackages" "post-jailbreak mrpackages"
require_path "$POST_JAILBREAK_ROOT/dashboard" "post-jailbreak dashboard"
mkdir -p "$VOLUME_PATH/extensions" "$VOLUME_PATH/mrpackages" "$VOLUME_PATH/dashboard"
rsync -av --no-o --no-g "$POST_JAILBREAK_ROOT/extensions/" "$VOLUME_PATH/extensions/"
rsync -av --no-o --no-g "$POST_JAILBREAK_ROOT/mrpackages/" "$VOLUME_PATH/mrpackages/"
rsync -av --no-o --no-g "$POST_JAILBREAK_ROOT/dashboard/" "$VOLUME_PATH/dashboard/"
}
copy_ssh_helpers() {
require_path "$SSH_HELPERS_DIR/ssh-force-dropbear-22.sh" "SSH helper scripts"
rsync -av --no-o --no-g "$SSH_HELPERS_DIR/" "$VOLUME_PATH/"
}
print_storage_next_steps() {
cat <<'EOF'
下一步请在 Kindle 上继续:
1. 按 [dash/docs/kindle-voyage-5.13.6-watchthis-zh.md] 的流程进入 demo mode。
2. 走到真正的 Sideload Content 时,脚本已预置好:
- .demo/KV-5.13.6.zip
- .demo/demo.json
- .demo/goodreads/
3. 触发 Get Started 完成越狱。
4. 搜索 `;log mrpi` 安装 KUAL / MRPI / USBNetwork / dashboard。
5. 先在 KUAL 里执行 `Rename OTA Binaries -> Rename`。
6. 连上 Wi-Fi。
7. 打开 KTerm执行
sh /mnt/us/ssh-force-dropbear-22.sh
8. 回到 Mac 后,再运行:
sh bootstrap-new-kindle.sh post-ssh
EOF
}
prepare_storage() {
if ! kindle_volume_available; then
echo "未找到 Kindle 挂载目录: $VOLUME_PATH" >&2
exit 1
fi
log_step "USB" "预置 WatchThis payload"
copy_watchthis_payload
log_step "USB" "预置越狱后安装包"
copy_post_jailbreak_bundle
log_step "USB" "预置 SSH 恢复脚本"
copy_ssh_helpers
log_step "完成" "USB 存储预置已完成:$VOLUME_PATH"
print_storage_next_steps
}
prepare_remote_helpers() {
ssh "$HOST_TARGET" "chmod +x /mnt/us/ssh-collect.sh /mnt/us/ssh-fix-all-keys.sh /mnt/us/ssh-force-dropbear-22.sh /mnt/us/ssh-force-openssh-22.sh /mnt/us/ssh-stop-all.sh 2>/dev/null || true"
ssh "$HOST_TARGET" "if [ -f /mnt/us/ssh-fix-all-keys.sh ]; then sh /mnt/us/ssh-fix-all-keys.sh; fi"
}
sync_dashboard_runtime() {
if [ -n "$THEME_ID" ]; then
if [ -n "$ORIENTATION" ]; then
sh "$SYNC_SCRIPT" "$HOST_TARGET" --theme "$THEME_ID" --orientation "$ORIENTATION"
else
sh "$SYNC_SCRIPT" "$HOST_TARGET" --theme "$THEME_ID"
fi
else
sh "$SYNC_SCRIPT" "$HOST_TARGET"
fi
}
show_background_once() {
load_theme_selection
ssh "$HOST_TARGET" "/mnt/us/dashboard/switch-theme.sh '$RESOLVED_THEME_ID' '$RESOLVED_ORIENTATION'"
}
start_dashboard_loop() {
# 当前仓库里最稳的是 SSH 触发启动;这里后台拉起,便于 bootstrap 脚本直接返回。
ssh "$HOST_TARGET" "mkdir -p /mnt/us/dashboard/logs && cd /mnt/us/dashboard && nohup ./start.sh >/mnt/us/dashboard/logs/bootstrap-start.log 2>&1 </dev/null &"
}
print_post_ssh_summary() {
load_theme_selection
cat <<EOF
SSH 阶段已完成:
- 主机:$HOST_TARGET
- 已同步 dashboard 运行时与主题包
- 当前用于出图的主题:$RESOLVED_THEME_ID / $RESOLVED_ORIENTATION
如果你要继续验证:
- SSH 登录ssh $HOST_TARGET
- 立即切主题ssh $HOST_TARGET '/mnt/us/dashboard/switch-theme.sh $RESOLVED_THEME_ID $RESOLVED_ORIENTATION'
- 抓图到本地sh snapshot.sh -t $RESOLVED_THEME_ID -o $RESOLVED_ORIENTATION
EOF
}
post_ssh() {
if ! ssh_reachable; then
echo "当前还无法通过 SSH 连接 $HOST_TARGET" >&2
echo "请先在 Kindle 上连上 Wi-Fi并在 KTerm 执行 sh /mnt/us/ssh-force-dropbear-22.sh" >&2
exit 1
fi
log_step "SSH" "修复设备侧 SSH 辅助脚本权限与 authorized_keys"
prepare_remote_helpers
log_step "同步" "同步 dashboard 运行时和主题包到 Kindle"
sync_dashboard_runtime
if [ "$SHOW_BACKGROUND" = true ]; then
log_step "显示" "立即切到当前主题并出图"
show_background_once
fi
if [ "$START_DASHBOARD" = true ]; then
log_step "启动" "后台启动 dashboard 主循环"
start_dashboard_loop
fi
log_step "完成" "SSH 阶段自动化已完成"
print_post_ssh_summary
}
while [ "$#" -gt 0 ]; do
case "$1" in
all|prepare-storage|post-ssh)
MODE=$1
;;
-v|--volume)
shift
VOLUME_PATH=${1:?"missing volume path"}
;;
-k|--kindle)
shift
HOST_TARGET=${1:?"missing kindle host"}
;;
-t|--theme)
shift
THEME_ID=${1:?"missing theme id"}
;;
-o|--orientation)
shift
ORIENTATION=${1:?"missing orientation"}
;;
--no-background)
SHOW_BACKGROUND=false
;;
--start-dashboard)
START_DASHBOARD=true
;;
-h|--help)
print_usage
exit 0
;;
*)
echo "未知参数: $1" >&2
print_usage >&2
exit 1
;;
esac
shift
done
require_path "$THEMES_JSON" "themes.json"
require_path "$SYNC_SCRIPT" "sync-layered-clock-to-kindle.sh"
case "$MODE" in
prepare-storage)
prepare_storage
;;
post-ssh)
post_ssh
;;
all)
did_anything=false
if kindle_volume_available; then
prepare_storage
did_anything=true
else
log_step "跳过" "未检测到 Kindle 存储挂载:$VOLUME_PATH"
fi
if ssh_reachable; then
post_ssh
did_anything=true
else
log_step "等待" "SSH 还未打通;等 Kindle 连上 Wi-Fi 并在 KTerm 执行 sh /mnt/us/ssh-force-dropbear-22.sh 后,再重跑本脚本或执行 post-ssh。"
fi
if [ "$did_anything" != true ]; then
echo "既没有检测到 Kindle 存储挂载,也没有检测到可用 SSH。" >&2
echo "请先通过 USB 挂载 Kindle或先恢复 SSH 后再执行。" >&2
exit 1
fi
;;
esac