Communitygithub.com

vellum-ai/vellum-assistant

通过 OAuth2 连接 Oura Ring — 应用注册、令牌交换和凭据存储

vellum-assistant 是什么?

该技能使 AI 助手能够将用户的 Oura Ring(第3代或 Ring 4)连接到 Oura Cloud API V2,以获取睡眠、心率、准备状态、活动等健康数据。它通过 OAuth2 流程完成授权,包括在 Oura Cloud 上注册开发者应用、使用 Python 脚本自动交换授权码为令牌,以及将访问令牌和刷新令牌安全地存储在凭据库中。该技能适用于任何支持 bash 执行和凭据管理的 AI 代理平台(如 Claude Code、Cursor 或 Codex)。令牌每30天过期,技能还提供了手动刷新令牌的 curl 命令。通过使用此技能,用户无需手动复制授权码,即可快速、安全地集成 Oura Ring 数据,用于健康分析、日常报告或自动化工作流。

兼容平台~Claude Code~Codex CLI~Cursor
npx skills add https://github.com/vellum-ai/vellum-assistant/tree/HEAD/skills/oura-setup

在你喜欢的 AI 中提问

打开一个已预加载此 Agent Skill 的新对话。

文档

Oura Ring 集成

连接用户的 Oura Ring,通过 Oura Cloud API V2 获取睡眠、心率、准备状态、活动及其他健康数据。

前置条件

  • 一个 Oura Ring(第3代或 Ring 4),并拥有有效的 Oura 会员资格(API 访问需要每月 $6)
  • 已安装 Oura 应用并与戒指配对
  • 在 https://cloud.ouraring.com 拥有 Oura Cloud 账户

设置

1. 注册开发者应用

让用户访问 https://developer.ouraring.com/applications 并创建一个新应用:

  • 显示名称: 用户希望用于集成的名称
  • 描述: 集成的简要描述
  • 联系邮箱: 用户的邮箱
  • 网站: 任何有效的 URL(例如个人网站)
  • 隐私政策 / 服务条款: 任何有效的 URL(必填字段,但对个人应用不作强制)
  • 重定向 URI: http://localhost:3000/callback
  • 作用域: 启用所有需要的作用域。推荐:personal, daily, heartrate, sleep, workout, spo2, stress, heart_health, session, ring_configuration

保存客户端 ID 和客户端密钥。

2. 运行 OAuth 流程

使用 assistant credentials prompt 安全地存储客户端密钥,然后在用户的机器上编写并运行 OAuth 辅助脚本:

#!/usr/bin/env python3
"""Oura Ring OAuth2 辅助程序 — 即时捕获授权码并交换为令牌。"""
from http.server import HTTPServer, BaseHTTPRequestHandler
from urllib.parse import urlparse, parse_qs, urlencode
import urllib.request, json, webbrowser, ssl

CLIENT_ID = 'YOUR_CLIENT_ID'
CLIENT_SECRET = 'YOUR_CLIENT_SECRET'
REDIRECT_URI = 'http://localhost:3000/callback'
SCOPES = 'email personal daily heartrate workout tag session spo2 ring_configuration stress heart_health'

