Oura Ring 整合
連接使用者的 Oura Ring,透過 Oura Cloud API V2 提取睡眠、心率、準備度、活動及其他健康資料。
先決條件
- Oura Ring(Gen 3 或 Ring 4)且擁有有效的 Oura 會員資格(API 存取需每月 $6 美元)
- 已安裝 Oura 應用程式並與戒指配對
- 在 https://cloud.ouraring.com 擁有 Oura Cloud 帳戶
設定
1. 註冊開發者應用程式
請使用者前往 https://developer.ouraring.com/applications 並建立一個新的應用程式:
- 顯示名稱: 使用者偏好的整合名稱
- 描述: 整合的簡短描述
- 聯絡 Email: 使用者的電子郵件
- 網站: 任何有效的 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 輔助工具 — 立即捕捉授權碼並交換 Token。"""
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'\nToken 已儲存至 /tmp/oura_tokens.json')
except Exception as e:
self.send_response(500)
self.end_headers()
self.wfile.write(f'Token 交換失敗:{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)。使用者在瀏覽器中授權後,腳本會立即捕捉授權碼並交換成 Token,過程不到一秒。
重要: 授權碼約在 30 秒內過期。請勿讓使用者手動貼上授權碼 — 請使用此腳本自動捕捉並交換。
3. 儲存 Token
OAuth 流程完成後,從 /tmp/oura_tokens.json 讀取 Token 並儲存:
access_token— 儲存至憑證庫,使用注入模板為api.ouraring.com的 Authorization 標頭(Bearer 前綴)以及allowed_tools: ["bash"]refresh_token— 儲存至憑證庫,用於 Token 重新整理- 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 | 年齡、體重、身高、電子郵件 | 良好的連線測試端點 |
Token 重新整理
Token 會在 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 和 Gen 3 使用者必須擁有有效的 Oura 會員資格才能使用 API