chore: localize CLI output and comments to Chinese

This commit is contained in:
binglua committed 2026-06-29 02:49:10 +00:00
1 parent 636d2857c3
commit ebc94b50e9
5 files changed
+70 -71

No files matched your search

@@ -244,11 +244,10 @@ class MiFitnessCloudAdapter(DataAdapter):
return preferred_region return preferred_region
async def _discover_data_types(self) -> list[str]: async def _discover_data_types(self) -> list[str]:
# The cloud API does not provide a reliable capability endpoint. The # 小米健康云没有可靠的能力发现接口。旧实现用固定历史区间探测,
# previous implementation probed a fixed historical range, which made # 当该区间无数据时会导致自动同步静默跳过大部分指标。
# automatic sync silently skip most metrics when that range had no data. # 这里直接返回本适配器支持的全部数据类型;某类型无记录时同步 0 条即可,
# Return all data types supported by this adapter; syncing a type with no # 比漏掉近期数据更安全。
# records is safe and preferable to missing recent data.
return [ return [
"daily_activity", "daily_activity",
"heart_rate", "heart_rate",
+45 -45
View File
@@ -26,104 +26,104 @@ async def _check_adapter_health(adapter) -> tuple[bool, list[str], str | None]:
def cmd_setup(args): def cmd_setup(args):
if args.mode == "mi_fitness_cloud" and args.user_id and args.pass_token: if args.mode == "mi_fitness_cloud" and args.user_id and args.pass_token:
save_mi_fitness_token(args.user_id, args.pass_token) save_mi_fitness_token(args.user_id, args.pass_token)
config = Config(mode="mi_fitness_cloud", region=args.region or "ru") config = Config(mode="mi_fitness_cloud", region=args.region or "cn")
save_config(config) save_config(config)
print("✅ Конфигурация сохранена (mi_fitness_cloud)") print("✅ 配置已保存(mi_fitness_cloud)")
print(f" User ID: {args.user_id}") print(f" User ID: {args.user_id}")
print(f" Region: {config.region}") print(f" Region: {config.region}")
return return
print(f"{PROGRAM_NAME} - Setup Wizard") print(f"{PROGRAM_NAME} - 配置向导")
print("=" * 50) print("=" * 50)
print() print()
user_id = input("Mi Fitness user_id: ").strip() user_id = input("Mi Fitness user_id: ").strip()
pass_token = input("Mi Fitness passToken: ").strip() pass_token = input("Mi Fitness passToken: ").strip()
region = input("Region [ru]: ").strip() or "ru" region = input("区域 [cn]: ").strip() or "cn"
if not user_id or not pass_token: if not user_id or not pass_token:
print("❌ Требуются user_id и passToken") print("❌ 必须提供 user_id 和 passToken")
sys.exit(1) sys.exit(1)
save_mi_fitness_token(user_id, pass_token) save_mi_fitness_token(user_id, pass_token)
config = Config(mode="mi_fitness_cloud", region=region) config = Config(mode="mi_fitness_cloud", region=region)
save_config(config) save_config(config)
print("✅ Mi Fitness конфигурация сохранена!") print("✅ Mi Fitness 配置已保存!")
def cmd_doctor(args): def cmd_doctor(args):
print(f"{PROGRAM_NAME} - Doctor") print(f"{PROGRAM_NAME} - 诊断")
print("=" * 50) print("=" * 50)
print() print()
config_path = get_config_path() config_path = get_config_path()
print(f"Конфигурация: {config_path}") print(f"配置文件: {config_path}")
if not config_path.exists(): if not config_path.exists():
print("❌ Конфигурация не найдена") print("❌ 未找到配置文件")
print(f" Запустите: {PROGRAM_NAME} setup") print(f" 请运行: {PROGRAM_NAME} setup")
sys.exit(1) sys.exit(1)
try: try:
config = load_config() config = load_config()
print("✅ Конфигурация загружена") print("✅ 配置已加载")
print(f" Режим: {config.mode}") print(f" 模式: {config.mode}")
user_id, pass_token = load_mi_fitness_token() user_id, pass_token = load_mi_fitness_token()
if user_id and pass_token: if user_id and pass_token:
print("✅ Mi Fitness credentials найдены") print("✅ 已找到 Mi Fitness 凭据")
print(f" User ID: {user_id}") print(f" User ID: {user_id}")
print(f" Region: {config.region}") print(f" Region: {config.region}")
adapter = MiFitnessCloudAdapter( adapter = MiFitnessCloudAdapter(
user_id=user_id, pass_token=pass_token, region=config.region user_id=user_id, pass_token=pass_token, region=config.region
) )
connected, data_types, region = asyncio.run(_check_adapter_health(adapter)) connected, data_types, region = asyncio.run(_check_adapter_health(adapter))
print(f" Подключение: {'✅' if connected else '❌'}") print(f" 连接状态: {'✅' if connected else '❌'}")
if connected: if connected:
print(f" Определённый регион: {region}") print(f" 识别到的区域: {region}")
print(f" Типы данных: {', '.join(data_types)}") print(f" 数据类型: {', '.join(data_types)}")
else: else:
print("❌ Mi Fitness credentials не найдены") print("❌ 未找到 Mi Fitness 凭据")
print(f" Запустите: {PROGRAM_NAME} setup") print(f" 请运行: {PROGRAM_NAME} setup")
print() print()
print(f"База данных: {config.database_path}") print(f"数据库: {config.database_path}")
if config.database_path.exists(): if config.database_path.exists():
Database(config.database_path) Database(config.database_path)
print("✅ База данных доступна") print("✅ 数据库可用")
else: else:
print("ℹ️ База данных будет создана при первом запуске") print("ℹ️ 数据库将在首次运行时创建")
except Exception as e: except Exception as e:
print(f"❌ Ошибка загрузки конфигурации: {e}") print(f"❌ 加载配置失败: {e}")
sys.exit(1) sys.exit(1)
async def cmd_sync_async(args): async def cmd_sync_async(args):
print(f"{PROGRAM_NAME} - Sync") print(f"{PROGRAM_NAME} - 同步")
print("=" * 50) print("=" * 50)
print() print()
try: try:
config = load_config() config = load_config()
except Exception as e: except Exception as e:
print(f"❌ Ошибка загрузки конфигурации: {e}") print(f"❌ 加载配置失败: {e}")
sys.exit(1) sys.exit(1)
if config.mode == "not_configured": if config.mode == "not_configured":
print("❌ Сервер не настроен") print("❌ 服务尚未配置")
print(f" Запустите: {PROGRAM_NAME} setup") print(f" 请运行: {PROGRAM_NAME} setup")
sys.exit(1) sys.exit(1)
user_id, pass_token = load_mi_fitness_token() user_id, pass_token = load_mi_fitness_token()
if not user_id or not pass_token: if not user_id or not pass_token:
print("❌ Mi Fitness credentials не найдены") print("❌ 未找到 Mi Fitness 凭据")
print(f" Запустите: {PROGRAM_NAME} setup") print(f" 请运行: {PROGRAM_NAME} setup")
sys.exit(1) sys.exit(1)
db = Database(config.database_path) db = Database(config.database_path)
adapter = MiFitnessCloudAdapter(user_id=user_id, pass_token=pass_token, region=config.region) adapter = MiFitnessCloudAdapter(user_id=user_id, pass_token=pass_token, region=config.region)
if not await adapter.connect(): if not await adapter.connect():
print("❌ Не удалось подключиться к Mi Fitness API") print("❌ 无法连接到 Mi Fitness API")
sys.exit(1) sys.exit(1)
sync_service = SyncService(adapter, db) sync_service = SyncService(adapter, db)
data_types = [args.type] if args.type else adapter.get_available_data_types() data_types = [args.type] if args.type else adapter.get_available_data_types()
print(f"Синхронизация {len(data_types)} типов данных...") print(f"正在同步 {len(data_types)} 种数据类型...")
print() print()
for data_type in data_types: for data_type in data_types:
try: try:
@@ -132,12 +132,12 @@ async def cmd_sync_async(args):
start_date=args.start_date, start_date=args.start_date,
end_date=args.end_date, end_date=args.end_date,
) )
print(f"✅ {data_type}: {result['added']} добавлено, {result['updated']} обновлено") print(f"✅ {data_type}: 新增 {result['added']} 条,更新 {result['updated']} 条")
except Exception as e: except Exception as e:
print(f"❌ {data_type}: {e}") print(f"❌ {data_type}: {e}")
print() print()
print("Синхронизация завершена!") print("同步完成!")
def cmd_sync(args): def cmd_sync(args):
@@ -146,20 +146,20 @@ def cmd_sync(args):
def main(): def main():
parser = argparse.ArgumentParser( parser = argparse.ArgumentParser(
prog=PROGRAM_NAME, description="MCP server for Mi Fitness data" prog=PROGRAM_NAME, description="小米运动健康数据 MCP Server"
) )
subparsers = parser.add_subparsers(dest="command", help="Available commands") subparsers = parser.add_subparsers(dest="command", help="可用命令")
subparsers.add_parser("serve", help="Run MCP server") subparsers.add_parser("serve", help="运行 MCP Server")
setup_parser = subparsers.add_parser("setup", help="Configure the server") setup_parser = subparsers.add_parser("setup", help="配置服务")
setup_parser.add_argument("--mode", choices=["mi_fitness_cloud"], help="Setup mode") setup_parser.add_argument("--mode", choices=["mi_fitness_cloud"], help="配置模式")
setup_parser.add_argument("--user-id", help="Mi Fitness user ID") setup_parser.add_argument("--user-id", help="Mi Fitness user ID")
setup_parser.add_argument("--pass-token", help="Mi Fitness passToken") setup_parser.add_argument("--pass-token", help="Mi Fitness passToken")
setup_parser.add_argument("--region", help="Cloud region") setup_parser.add_argument("--region", help="云端区域")
subparsers.add_parser("doctor", help="Check configuration and diagnose issues") subparsers.add_parser("doctor", help="检查配置并诊断问题")
sync_parser = subparsers.add_parser("sync", help="Sync data from source") sync_parser = subparsers.add_parser("sync", help="从数据源同步数据")
sync_parser.add_argument( sync_parser.add_argument(
"--type", "--type",
choices=[ choices=[
@@ -172,10 +172,10 @@ def main():
"stress", "stress",
"abnormal_heart_beat", "abnormal_heart_beat",
], ],
help="Type of data to sync", help="要同步的数据类型",
) )
sync_parser.add_argument("--start-date", help="Start date (YYYY-MM-DD)") sync_parser.add_argument("--start-date", help="开始日期(YYYY-MM-DD)")
sync_parser.add_argument("--end-date", help="End date (YYYY-MM-DD)") sync_parser.add_argument("--end-date", help="结束日期(YYYY-MM-DD)")
args = parser.parse_args() args = parser.parse_args()
if args.command == "serve" or args.command is None: if args.command == "serve" or args.command is None:
+8 -8
View File
@@ -24,7 +24,7 @@ class QueryService:
"""Get daily activity summaries for date range.""" """Get daily activity summaries for date range."""
records = self.db.query_daily_activity(self.user_id, start_date, end_date) records = self.db.query_daily_activity(self.user_id, start_date, end_date)
# Group by date and aggregate # 按日期分组并聚合
summaries = {} summaries = {}
for record in records: for record in records:
date = record["date"] date = record["date"]
@@ -73,7 +73,7 @@ class QueryService:
} }
) )
# Apply aggregation if needed # 按需执行周/月聚合
if granularity == "week": if granularity == "week":
series = self._aggregate_by_week(series, aggregation) series = self._aggregate_by_week(series, aggregation)
elif granularity == "month": elif granularity == "month":
@@ -91,7 +91,7 @@ class QueryService:
for item in series: for item in series:
date = datetime.strptime(item["date"], "%Y-%m-%d") date = datetime.strptime(item["date"], "%Y-%m-%d")
# Get week start (Monday) # 计算周起始日(周一)
week_start = date - timedelta(days=date.weekday()) week_start = date - timedelta(days=date.weekday())
week_key = week_start.strftime("%Y-%m-%d") week_key = week_start.strftime("%Y-%m-%d")
@@ -160,7 +160,7 @@ class QueryService:
sessions = [] sessions = []
for record in records: for record in records:
# Skip naps if not included # 不包含小睡时跳过 nap 记录
if not include_naps and record.get("is_nap"): if not include_naps and record.get("is_nap"):
continue continue
@@ -175,7 +175,7 @@ class QueryService:
"is_nap": record.get("is_nap", False), "is_nap": record.get("is_nap", False),
} }
# Parse stages if available # 如有睡眠阶段信息则解析
if record.get("stages"): if record.get("stages"):
import json import json
@@ -199,7 +199,7 @@ class QueryService:
workouts = [] workouts = []
for record in records: for record in records:
# Apply filters # 应用过滤条件
if activity_types and record["activity_type"].lower() not in [ if activity_types and record["activity_type"].lower() not in [
t.lower() for t in activity_types t.lower() for t in activity_types
]: ]:
@@ -248,7 +248,7 @@ class QueryService:
"weight_kg": record["weight_kg"], "weight_kg": record["weight_kg"],
} }
# Add optional metrics # 添加可选指标
optional_fields = [ optional_fields = [
"bmi", "bmi",
"body_fat_pct", "body_fat_pct",
@@ -264,7 +264,7 @@ class QueryService:
if record.get(field) is not None: if record.get(field) is not None:
measurement[field] = record[field] measurement[field] = record[field]
# Filter metrics if specified # 如指定指标列表则过滤字段
if metrics: if metrics:
filtered = {"timestamp": measurement["timestamp"]} filtered = {"timestamp": measurement["timestamp"]}
for metric in metrics: for metric in metrics:
+3 -3
View File
@@ -55,7 +55,7 @@ class SyncService:
if not self.adapter.is_connected(): if not self.adapter.is_connected():
raise RuntimeError("Adapter not connected") raise RuntimeError("Adapter not connected")
# Get last sync state for incremental sync # 获取上次同步状态,用于增量同步
last_record_ts = None last_record_ts = None
if not force_full: if not force_full:
state = self.db.get_sync_state(data_type) state = self.db.get_sync_state(data_type)
@@ -72,7 +72,7 @@ class SyncService:
skipped = 0 skipped = 0
last_ts = None last_ts = None
# Sync based on data type # 按数据类型执行同步
if data_type == "daily_activity": if data_type == "daily_activity":
records = self.adapter.iter_daily_activity(start_date, end_date) records = self.adapter.iter_daily_activity(start_date, end_date)
async for activity in self._iterate_records(records): async for activity in self._iterate_records(records):
@@ -156,7 +156,7 @@ class SyncService:
else: else:
raise ValueError(f"Unknown data type: {data_type}") raise ValueError(f"Unknown data type: {data_type}")
# Update sync state # 更新同步状态
if last_ts: if last_ts:
self.db.update_sync_state(data_type, last_ts) self.db.update_sync_state(data_type, last_ts)
+10 -10
View File
@@ -36,7 +36,7 @@ class Database:
self.db_path.parent.mkdir(parents=True, exist_ok=True) self.db_path.parent.mkdir(parents=True, exist_ok=True)
with self._get_connection() as conn: with self._get_connection() as conn:
# Daily activity table # 日常活动表
conn.execute(""" conn.execute("""
CREATE TABLE IF NOT EXISTS daily_activity ( CREATE TABLE IF NOT EXISTS daily_activity (
id TEXT PRIMARY KEY, id TEXT PRIMARY KEY,
@@ -60,7 +60,7 @@ class Database:
) )
""") """)
# Sleep sessions table # 睡眠记录表
conn.execute(""" conn.execute("""
CREATE TABLE IF NOT EXISTS sleep_sessions ( CREATE TABLE IF NOT EXISTS sleep_sessions (
id TEXT PRIMARY KEY, id TEXT PRIMARY KEY,
@@ -86,7 +86,7 @@ class Database:
) )
""") """)
# Workouts table # 运动记录表
conn.execute(""" conn.execute("""
CREATE TABLE IF NOT EXISTS workouts ( CREATE TABLE IF NOT EXISTS workouts (
id TEXT PRIMARY KEY, id TEXT PRIMARY KEY,
@@ -115,7 +115,7 @@ class Database:
) )
""") """)
# Body measurements table # 身体测量表
conn.execute(""" conn.execute("""
CREATE TABLE IF NOT EXISTS body_measurements ( CREATE TABLE IF NOT EXISTS body_measurements (
id TEXT PRIMARY KEY, id TEXT PRIMARY KEY,
@@ -218,7 +218,7 @@ class Database:
) )
""") """)
# Sync state table # 同步状态表
conn.execute(""" conn.execute("""
CREATE TABLE IF NOT EXISTS sync_state ( CREATE TABLE IF NOT EXISTS sync_state (
id INTEGER PRIMARY KEY AUTOINCREMENT, id INTEGER PRIMARY KEY AUTOINCREMENT,
@@ -229,7 +229,7 @@ class Database:
) )
""") """)
# Create indexes for better query performance # 创建索引以提升查询性能
conn.execute(""" conn.execute("""
CREATE INDEX IF NOT EXISTS idx_activity_user_date CREATE INDEX IF NOT EXISTS idx_activity_user_date
ON daily_activity(user_id, date) ON daily_activity(user_id, date)
@@ -758,7 +758,7 @@ class Database:
with self._get_connection() as conn: with self._get_connection() as conn:
results = [] results = []
# Activity coverage # 日常活动覆盖范围
row = conn.execute( row = conn.execute(
""" """
SELECT SELECT
@@ -778,7 +778,7 @@ class Database:
} }
) )
# Sleep coverage # 睡眠覆盖范围
row = conn.execute( row = conn.execute(
""" """
SELECT SELECT
@@ -798,7 +798,7 @@ class Database:
} }
) )
# Workouts coverage # 运动记录覆盖范围
row = conn.execute( row = conn.execute(
""" """
SELECT SELECT
@@ -818,7 +818,7 @@ class Database:
} }
) )
# Body measurements coverage # 身体测量覆盖范围
row = conn.execute( row = conn.execute(
""" """
SELECT SELECT