class Handler(BaseHTTPRequestHandler):
    def do_GET(self):
        parsed = urlparse(self.path)
        if parsed.path == '/':
            params = urlencode({
                'response_type': 'code', 'client_id': CLIENT_ID,
                'redirect_uri': REDIRECT_URI, 'scope': SCOPES, 'state': 'assistant'
            })
            self.send_response(302)
            self.send_header('Location', f'https://cloud.ouraring.com/oauth/authorize?{params}')
            self.end_headers()
        elif parsed.path == '/callback':
            code = parse_qs(parsed.query).get('code', [''])[0]
            if code:
                token_data = urlencode({
                    'grant_type': 'authorization_code', 'code': code,
                    'redirect_uri': REDIRECT_URI, 'client_id': CLIENT_ID,
                    'client_secret': CLIENT_SECRET,
                }).encode()
                try:
                    req = urllib.request.Request('https://api.ouraring.com/oauth/token',
                        data=token_data,
                        headers={'Content-Type': 'application/x-www-form-urlencoded'},
                        method='POST')
                    resp = urllib.request.urlopen(req, context=ssl.create_default_context())
                    result = json.loads(resp.read())
                    with open('/tmp/oura_tokens.json', 'w') as f:
                        json.dump(result, f, indent=2)
                    self.send_response(200)
                    self.send_header('Content-Type', 'text/html')
                    self.end_headers()
                    self.wfile.write(b'<html><body><h1>已连接!您可以关闭此标签页。</h1></body></html>')
                    print(f'\n令牌已保存到 /tmp/oura_tokens.json')
                except Exception as e:
                    self.send_response(500)
                    self.end_headers()
                    self.wfile.write(f'令牌交换失败: {e}'.encode())
    def log_message(self, *a): pass

print('正在打开浏览器进行 Oura 授权...')
webbrowser.open('http://localhost:3000')
HTTPServer(('localhost', 3000), Handler).serve_forever()

在用户的机器上使用 python3 /path/to/script.py 运行(host_bash)。用户在浏览器中授权,脚本会捕获授权码并在不到一秒内交换为令牌。

重要: 授权码约在 30 秒内过期。不要让用户手动粘贴授权码——使用此脚本自动捕获并交换。

3. 存储令牌

OAuth 流程完成后,从 /tmp/oura_tokens.json 读取令牌并存储:

  • access_token — 存储在凭据库中,注入模板用于 api.ouraring.com 的 Authorization 头(Bearer 前缀)和 allowed_tools: ["bash"]
  • refresh_token — 存储在凭据库中,用于令牌刷新
  • 令牌约在 30 天后过期。设置提醒在过期前刷新。

4. 验证连接

使用个人信息端点进行测试:

curl -s -H "Authorization: Bearer $TOKEN" "https://api.ouraring.com/v2/usercollection/personal_info"

可用端点

所有端点均使用 GET https://api.ouraring.com/v2/usercollection/{type},查询参数为 start_date 和 end_date(YYYY-MM-DD 格式)。

端点数据说明
/v2/usercollection/daily_sleep睡眠评分、时长、效率、阶段最好在用户通常的醒来时间后检查
/v2/usercollection/sleep详细的睡眠时段,包括心率、HRV、运动原始睡眠时段数据
/v2/usercollection/daily_readiness准备度评分、HRV 平衡、恢复早晨检查指标
/v2/usercollection/daily_activity步数、卡路里、运动、静止时间活动摘要
/v2/usercollection/heartrate连续心率(使用 start_datetime/end_datetime ISO 格式)数据可能较大 — 限制日期范围
/v2/usercollection/daily_spo2血氧水平夜间平均值
/v2/usercollection/daily_stress压力评分和恢复日间压力跟踪
/v2/usercollection/workout检测到的锻炼,包括心率、卡路里自动检测或手动记录
/v2/usercollection/personal_info年龄、体重、身高、邮箱良好的连接测试

令牌刷新

令牌在 30 天后过期。使用以下命令刷新:

curl -s -X POST "https://api.ouraring.com/oauth/token" \
  -d "grant_type=refresh_token&refresh_token=$REFRESH_TOKEN&client_id=$CLIENT_ID&client_secret=$CLIENT_SECRET"

从响应中存储新的 access_token 和 refresh_token。

提示

  • 新戒指需要 1-2 晚进行校准,之后数据才有意义
  • 睡眠数据通常在用户醒来几小时后出现
  • 锻炼期间的心率数据在锻炼通过 Oura 应用同步后出现
  • 速率限制:每 5 分钟 5000 次请求
  • Ring 4 和第 3 代用户必须拥有有效的 Oura 会员资格才能使用 API

相关技能