作成: 2026-06-30 / 更新: 2026-07-04 / 対象: Open-Meteo・SwitchBot・Withings・Anthropic / OpenAI API
| サービス | 用途 | 状態 | 認証 |
|---|---|---|---|
| Open-Meteo | 地点、気圧、外気温、外湿度 | 実装済み | APIキー不要 |
| SwitchBot API v1.1 | 室温、室湿度の現在値(毎時Cronで蓄積) | 実装済み | Token + SecretによるHMAC-SHA256 |
| Withings Health Data API | 睡眠、体重 | 実装済み | OAuth 2.0 |
| Anthropic Messages / OpenAI Chat Completions | Worker解析結果(相関統計)をAIが日本語で解説 | 実装済み | APIキーはWorker Secret(プロバイダーを直接呼び出し) |
wrangler.jsoncのtriggers.crons = ["0 * * * *"]、worker/index.tsのscheduledハンドラ)。Cronは室内温湿度の現在値ポーリングと気象のトップアップを実行し、片方の失敗がもう片方を止めないようPromise.allSettledで分離しています。SwitchBot公開API v1.1が返すのは現在値だけで、過去履歴を取るエンドポイントが存在しません(公開されているのはデバイス一覧・現在ステータス・コマンド送信・シーン一覧・シーン実行の5種類のみ)。したがって室内温湿度を時系列として蓄積する唯一の手段が定期ポーリングです。毎時Cronが室内温湿度を1点取り、気象は取りこぼしを補充します(Withingsはアプリ起動時同期のまま)。
| 収集経路 | きっかけ | 取るもの |
|---|---|---|
| オンデマンド同期 | アプリを開いたとき | 気象・SwitchBot・Withings(睡眠/体重)を裏で同期 |
| 毎時Cron | 毎時0分(Workers Cron Trigger) | SwitchBot室内温湿度の現在値+気象トップアップ |
フロントは開いた瞬間にD1の保存済みデータを描画し、3つの外部同期(気象・SwitchBot・Withings)はバックグラウンドで実行して「…同期中」と表示します。同期完了後に自動で再取得して最新化するため、上流が遅くても最初の描画はブロックされません。
外部API(Open-Meteo / SwitchBot / Withings)への全fetchにAbortSignal.timeout(10_000)を設定。タイムアウトやネットワーク失敗は上流障害として502(UPSTREAM_ERROR)にマップします(内部エラー500ではなく)。
api.open-meteo.com(Forecast)はドイツHetznerの単一オリジン(CDNなし)で、間欠的にTCPレベルで無応答になります。同時刻にarchive-api・geocoding-apiは正常応答したことから、Forecastホスト固有の問題と特定しました。これが「アプリが時々すごく遅い」の原因で、タイムアウト+2段階ロードで画面のブロックを解消しています。frontend/worker/open-meteo.ts。時間はUTCで取得し、D1へ保存します。| 目的 | URL | 使い分け |
|---|---|---|
| 直近期間の時間別気象 | https://api.open-meteo.com/v1/forecast | 同期終了日の5日前以降 |
| それより古い時間別気象 | https://archive-api.open-meteo.com/v1/archive | 過去の欠損期間 |
| 地名検索 | https://geocoding-api.open-meteo.com/v1/search | 地名から緯度・経度・タイムゾーン候補を取得 |
時間別気象リクエストの主要パラメータは次のとおりです。日付と緯度経度は保存済み地点と同期範囲からWorkerが埋めます。
latitude={lat}
longitude={lon}
hourly=surface_pressure,temperature_2m,relative_humidity_2m
timezone=UTC
start_date=YYYY-MM-DD
end_date=YYYY-MM-DD
札幌市の座標を使った形式例です。日付指定を省略したため、リンクを開いた時点のForecast API既定期間が返ります。
POST /api/sync/weatherと毎時Cronは同期カーソル以降を保存し、直近7日より過去へは遡りません(それ以前は保存済みとみなす)。Forecast APIは最新5日ぶんだけを担当し、それより古い欠損はArchive(再解析)で埋めます。24時間気圧変化は各行のt−24hの値が要るため、保存範囲の1日前からウォームアップ取得しますが、ウォームアップ行自体は保存しません。以前はウォームアップ行をnullで書き戻して既存のpressure_change_24hを消すバグがあり、本番122行中120行がnullになっていました(修正済み)。| health-moniterの値 | 由来 | 処理 |
|---|---|---|
| 気圧 | surface_pressure(hPa) | Open-Meteoの地表面気圧。海面更正気圧pressure_mslではない |
| 外気温 | temperature_2m(℃) | 地上2mの1時間値 |
| 外湿度 | relative_humidity_2m(%) | 地上2mの1時間値 |
| 24時間気圧変化 | Worker計算 | 現在時刻の気圧 − 24時間前の気圧 |
| 日次平均気温・気圧・湿度 | Worker計算 | 地点タイムゾーンの同じ日付に属する1時間値を平均 |
worker/switchbot.ts)。公開APIに履歴取得がないため、毎時Cronのポーリングで時系列を蓄積します(第2節「収集アーキテクチャ」参照)。fetchは10秒でタイムアウトし、失敗時は502。| 目的 | メソッド・URL | 結果 |
|---|---|---|
| デバイス一覧 | GET https://api.switch-bot.com/v1.1/devices | 温湿度計のdeviceIdを特定 |
| 現在状態 | GET https://api.switch-bot.com/v1.1/devices/{deviceId}/status | 機種が返す現在の温度・湿度 |
認証ヘッダーはAuthorization、sign、t、nonceです。WorkerのWeb Cryptoでtoken + timestamp + nonceをSecretによるHMAC-SHA256で署名します。TokenとSecretはGitやD1へ置かず、Cloudflareのsecretとして管理します。
worker/withings.ts)。同期はカーソル方式で毎回7日オーバーラップ、初回のみ過去365日を取得します。| 段階 | URL | 用途 |
|---|---|---|
| 本人認可 | https://account.withings.com/oauth2_user/authorize2 | 認可コードを取得。stateを検証 |
| トークン取得・更新 | POST https://wbsapi.withings.net/v2/oauth2 | action=requesttokenでaccess/refresh tokenを取得・更新 |
| 睡眠概要 | POST https://wbsapi.withings.net/v2/sleep | action=getsummary。睡眠時間と取得可能な睡眠段階 |
| 体重 | POST https://wbsapi.withings.net/measure | action=getmeas。体重を優先 |
予定スコープは睡眠用user.activityと体重用user.metricsです。access tokenは短命で、refresh時には新しいrefresh tokenも返るため、成功時にD1の単一行を必ず更新します。client secretはCloudflare secretに置きます。
POST /api/ai/explainで、Workerが計算した相関統計を外部AIに日本語で解説させます。プロバイダー(Claude / OpenAI)とモデルは実行のたびに本人が選び、選択していないプロバイダーへは送りません。自動フォールバックもありません。api.cloudflare.com/.../ai/v1/*やBYOK・cf-aig-collect-log-payloadの記述は無効です。sleep_end基準、20時間超は同期漏れノイズとして除外)。{ analyzed_days, correlations:[{symptom_label, metric_label, n, pearson_r, p_value, spearman_r}] }という相関統計だけを組み立てる。| 選択肢 | 実際に呼ぶURL | API形式・既定モデル |
|---|---|---|
| Claude | POST https://api.anthropic.com/v1/messages | Anthropic Messages(anthropic-version: 2023-06-01)/既定 claude-sonnet-5 |
| OpenAI | POST https://api.openai.com/v1/chat/completions | OpenAI Chat Completions(max_completion_tokens)/既定 gpt-5.4 |
モデルはshared/ai-models.tsの許可リストで検証し、リスト外のモデルは400で拒否します(ブラウザが任意のモデルを呼ばせられないように)。疎通確認用のPOST /api/ai/testは小さな"ping"を送って鍵が生きているかだけを返します。
ANTHROPIC_API_KEY / OPENAI_API_KEY)にのみ保持し、ブラウザには一切露出しません。設定画面には「設定済み/未設定」と接続テスト結果だけを表示します。| 送る | 送らない |
|---|---|
| 解析対象日数、相関ごとのn(有効ペア数) | 時間別の気象・室内環境の生データ |
| 症状ラベル・指標ラベルと相関係数(Pearson/Spearman)、p値 | 個別の痛み記録、記録時刻、メモ |
| Workerが決定論的に算出した相関統計のみ | 緯度・経度、地点名、OAuthトークン、APIキー |
AIの役割は数値計算ではなく、傾向・不確実性・軽い生活上の提案を説明することです。診断・薬の変更・治療指示は行わせず、画面にも医療診断ではない旨を固定表示します。
| 段階 | 内容 | 状態 | 運用で再確認すること |
|---|---|---|---|
| B | SwitchBot室温・室湿度(毎時Cron蓄積) | 実装済み | 所有機種のstatusレスポンス項目、Cloud Service有効化 |
| C | Withings睡眠・体重(OAuth・7日オーバーラップ) | 実装済み | 登録アプリのredirect URI、scope、token更新 |
| 統合 | 全データ源の日次解析・CSV | 実装済み | 欠損と複数回記録の集約規則 |
| D | AI解説(相関統計のみ送信・モデル選択・接続テスト) | 実装済み | 既定モデル名(claude-sonnet-5 / gpt-5.4)、許可リスト、Worker Secret |
外部APIは変更され得る。ここに記載したURL・認証・モデル仕様は2026-07-04時点のhealth-moniterソース(frontend/worker/)とdocs/design.mdに基づく。実装変更時はこのノートも更新する。