什么是 Provider?
Provider 是将 Mirobody Health 与外部健康和健身设备 API 集成的插件模块。每个 Provider 负责处理 OAuth 身份验证、数据获取以及将数据转换为标准化格式。
Provider 架构
Mirobody Health 使用可扩展的 Provider 架构,使添加新的健康设备集成变得非常容易:
BaseThetaProvider (抽象基类)
↓
ThetaGarminProvider (OAuth1 实现)
ThetaWhoopProvider (OAuth2 实现)
YourCustomProvider (您的自定义实现)
核心组件
身份验证处理程序 管理 OAuth 1.0 或 OAuth 2.0 流程,实现安全的用户授权
数据拉取器 从厂商 API 获取健康数据,支持分页和速率限制
数据格式化程序 将厂商特定的数据转换为标准化的健康指标
数据库服务 持久化存储原始数据和格式化数据,以便进行审计追踪
可用 Provider
Garmin Connect
身份验证 : OAuth 1.0
状态 : 生产就绪 (Production Ready)
数据类型 : 活动、睡眠、心率、HRV、呼吸、压力、身体成分Garmin Connect 是最全面的健康数据源之一,提供来自 Garmin 可穿戴设备和健身设备的详细指标。
类别 指标 活动 步数、距离、卡路里(活动和基础)、爬楼层数、活动时间 心率 每日最低、最高、平均、静息心率、时间序列样本 睡眠 时长、深睡/浅睡/REM 睡眠、清醒时间、血氧、呼吸 HRV 时间序列 HRV 值 呼吸 呼吸频率样本 压力 压力水平测量 身体 体重、BMI、体脂率、骨骼肌重量 锻炼 锻炼时长、强度、心率
GARMIN_CLIENT_ID : '您的_consumer_key'
GARMIN_CLIENT_SECRET : '您的_consumer_secret'
GARMIN_REDIRECT_URL : 'http://localhost:18080/api/v1/pulse/theta/theta_garmin/callback'
有关详细设置说明,请参阅 配置指南 。
Whoop
身份验证 : OAuth 2.0
状态 : 生产就绪 (Production Ready)
数据类型 : 睡眠、恢复、周期、锻炼、身体测量Whoop 提供全面的恢复和负荷指标,深受运动员和健身爱好者的欢迎。
类别 指标 睡眠 在床总时间、清醒时间、浅睡/慢波/REM 睡眠、睡眠效率 恢复 HRV、静息心率、呼吸频率、血氧、皮肤温度 周期 负荷、千焦耳、平均心率 锻炼 活动类型、时长、强度、心率区间、卡路里 身体 身高、体重、最高心率
WHOOP_CLIENT_ID : '您的_client_id'
WHOOP_CLIENT_SECRET : '您的_client_secret'
WHOOP_REDIRECT_URL : 'http://localhost:18080/api/v1/pulse/theta/theta_whoop/callback'
WHOOP_SCOPES : 'offline read:recovery read:sleep read:cycles read:profile read:workout read:body_measurement'
有关详细设置说明,请参阅 配置指南 。
Provider 生命周期
理解 Provider 的生命周期有助于您更有效地开发和使用它们:
初始化
应用程序启动时,会自动从 THETA_PROVIDER_DIRS 指定的目录中发现并初始化 Provider。 THETA_PROVIDER_DIRS :
- mirobody/pulse/theta
注册
系统会调用每个 Provider 的 create_provider() 类方法来验证配置并创建实例。 @ classmethod
def create_provider ( cls , config : Dict[ str , Any]) -> Optional[ 'ThetaYourProvider' ]:
if not cls ._validate_config(config):
return None
return cls ()
用户绑定
当用户发起绑定时,Provider 的 link() 方法会生成一个 OAuth 授权 URL。 async def link ( self , request : LinkRequest) -> Dict[ str , Any]:
# 生成 OAuth 授权 URL
return { "link_web_url" : authorization_url}
身份验证
用户授权后,Provider 的 callback() 方法会交换 Token 并保存凭据。 async def callback ( self , code : str , state : str ) -> Dict[ str , Any]:
# 使用 code 交换 token
# 将凭据保存到数据库
# 触发初始数据拉取
return { "provider_slug" : self .info.slug, "stage" : "completed" }
数据同步
Provider 会定期或按需从厂商 API 拉取数据,保存原始数据,进行格式化,并推送到平台。 async def _pull_and_push_for_user ( self , credentials : Dict) -> bool :
# 从厂商 API 拉取数据
# 保存原始数据
# 转换为标准格式
# 推送到平台
pass
取消绑定
当用户取消绑定时,Provider 的 unlink() 方法会撤销访问权限并删除存储的凭据。 async def unlink ( self , user_id : str ) -> Dict[ str , Any]:
# 在厂商处撤销访问权限 (可选)
# 从数据库中删除凭据
return { "success" : True }
标准化健康指标
所有 Provider 收集的数据都会转换为标准化的健康指标,以确保不同设备之间的一致性:
DAILY_STEPS: 步数
DAILY_DISTANCE: 行进距离(米)
DAILY_CALORIES_ACTIVE: 活动消耗卡路里(kcal)
DAILY_CALORIES_BASAL: 基础代谢卡路里(kcal)
DAILY_FLOORS_CLIMBED: 爬楼层数
ACTIVE_TIME: 活动时长(分钟)
HEART_RATE: 瞬时心率 (bpm)
DAILY_HEART_RATE_MIN: 每日最低心率 (bpm)
DAILY_HEART_RATE_MAX: 每日最高心率 (bpm)
DAILY_AVG_HEART_RATE: 每日平均心率 (bpm)
DAILY_HEART_RATE_RESTING: 静息心率 (bpm)
HRV: 心率变异性 RMSSD (ms)
DAILY_SLEEP_DURATION: 总睡眠时长 (ms)
SLEEP_IN_BED: 在床时间 (ms)
DAILY_AWAKE_TIME: 睡眠期间清醒时间 (ms)
DAILY_LIGHT_SLEEP: 浅睡时长 (ms)
DAILY_DEEP_SLEEP: 深睡时长 (ms)
DAILY_REM_SLEEP: REM 睡眠时长 (ms)
SLEEP_EFFICIENCY: 睡眠效率百分比
WEIGHT: 体重 (kg)
HEIGHT: 身高 (m)
BMI: 身体质量指数
BODY_FAT_PERCENTAGE: 体脂率
SKELETAL_MUSCLE_MASS: 骨骼肌重量 (kg)
RESPIRATORY_RATE: 呼吸频率 (次/分钟)
BLOOD_OXYGEN: 血氧饱和度 (SpO2) 百分比
WORKOUT_DURATION_LOW: 低强度锻炼时长 (分钟)
WORKOUT_DURATION_MEDIUM: 中强度锻炼时长 (分钟)
WORKOUT_DURATION_HIGH: 高强度锻炼时长 (分钟)
ALTITUDE_GAIN: 爬升高度 (米)
SPEED: 平均速度 (m/s)
有关标准化指标的完整列表,请参阅 Mirobody 框架文档中的 StandardIndicator 枚举。
Provider 要求
为确保质量和一致性,所有 Provider 必须实现以下内容:
class ThetaYourProvider ( BaseThetaProvider ):
@ classmethod
def create_provider ( cls , config : Dict) -> Optional[ 'ThetaYourProvider' ]:
"""用于实例化 Provider 的工厂方法"""
@ property
def info ( self ) -> ProviderInfo:
"""Provider 元数据"""
async def link ( self , request : Any) -> Dict[ str , Any]:
"""发起 OAuth 流程"""
async def callback ( self , * args , ** kwargs ) -> Dict[ str , Any]:
"""处理 OAuth 回调"""
async def unlink ( self , user_id : str ) -> Dict[ str , Any]:
"""取消用户关联"""
async def format_data ( self , raw_data : Dict) -> StandardPulseData:
"""将原始数据转换为标准格式"""
async def pull_from_vendor_api ( self , * args , ** kwargs ) -> List[Dict]:
"""从厂商 API 拉取数据"""
async def save_raw_data_to_db ( self , raw_data : Dict) -> List[Dict]:
"""将原始数据保存到数据库"""
async def is_data_already_processed ( self , raw_data : Dict) -> bool :
"""检查数据是否已处理"""
async def _pull_and_push_for_user ( self , credentials : Dict) -> bool :
"""为用户拉取并推送数据"""
每个 Provider 都需要一个数据库表: CREATE TABLE IF NOT EXISTS theta_ai . health_data_ < provider > (
id SERIAL PRIMARY KEY ,
create_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
update_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
is_del BOOLEAN DEFAULT FALSE,
msg_id VARCHAR ( 255 ) UNIQUE NOT NULL ,
raw_data JSONB NOT NULL ,
theta_user_id VARCHAR ( 255 ) NOT NULL ,
external_user_id VARCHAR ( 255 )
);
CREATE INDEX idx_health_data_ < provider > _theta_user_id
ON theta_ai . health_data_ < provider > (theta_user_id);
CREATE INDEX idx_health_data_ < provider > _msg_id
ON theta_ai . health_data_ < provider > (msg_id);
在 config.yaml 中要求的配置: # OAuth 凭据 (必需)
<PROVIDER>_CLIENT_ID : "您的_client_id"
<PROVIDER>_CLIENT_SECRET : "您的_client_secret"
<PROVIDER>_REDIRECT_URL : "您的_回调_url"
# OAuth 端点 (可选,在代码中提供默认值)
<PROVIDER>_AUTH_URL : "https://..."
<PROVIDER>_TOKEN_URL : "https://..."
<PROVIDER>_API_BASE_URL : "https://..."
# OAuth 权限范围 (可选)
<PROVIDER>_SCOPES : "scope1 scope2"
Provider 发现机制
Mirobody Health 会从配置的目录中自动发现并加载 Provider:
# 在 config.localdb.yaml 中
THETA_PROVIDER_DIRS :
- mirobody / pulse / theta # 系统内置 Provider
- mirobody / pulse / custom # 您的自定义 Provider
目录结构:
mirobody/pulse/theta/
├── mirobody_garmin_connect/
│ ├── __init__.py
│ └── provider_garmin.py
├── mirobody_whoop/
│ ├── __init__.py
│ └── provider_whoop.py
└── mirobody_your_provider/
├── __init__.py
└── provider_your_provider.py
为保持一致性,Provider 目录名称应遵循 mirobody_<provider_name> 模式。
最佳实践
外部 API 调用始终使用 try-except 代码块
记录带有上下文的错误日志(用户 ID、时间戳、错误详情)
失败时返回空结果,而不是导致程序崩溃
对瞬时错误实现指数退避 (Exponential backoff)
优雅地处理速率限制
使用前验证 OAuth Token 的有效性
在转换前检查数据类型
优雅地处理缺失值或 null 值
验证时间戳格式
验证单位是否符合预期值
所有 I/O 操作使用 async/await
为大型数据集实现分页
限制对厂商 API 的并发请求数
缓存频繁访问的配置项
使用数据库连接池
切勿记录敏感数据(Token、密码)
在数据库中加密存储凭据
所有的外部通信都使用 HTTPS
验证 OAuth state 参数
为 OAuth 2.0 实现 Token 刷新机制
下一步